npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

bonbons

v1.0.13-beta

Published

a static typed MVC framework with express and typescript

Downloads

4

Readme

PROJECT Bonbons

a static-typed express framework with typescript.

This is an MVC framework based on node.js and express. The framework is created by typescript, which includes a dependency injection system and RESTful high-level abstraction.

The core features are under development. Currently, the dependency injection system is basically completed, the controller module is basically completed, and the GET method is also completed. Other features are still developing.

Installation

npm install bonbons --save

How it works?

1. Create a service and register

@Injectabe()
export class SecService {
    constructor() {}
    print(){
        return "hello world!";
    }
}

...

// register a scoped-service
Bonbons.Create().scoped(SecService);
// or register a singleton-service
Bonbons.Create().singleton(SecService);

2. Create a controller and register

@Controller("api")
export class MainController extends BaseController {

    constructor(private sup: SuperService) {
        super();
    }

    @Method("GET")
    @Route("/index")
    public GetIndex(): string {
        return "this is a get method with base : ";
    }

    @Method("GET", "POST")
    @Route("index")
    public ApiIndex(): JsonResult {
        console.log("this is a api method with query id : " + this.context.query("id", Number));
        console.log("this is a api method with query select : " + this.context.query("select", Boolean));
        console.log("this is a api method with query notexist : " + this.context.query("notexist"));
        return new JsonResult({ value: "this is a api method with base : " });
    }

    @Method("GET")
    @Route("page", ["id", "select", "message"]) 
    // provide query params name list to open static query feature
    // example : localhost/api/page?id=123456&select=true&message=mmmmmm
    public AnotherGET(id:number, select:boolean, message): JsonResult {
        console.log(id); // 123456
        console.log(select); // true
        console.log(message) // "mmmmmm" (string is the default type)
        console.log(typeof id); // number
        console.log(typeof select); // boolean
        console.log(typeof message); // string
        return new JsonResult({ value: "666666" });
    }

}

...

// register controller
Bonbons.Create()
    .scoped(SecService)
    .controller(MainController);

3. Use ABC to split dependency

// define a ABC
export abstract class ABCService {
    public abstract getMessage(): string;
}

// then extends ABC and implements methods
@Injectabe()
export class MainService extends ABCService {

    private id = UUID.Create();

    constructor(sec: SecService) {
        super();
    }

    public getMessage(): string {
        return this.id;
    }

}

...

// finally inject it
Bonbons.Create()
    .controller(MainController)
    .scoped(SecService)
    .scoped(ABCService, MainService)

// now you can split the dependency by ABC injection
export class SuperService {

    constructor(private sec: SecService, private main: ABCService) {

    }

    print() {
        return "Hello World! " + this.main.getMessage();
    }

}

// it works!

4. Add middlewares for route or controller

// first create middleware in pure function format.
function middleware01(r, rs, next) {
    console.log("123456");
    next();
}
function middleware02(r, rs, next) {
    console.log("555555");
    next();
}

// then add it to method by decorator
@Method("GET", "POST")
@Route("index")
@Middleware([middleware02])
public ApiIndex(): JsonResult {
    return new JsonResult({ value: this.sup.print() });
}

// this decorator is alse can be add in controller
// the middlewares add to controller will add to all the registeres methods, but you can still rewrite this behavior.
@Controller("api")
@Middleware([middleware01])
export class MainController extends BaseController {

    constructor(private sup: SuperService) {
        super();
    }

    @Method("GET")
    @Route("/index")
    // will extends controller middlewares list : [middleware01]
    public GetIndex(): string {
        return this.sup.print();
    }

    @Method("GET", "POST")
    @Route("index")
    @Middleware([middleware02], false) 
    // merge:true(default), will extends controller middlewares list : [middleware01, middleware02]
    // merge:false, will not extends controller middlewares list : [middleware02]
    public ApiIndex(): JsonResult {
        return new JsonResult({ value: this.sup.print() });
    }

}

5. Form control

