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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bonbons/core

v1.3.13

Published

@bonbons/core

Downloads

337

Readme

PROJECT Bonbons.koa

一个以koa2为底,用typescript设计的IoC框架.

其实没什么太多可以的描述,Bonbons纯粹依照静态语言风格来设计,包括不仅限于静态类型依赖注入能力,线性请求管道中间件,高可配置容器,静态路由与表单参数和传统的RESTful风格路由支持.

核心功能仍在开发中,而且目前看来进度缓慢- -。

Build Status package version

Bonbons.express

More about Bonbons(express version)

安装

$ npm install @bonbons/core --save

Bonbons如何工作?

1. 创建app

// 1. 直接使用构造
new Bonbons()
    .option(DEPLOY_MODE, { port: 5678 });
    .start();
// 2. 使用静态属性
Bonbons.New
    .option(DEPLOY_MODE, { port: 5678 });
    .start();
// 3. 使用静态方法
Bonbons.Create()
    .option(DEPLOY_MODE, { port: 5678 });
    .start();
// 4. 定义构造
@BonbonsApp({
    options: [
        { token: DEPLOY_MODE, value: { port: 5678 } }
    ]
})
class MyApp extends BaseApp { 
    start(): void {
        const { port } = this.config.get(DEPLOY_MODE);
        this.logger.debug(`app is running on port ${port || 3000}`);
    }
 }

 new MyApp().start();

2. 创建一个service

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

// 1. 直接注册
new Bonbons()
    .scoped(SecService)
//    .scoped(SecService, new SecService())
//    .scoped(SecService, () => new SecService())
//    .singleton(SecService)
    .option(DEPLOY_MODE, { port: 5678 });
    .start();
// 或者进行定义
@BonbonsApp({
//    singleton: [ SecService ],
    scoped: [ SecService ]
    options: [
        { token: DEPLOY_MODE, value: { port: 5678 } }
    ]
})
// ...

// 2. 接口-实现注册
// 定义抽象类
export abstract class ABCService {
    abstract getMessage(): string;
}

// 使用接口实现来继承抽象类,不需要生产真实继承链
@Injectabe()
export class MainService implements ABCService {

    // create an unique token
    private id = UUID.Create();

    constructor() { }

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

}

// 然后进行注册
new Bonbons()
    .scoped(SecService)
    .scoped(ABCService, MainService)
//    .scoped(ABCService, new MainService())
//    .scoped(ABCService, () => new MainService())
//    .singleton(ABCService, MainService)
    .option(DEPLOY_MODE, { port: 5678 });
    .start();
// or
@BonbonsApp({
    scoped: [ 
        SecService,
//        [ ABCService, MainService ],
        { token: ABCService, implement: MainService },
//        { token: ABCService, implement: new MainService() },
//        { token: ABCService, implement: () => new MainService() }
    ],
    options: [
        { token: DEPLOY_MODE, value: { port: 5678 } }
    ]
})
// ...

// 目前支持在控制器、服务、和管道中,使用构造函数注入或者依赖查找来获取实例.

3. 创建控制器controller

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

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

    @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 : " });
        return this.toJSON({ value: "this is a api method with base : " });
    }

    @Method("GET")
    @Route("/page?{id}&{select}&{message}") 
    // 提供路由参数占位符列表来开启静态路由参数功能,确保函数参数数量和顺序与之保持一致
    // 举例 : 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" });
    }

}

...

// 注册controller
Bonbons.Create()
    .scoped(SecService)
    .controller(MainController);

4. 使用koa中间件或者线性管道 (可能存在变更,koa中间件可能在未来删除直接支持)

// 1 : koa风格中间件
// 纯函数构建模式.
const middleware01 = () => {
    return async (ctx, next) => {
        console.log("123456");
        await next();
    };
}
const middleware02 = (param) => {
    return async (ctx, next) => {
        console.log(param);
        await next();
    };
}

// 使用装饰器定制
@Method("GET", "POST")
@Route("/index")
@Middlewares([middleware02(55555)])
public ApiIndex(): JsonResult {
    return new JsonResult({ value: this.sup.print() });
}

// 可以在控制器上进行router级别定义
// 所有路由方法将默认继承,当然你也可以在路由上重写它们
@Controller("api")
@Middlewares([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("/index2")
    @Middleware([middleware02(33333)], true) 
    // merge:false(默认), 覆盖router的中间件定义 : [middleware02(33333)]
    // merge:true, 继承router的中间件定义 : [middleware01(), middleware02(555)]
    public ApiIndex(): JsonResult {
        return new JsonResult({ value: this.sup.print() });
    }

}

// 2. Bonbons线性管道
interface PipeDate {
  value: number;
  name: string;
}

@Pipe()
class PIpeClass2 extends PipeMiddleware<PipeDate> implements PipeOnInit {

  constructor(private logger: GlobalLogger) {
    super();
  }

  pipeOnInit(): void {
    // console.log(this.params);
  }

  async process(): void | Promise<void> {
    console.log(this.vName);
    console.log(this.params);
    this.logger.debug("process in pipe [ WrappedPipe ]");
  }

}

export const WrappedPipe = PipeFactory.generic(PIpeClass2);

// 在需要的地方定义
@Method("GET")
@Route("/index")
@Pipes([WrappedPipe({name: "aaa", value: 123456})])
public async GetIndex(): StringResult {
    console.log("this is a get method with base : ");
    return this.toStringfy("woshinidie : 鎴戞槸浣犵埞", { encoding: "GBK" });
}

pipe追求的是纯粹的流水线式风格。pipe在请求过程中总是保持同步顺序调用,依赖依赖注入能力可以在各个管道之间共享状态

5. 表单控制

// 有两种定义表单类型的方式
    @Method("POST")
    @Route("/post")
    @Middleware([], true)
    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
    */

   // 下面介绍的静态类型表单会自动完成类型映射,功能强大但同时意味着更高的性能开销。

6.强大的静态类型表单实现POST/PUT

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

// 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}")
    @Middlewares([], 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. 异步路由

    @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>|T, not only an alias.

8. 字符串编码

    @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 : 我是你爹"

数据库驱动、日志、模块隔离等其余功能还在开发中...