// there are two ways to access the form data
    @Method("POST")
    @Route("post")
    @Middleware([], false)
    public POSTIndex(name:string, @FromBody() params: any): JsonResult {
        console.log("this is a post method");
        const form = this.context.form;
        console.log(form.data);
        console.log(params);
        return new JsonResult(params);
    }

    // then post message in application/json format : 
    // {"name":"bws", age:123}

    // console output:
    // {"name":"bws", age:123}
    // {"name":"bws", age:123}

    /*
    * All supported decorators :
    * @FromBody()   -   default : application/json
    * @FormData()   -   default : multiple/form-data
    * @FromForm()   -   default : application/x-www-form-urlencoded
    * @RawBody()   -   default : application/octet-stream
    * @TextBody()   -   default : text/plain
    */

   // static-typed form params will be introduced later.

6.Multiple injections with POST/PUT

// create a static-type model to describe form structure:
import { Serialize, Deserialize } from "bonbons";

// model to describe the form data structure
export class PostModel {

    // data contract
    @Serialize("received_ame")
    @Deserialize("NAME_TEST")
    private _name: string;
    public get Name() { return this._name; }

    // data contract and type convert
    @Serialize(Number, "receivedMax")
    @Deserialize(Number, "MAX_TEST")
    private _max: number;
    public get MAX() { return this._max; }

}

// then try to create a post method:
    @Method("POST")
    @Route("post/:id/details/:name", ["query", "find"])
    @Middleware([], false)
    public POSTIndex( // you can access params, queryParams and form object by function-params-injection.
        id: number,
        name: string,
        query: string,
        find: string,
        @FromBody() params: PostModel): JsonResult {

        console.log("this is a post method");
        console.log(`${id} - ${name} - ${query} - ${find}`); 
        console.log(`${typeof id} - ${typeof name} - ${typeof query} - ${typeof find}`);
        console.log(params);
        console.log(Object.getPrototypeOf(params).constructor.name);
         return this.toJSON({
            theParams: params,
            theName: name,
            theQuery: query,
            theId: id,
            theFind: find
        }, { resolver: JsonResultResolvers.decamalize }); 
        // use resolver to change object keys, this will work an the end of JSON.stringfy.
    }

/**
* then post to "localhost:3000/api/post/123456/details/miao18game?query=sss&find=mmm" with body : 
* {
*   "NAME_TEST":"miao17game",
*   "MAX_TEST":123
* }
*  
* output: 
* "this is a post method"
* "123456 - miao18game - sss - mmm"
* "numner - string - string - string"
* "PostModel { _name: 'miao17game', _max: 123 }"
* "PostModel"
*
* response : 
*   {
*       "the_params": {
*           "received_name": "miao17game", 
*           // key is resolved by @Deserialize/@Serialize
*           "received_max": 123  
*           // key is resolved by @Deserialize/@Serialize and JsonResultResolvers.decamalize
*       },
*       "the_name": "miao18game",
*       "the_query": "sss",
*       "the_id": 123456,
*       "the_find": "mmm"
*   }
*
* JsonResult will automatically transform static-type object to correct object-json with data contract, 
* or you can transfoem manually with [TypedSerializer.ToJSON(obj)], 
* if you create contract with decorators : @Serialize/@Deserialize.
* 
* Of cource, you can define your own static-typed serialization behavior, 
* only create your serializer implements the IStaticTypedResolver
*/

7. Async method

    @Method("GET")
    @Route("/index")
    public async GetIndex(): Async<string> {
        console.log("this is a get method with base : ");
        // async mock
        await this.sleep(20);
        console.log("step 01");
        await this.sleep(20);
        console.log("step 02");
        await this.sleep(20);
        console.log("step 03");
        await this.sleep(20);
        console.log("step 04");
        await this.sleep(20);
        console.log("step 05");
        return this.sup.print();
    }

    // it works!
    // Async<T> = Promise<T>, only an alias.

8. String encoding support

    @Method("GET")
    @Route("/index")
    public async GetIndex(): StringResult {
        console.log("this is a get method with base : ");
        return this.toStringfy("woshinidie : 鎴戞槸浣犵埞", { encoding: "GBK" });
        // defaults:
        // encoding: "utf8"
        // decoding: "utf8"
    }

    // response:
    // "woshinidie : 我是你爹"

Still in developing...