From 77b3d80a4cbd6dff6d36c84dac8360a8dfcec812 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Mon, 3 Feb 2025 22:56:22 +0800 Subject: [PATCH 1/4] fix: use @eggjs/multipart and @eggjs/view --- index-old.d.ts | 1 - package.json | 2 +- site/docs/basics/controller.md | 4 ++-- site/docs/basics/controller.zh-CN.md | 6 ++++-- site/docs/basics/plugin.md | 2 +- site/docs/basics/plugin.zh-CN.md | 2 +- site/docs/intro/migration.md | 2 +- site/docs/intro/migration.zh-CN.md | 2 +- src/app/extend/request.ts | 2 +- src/config/plugin.ts | 2 +- src/lib/types.ts | 15 +++++++++++++++ test/index.test-d.ts | 7 +++++++ 12 files changed, 35 insertions(+), 12 deletions(-) diff --git a/index-old.d.ts b/index-old.d.ts index cfe28f351c..4e48b26f4f 100644 --- a/index-old.d.ts +++ b/index-old.d.ts @@ -32,7 +32,6 @@ // BaseContextClass as CoreBaseContextClass, // } from 'egg-core'; // import EggCookies = require('egg-cookies'); -// import 'egg-multipart'; // import 'egg-view'; // declare module 'egg' { diff --git a/package.json b/package.json index a65f0f1c3d..526993ed05 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@eggjs/i18n": "^3.0.1", "@eggjs/jsonp": "^3.0.0", "@eggjs/logrotator": "^4.0.0", + "@eggjs/multipart": "^4.0.0", "@eggjs/onerror": "^3.0.0", "@eggjs/schedule": "^5.0.2", "@eggjs/security": "^4.0.0", @@ -37,7 +38,6 @@ "cluster-client": "^3.7.0", "egg-errors": "^2.3.2", "egg-logger": "^3.6.1", - "egg-multipart": "^3.5.0", "egg-view": "^2.1.4", "extend2": "^4.0.0", "graceful": "^2.0.0", diff --git a/site/docs/basics/controller.md b/site/docs/basics/controller.md index ab57aefe71..b6d07c95d0 100644 --- a/site/docs/basics/controller.md +++ b/site/docs/basics/controller.md @@ -315,7 +315,7 @@ If user request exceeds the maximum length for parsing that we configured, the f ### Acquiring the Submitted Files -The `body` in the request can carry parameters as well as files. Generally speaking, our browsers always send files in `multipart/form-data`, and we now have two kinds of ways supporting submitting and acquiring files with the help of the framework's plugin [Multipart](https://github.com/eggjs/egg-multipart). +The `body` in the request can carry parameters as well as files. Generally speaking, our browsers always send files in `multipart/form-data`, and we now have two kinds of ways supporting submitting and acquiring files with the help of the framework's plugin [Multipart](https://github.com/eggjs/multipart). - #### `File` Mode: If you have no ideas about Nodejs's Stream at all, the `File` mode suits you well: @@ -589,7 +589,7 @@ module.exports = { **Notice:`fileExtensions` will be IGNORED when `whitelist` is overwritten.** -For more tech details about this, please refer [Egg-Multipart](https://github.com/eggjs/egg-multipart). +For more tech details about this, please refer [@eggjs/multipart](https://github.com/eggjs/multipart). ### `header` diff --git a/site/docs/basics/controller.zh-CN.md b/site/docs/basics/controller.zh-CN.md index d92008a7af..173263806a 100644 --- a/site/docs/basics/controller.zh-CN.md +++ b/site/docs/basics/controller.zh-CN.md @@ -311,9 +311,10 @@ module.exports = { **注意:调整 bodyParser 支持的 body 长度时,如果应用之前有一层反向代理(如 Nginx),同样需要调整配置确保支持相等长度的请求 body。** **常见错误:将 `ctx.request.body` 与 `ctx.body` 混淆,后者实际上是 `ctx.response.body` 的简写。** + ### 获取上传的文件 -请求体除了可以带参数之外,还可以发送文件。通常情况下,浏览器会通过 `Multipart/form-data` 格式发送文件。通过内置的 [Multipart](https://github.com/eggjs/egg-multipart) 插件,框架支持获取用户上传的文件。我们为你提供了两种方式: +请求体除了可以带参数之外,还可以发送文件。通常情况下,浏览器会通过 `Multipart/form-data` 格式发送文件。通过内置的 [Multipart](https://github.com/eggjs/multipart) 插件,框架支持获取用户上传的文件。我们为你提供了两种方式: #### File 模式 @@ -579,7 +580,8 @@ module.exports = { **注意:当重写了 whitelist 时,fileExtensions 不生效。** -欲了解更多有关的技术细节和信息,请参阅 [Egg-Multipart](https://github.com/eggjs/egg-multipart)。 +欲了解更多有关的技术细节和信息,请参阅 [@eggjs/multipart](https://github.com/eggjs/multipart)。 + ### Header 除了从 URL 和请求 body 上获取参数之外,还有许多参数是通过请求 header 传递的。框架提供了一些辅助属性和方法来获取: diff --git a/site/docs/basics/plugin.md b/site/docs/basics/plugin.md index 0bc414d0ea..c53adc7c2e 100644 --- a/site/docs/basics/plugin.md +++ b/site/docs/basics/plugin.md @@ -164,7 +164,7 @@ Specific consolidation rules can be found in [Configuration](./config.md).   - [session](https://github.com/eggjs/session) Session implementation   - [i18n](https://github.com/eggjs/i18n) Multilingual   - [watcher](https://github.com/eggjs/watcher) File and folder monitoring -   - [multipart](https://github.com/eggjs/egg-multipart) File Streaming Upload +   - [multipart](https://github.com/eggjs/multipart) File Streaming Upload   - [security](https://github.com/eggjs/security) Security   - [development](https://github.com/eggjs/development) Development Environment Configuration   - [logrotator](https://github.com/eggjs/logrotator) Log segmentation diff --git a/site/docs/basics/plugin.zh-CN.md b/site/docs/basics/plugin.zh-CN.md index f91922d10c..ca8252ad9c 100644 --- a/site/docs/basics/plugin.zh-CN.md +++ b/site/docs/basics/plugin.zh-CN.md @@ -164,7 +164,7 @@ exports.mysql = { - [session](https://github.com/eggjs/session) Session 实现 - [i18n](https://github.com/eggjs/i18n) 多语言 - [watcher](https://github.com/eggjs/watcher) 文件和文件夹监控 - - [multipart](https://github.com/eggjs/egg-multipart) 文件流式上传 + - [multipart](https://github.com/eggjs/multipart) 文件流式上传 - [security](https://github.com/eggjs/security) 安全 - [development](https://github.com/eggjs/development) 开发环境配置 - [logrotator](https://github.com/eggjs/logrotator) 日志切分 diff --git a/site/docs/intro/migration.md b/site/docs/intro/migration.md index 214a66d30f..e490d9a68d 100644 --- a/site/docs/intro/migration.md +++ b/site/docs/intro/migration.md @@ -60,7 +60,7 @@ while ((part = await parts()) != null) { } ``` -- [egg-multipart#upload-multiple-files](https://github.com/eggjs/egg-multipart#upload-multiple-files) +- [egg-multipart#upload-multiple-files](https://github.com/eggjs/multipart#upload-multiple-files) ### egg-userrole diff --git a/site/docs/intro/migration.zh-CN.md b/site/docs/intro/migration.zh-CN.md index 6003c015c2..d8b4cf84a2 100644 --- a/site/docs/intro/migration.zh-CN.md +++ b/site/docs/intro/migration.zh-CN.md @@ -61,7 +61,7 @@ while ((part = await parts()) != null) { } ``` -- [egg-multipart#upload-multiple-files](https://github.com/eggjs/egg-multipart#upload-multiple-files) +- [egg-multipart#upload-multiple-files](https://github.com/eggjs/multipart#upload-multiple-files) ### egg-userrole diff --git a/src/app/extend/request.ts b/src/app/extend/request.ts index e4819768ef..9ac515278d 100644 --- a/src/app/extend/request.ts +++ b/src/app/extend/request.ts @@ -17,7 +17,7 @@ export default class Request extends EggCoreRequest { declare response: Response; /** - * Request body, parsed from koa-bodyparser or egg-multipart + * Request body, parsed from koa-bodyparser or @eggjs/multipart */ declare body: any; diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 094bca10b6..0d583245e0 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -50,7 +50,7 @@ export default { */ multipart: { enable: true, - package: 'egg-multipart', + package: '@eggjs/multipart', }, /** diff --git a/src/lib/types.ts b/src/lib/types.ts index d560d1cd95..6c03ab88b3 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -27,6 +27,7 @@ import '@eggjs/schedule'; import '@eggjs/session'; import '@eggjs/onerror'; import '@eggjs/logrotator'; +import '@eggjs/multipart'; export type { EggAppInfo, @@ -81,6 +82,20 @@ export interface HttpClientConfig { useHttpClientNext?: boolean; } +/** + * Powerful Partial, Support adding ? modifier to a mapped property in deep level + * @example + * import { PowerPartial, EggAppConfig } from 'egg'; + * + * // { view: { defaultEngines: string } } => { view?: { defaultEngines?: string } } + * type EggConfig = PowerPartial + */ +export type PowerPartial = { + [U in keyof T]?: T[U] extends object + ? PowerPartial + : T[U] +}; + export interface EggAppConfig extends EggCoreAppConfig { workerStartTimeout: number; baseDir: string; diff --git a/test/index.test-d.ts b/test/index.test-d.ts index fdc5f48b83..9b0f4004c9 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -85,6 +85,13 @@ expectType(app.config.logrotator.gzip); expectType(app.config.logrotator.hourDelimiter); expectType(app.config.logrotator.filesRotateBySize); +// multipart plugin types +expectType(app.config.multipart.cleanSchedule.disable); +expectType(app.config.multipart.cleanSchedule.cron); +expectType(app.config.multipart.defaultCharset); +expectType<'file' | 'stream'>(app.config.multipart.mode); + + class AppBoot implements ILifecycleBoot { private readonly app: Application; From 87389558663385216363168422cb305bf05a3cb4 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 4 Feb 2025 00:44:11 +0800 Subject: [PATCH 2/4] f --- index-old.d.ts | 15 --------------- package.json | 2 +- site/docs/advanced/view-plugin.zh-CN.md | 1 + site/docs/core/view.md | 2 +- site/docs/tutorials/index.md | 2 +- site/docs/tutorials/index.zh-CN.md | 2 +- site/docs/tutorials/typescript.zh-CN.md | 3 ++- src/config/plugin.ts | 2 +- src/lib/types.ts | 1 + test/index.test-d.ts | 9 +++++++++ 10 files changed, 18 insertions(+), 21 deletions(-) diff --git a/index-old.d.ts b/index-old.d.ts index 4e48b26f4f..4ca765a383 100644 --- a/index-old.d.ts +++ b/index-old.d.ts @@ -32,7 +32,6 @@ // BaseContextClass as CoreBaseContextClass, // } from 'egg-core'; // import EggCookies = require('egg-cookies'); -// import 'egg-view'; // declare module 'egg' { // export type EggLogger = Logger; @@ -1042,20 +1041,6 @@ // export function start(options?: StartOptions): Promise; -// /** -// * Powerful Partial, Support adding ? modifier to a mapped property in deep level -// * @example -// * import { PowerPartial, EggAppConfig } from 'egg'; -// * -// * // { view: { defaultEngines: string } } => { view?: { defaultEngines?: string } } -// * type EggConfig = PowerPartial -// */ -// export type PowerPartial = { -// [U in keyof T]?: T[U] extends object -// ? PowerPartial -// : T[U] -// }; - // // send data can be number|string|boolean|object but not Set|Map // export interface Messenger extends EventEmitter { // /** diff --git a/package.json b/package.json index 526993ed05..1855962733 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,12 @@ "@eggjs/session": "^4.0.1", "@eggjs/static": "^3.0.0", "@eggjs/utils": "^4.2.5", + "@eggjs/view": "^3.0.1", "@eggjs/watcher": "^4.0.4", "circular-json-for-egg": "^1.0.0", "cluster-client": "^3.7.0", "egg-errors": "^2.3.2", "egg-logger": "^3.6.1", - "egg-view": "^2.1.4", "extend2": "^4.0.0", "graceful": "^2.0.0", "humanize-ms": "^2.0.0", diff --git a/site/docs/advanced/view-plugin.zh-CN.md b/site/docs/advanced/view-plugin.zh-CN.md index 75ec524668..b29bfad552 100644 --- a/site/docs/advanced/view-plugin.zh-CN.md +++ b/site/docs/advanced/view-plugin.zh-CN.md @@ -8,6 +8,7 @@ order: 5 本文将阐述框架对 View 插件的规范约束。我们可以依此来封装对应的模板引擎插件。以下以 [egg-view-ejs](https://github.com/eggjs/egg-view-ejs) 为例。 ## 插件目录结构 + ```bash egg-view-ejs ├── config diff --git a/site/docs/core/view.md b/site/docs/core/view.md index 174ca42a36..5051286d54 100644 --- a/site/docs/core/view.md +++ b/site/docs/core/view.md @@ -222,4 +222,4 @@ The built-in plugin [@eggjs/security] provides common security helper functions, [@eggjs/security]: https://github.com/eggjs/security [egg-view-nunjucks]: https://github.com/eggjs/egg-view-nunjucks -[egg-view]: https://github.com/eggjs/egg-view +[egg-view]: https://github.com/eggjs/view diff --git a/site/docs/tutorials/index.md b/site/docs/tutorials/index.md index a10794d048..18b980b58e 100644 --- a/site/docs/tutorials/index.md +++ b/site/docs/tutorials/index.md @@ -51,7 +51,7 @@ Official maintained ORM model is [egg-orm] base on [Leoric], and the following d [egg-sequelize]: https://github.com/eggjs/egg-sequelize [egg-mongoose]: https://github.com/eggjs/egg-mongoose [egg-mysql]: https://github.com/eggjs/egg-mysql -[egg-view]: https://github.com/eggjs/egg-view +[egg-view]: https://github.com/eggjs/view [egg-view-nunjucks]: https://github.com/eggjs/egg-view-nunjucks [egg-view-ejs]: https://github.com/eggjs/egg-view-ejs [egg-view-handlebars]: https://github.com/eggjs/egg-view-handlebars diff --git a/site/docs/tutorials/index.zh-CN.md b/site/docs/tutorials/index.zh-CN.md index b2bad980fe..9023c8fffa 100644 --- a/site/docs/tutorials/index.zh-CN.md +++ b/site/docs/tutorials/index.zh-CN.md @@ -53,7 +53,7 @@ $ npm init egg --type=simple [egg-sequelize]: https://github.com/eggjs/egg-sequelize [egg-mongoose]: https://github.com/eggjs/egg-mongoose [egg-mysql]: https://github.com/eggjs/egg-mysql -[egg-view]: https://github.com/eggjs/egg-view +[egg-view]: https://github.com/eggjs/view [egg-view-nunjucks]: https://github.com/eggjs/egg-view-nunjucks [egg-view-ejs]: https://github.com/eggjs/egg-view-ejs [egg-view-handlebars]: https://github.com/eggjs/egg-view-handlebars diff --git a/site/docs/tutorials/typescript.zh-CN.md b/site/docs/tutorials/typescript.zh-CN.md index 27d39dae1f..bf80dd2179 100644 --- a/site/docs/tutorials/typescript.zh-CN.md +++ b/site/docs/tutorials/typescript.zh-CN.md @@ -706,8 +706,9 @@ describe('typescript', () => { 以下几个项目可作为单元测试参考: - [https://github.com/eggjs/egg](https://github.com/eggjs/egg) -- [https://github.com/eggjs/egg-view](https://github.com/eggjs/egg-view) +- [https://github.com/eggjs/view](https://github.com/eggjs/view) - [https://github.com/eggjs/egg-logger](https://github.com/eggjs/egg-logger) + ### 编译速度慢? 根据我们的实践,`ts-node` 是目前相对较优的解决方案,既不用另起终端执行 `tsc`,也能获得还能接受的启动速度(仅限于 `ts-node@7`,新的版本由于把文件缓存去掉了,导致特别慢([#754](https://github.com/TypeStrong/ts-node/issues/754)),因此未升级)。 diff --git a/src/config/plugin.ts b/src/config/plugin.ts index 0d583245e0..305575dec4 100644 --- a/src/config/plugin.ts +++ b/src/config/plugin.ts @@ -127,6 +127,6 @@ export default { */ view: { enable: true, - package: 'egg-view', + package: '@eggjs/view', }, }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 6c03ab88b3..82c925c04b 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -28,6 +28,7 @@ import '@eggjs/session'; import '@eggjs/onerror'; import '@eggjs/logrotator'; import '@eggjs/multipart'; +import '@eggjs/view'; export type { EggAppInfo, diff --git a/test/index.test-d.ts b/test/index.test-d.ts index 9b0f4004c9..9f5ffee717 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -91,6 +91,15 @@ expectType(app.config.multipart.cleanSchedule.cron); expectType(app.config.multipart.defaultCharset); expectType<'file' | 'stream'>(app.config.multipart.mode); +// view plugin types +expectType(app.config.view.defaultViewEngine); +expectType(app.config.view.root); +expectType(app.config.view.mapping.html); +expectType(app.config.view.defaultExtension); +expectType(await ctx.renderString('hello')); +expectType(await ctx.view.renderString('hello')); +const ViewEngine = app.view.get('html')!; +expectType(await new ViewEngine(ctx).render('hello')); class AppBoot implements ILifecycleBoot { private readonly app: Application; From ad439463de98caed1faee913501923aeb07f43b2 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 4 Feb 2025 00:52:15 +0800 Subject: [PATCH 3/4] f --- test/lib/core/view.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/lib/core/view.test.ts b/test/lib/core/view.test.ts index 70f5d29282..b9b523277c 100644 --- a/test/lib/core/view.test.ts +++ b/test/lib/core/view.test.ts @@ -18,8 +18,12 @@ describe('test/lib/core/view.test.ts', () => { describe('use', () => { it('should register success', () => { class View { - render() {} - renderString() {} + async render() { + return ''; + } + async renderString() { + return ''; + } } app.view.use('e', View); // assert(app.view.has('e')); From 1ccd59e8a00a482aaea451557ea7125d41ecf5b5 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Tue, 4 Feb 2025 10:07:07 +0800 Subject: [PATCH 4/4] f --- index-old.d.ts | 1140 ------------------------------------------------ 1 file changed, 1140 deletions(-) delete mode 100644 index-old.d.ts diff --git a/index-old.d.ts b/index-old.d.ts deleted file mode 100644 index 4ca765a383..0000000000 --- a/index-old.d.ts +++ /dev/null @@ -1,1140 +0,0 @@ -// import accepts = require('accepts'); -// import { AsyncLocalStorage } from 'async_hooks'; -// import { EventEmitter } from 'events'; -// import { Readable } from 'stream'; -// import { Socket } from 'net'; -// import { IncomingMessage, ServerResponse } from 'http'; -// import KoaApplication = require('koa'); -// import KoaRouter = require('koa-router'); -// import { -// EggLogger as Logger, -// EggLoggers, -// LoggerLevel as EggLoggerLevel, -// EggLoggersOptions, -// EggLoggerOptions, -// EggContextLogger, -// } from 'egg-logger'; -// import { -// RequestOptions2 as RequestOptionsOld, -// HttpClientResponse as HttpClientResponseOld, -// } from 'urllib'; -// import { -// RequestURL as HttpClientRequestURL, -// RequestOptions as HttpClientRequestOptions, -// HttpClientResponse, -// } from 'urllib-next'; -// import { -// EggCoreBase, -// FileLoaderOption, -// EggLoader as CoreLoader, -// EggCoreOptions as CoreOptions, -// EggLoaderOptions as CoreLoaderOptions, -// BaseContextClass as CoreBaseContextClass, -// } from 'egg-core'; -// import EggCookies = require('egg-cookies'); - -// declare module 'egg' { -// export type EggLogger = Logger; -// // plain object -// type PlainObject = { [key: string]: T }; - -// // Remove specific property from the specific class -// type RemoveSpecProp = Pick>; - -// // Usage: -// // ```ts -// // import { HttpClientRequestURL, HttpClientRequestOptions, HttpClientResponse } from 'egg'; -// // async function request(url: HttpClientRequestURL, options: HttpClientRequestOptions): Promise { -// // return await app.httpclient.request(url, options); -// // } -// // ``` -// export { HttpClientRequestURL, HttpClientRequestOptions, HttpClientResponse }; -// // Compatible with both urllib@2 and urllib@3 RequestOptions to request -// export interface EggHttpClient extends EventEmitter { -// request(url: HttpClientRequestURL): Promise | HttpClientResponse>; -// request(url: HttpClientRequestURL, options: RequestOptionsOld | HttpClientRequestOptions): -// Promise | HttpClientResponse>; -// curl(url: HttpClientRequestURL): Promise | HttpClientResponse>; -// curl(url: HttpClientRequestURL, options: RequestOptionsOld | HttpClientRequestOptions): -// Promise | HttpClientResponse>; -// } - -// interface EggHttpConstructor { -// new(app: Application): EggHttpClient; -// } - -// export interface EggContextHttpClient extends EggHttpClient { } -// interface EggContextHttpClientConstructor { -// new(ctx: Context): EggContextHttpClient; -// } - -// /** -// * BaseContextClass is a base class that can be extended, -// * it's instantiated in context level, -// * {@link Helper}, {@link Service} is extending it. -// */ -// export class BaseContextClass extends CoreBaseContextClass { // tslint:disable-line -// /** -// * logger -// */ -// protected logger: EggLogger; -// } - -// export class Boot { -// /** -// * logger -// * @member {EggLogger} -// */ -// protected logger: EggLogger; - -// /** -// * The configuration of application -// * @member {EggAppConfig} -// */ -// protected config: EggAppConfig; - -// /** -// * The instance of agent -// * @member {Agent} -// */ -// protected agent: Agent; - -// /** -// * The instance of app -// * @member {Application} -// */ -// protected app: Application; -// } - -// export type RequestArrayBody = any[]; -// export type RequestObjectBody = PlainObject; -// export interface Request extends KoaApplication.Request { // tslint:disable-line -// /** -// * detect if response should be json -// * 1. url path ends with `.json` -// * 2. response type is set to json -// * 3. detect by request accept header -// * -// * @member {Boolean} Request#acceptJSON -// * @since 1.0.0 -// */ -// acceptJSON: boolean; - -// /** -// * Request remote IPv4 address -// * @member {String} Request#ip -// * @example -// * ```js -// * this.request.ip -// * => '127.0.0.1' -// * => '111.10.2.1' -// * ``` -// */ -// ip: string; - -// /** -// * Get all pass through ip addresses from the request. -// * Enable only on `app.config.proxy = true` -// * -// * @member {Array} Request#ips -// * @example -// * ```js -// * this.request.ips -// * => ['100.23.1.2', '201.10.10.2'] -// * ``` -// */ -// ips: string[]; - -// protocol: string; - -// /** -// * get params pass by querystring, all value are Array type. {@link Request#query} -// * @member {Array} Request#queries -// * @example -// * ```js -// * GET http://127.0.0.1:7001?a=b&a=c&o[foo]=bar&b[]=1&b[]=2&e=val -// * this.queries -// * => -// * { -// * "a": ["b", "c"], -// * "o[foo]": ["bar"], -// * "b[]": ["1", "2"], -// * "e": ["val"] -// * } -// * ``` -// */ -// queries: PlainObject; - -// /** -// * get params pass by querystring, all value are String type. -// * @member {Object} Request#query -// * @example -// * ```js -// * GET http://127.0.0.1:7001?name=Foo&age=20&age=21 -// * this.query -// * => { 'name': 'Foo', 'age': 20 } -// * -// * GET http://127.0.0.1:7001?a=b&a=c&o[foo]=bar&b[]=1&b[]=2&e=val -// * this.query -// * => -// * { -// * "a": "b", -// * "o[foo]": "bar", -// * "b[]": "1", -// * "e": "val" -// * } -// * ``` -// */ -// query: PlainObject; - -// body: any; -// } - -// export interface Response extends KoaApplication.Response { // tslint:disable-line -// /** -// * read response real status code. -// * -// * e.g.: Using 302 status redirect to the global error page -// * instead of show current 500 status page. -// * And access log should save 500 not 302, -// * then the `realStatus` can help us find out the real status code. -// * @member {Number} Context#realStatus -// */ -// realStatus: number; -// body: ResponseBodyT; -// } - -// export type LoggerLevel = EggLoggerLevel; - -// type IgnoreItem = string | RegExp | ((ctx: Context) => boolean); -// type IgnoreOrMatch = IgnoreItem | IgnoreItem[]; - -// /** Custom Loader Configuration */ -// export interface CustomLoaderConfig extends RemoveSpecProp { -// /** -// * an object you wanner load to, value can only be 'ctx' or 'app'. default to app -// */ -// inject?: 'ctx' | 'app'; -// /** -// * whether need to load files in plugins or framework, default to false -// */ -// loadunit?: boolean; -// } - -// export interface HttpClientBaseConfig { -// /** Whether use http keepalive */ -// keepAlive?: boolean; -// /** Free socket after keepalive timeout */ -// freeSocketKeepAliveTimeout?: number; -// /** Free socket after request timeout */ -// freeSocketTimeout?: number; -// /** Request timeout */ -// timeout?: number; -// /** Determines how many concurrent sockets the agent can have open per origin */ -// maxSockets?: number; -// /** The maximum number of sockets that will be left open in the free state */ -// maxFreeSockets?: number; -// } - -// /** HttpClient config */ -// export interface HttpClientConfig extends HttpClientBaseConfig { -// /** http.Agent */ -// httpAgent?: HttpClientBaseConfig; -// /** https.Agent */ -// httpsAgent?: HttpClientBaseConfig; -// /** Default request args for httpclient */ -// request?: HttpClientRequestOptions | RequestOptionsOld; -// /** Whether enable dns cache */ -// enableDNSCache?: boolean; -// /** Enable proxy request, default is false. */ -// enableProxy?: boolean; -// /** proxy agent uri or options, default is null. */ -// proxy?: string | { [key: string]: any }; -// /** DNS cache lookup interval */ -// dnsCacheLookupInterval?: number; -// /** DNS cache max age */ -// dnsCacheMaxLength?: number; -// /** use urllib@3 HttpClient */ -// useHttpClientNext?: boolean; -// } - -// export interface EggAppConfig { -// workerStartTimeout: number; -// baseDir: string; -// middleware: string[]; - -// /** -// * The option of `bodyParser` middleware -// * -// * @member Config#bodyParser -// * @property {Boolean} enable - enable bodyParser or not, default to true -// * @property {String | RegExp | Function | Array} ignore - won't parse request body when url path hit ignore pattern, can not set `ignore` when `match` presented -// * @property {String | RegExp | Function | Array} match - will parse request body only when url path hit match pattern -// * @property {String} encoding - body encoding config, default utf8 -// * @property {String} formLimit - form body size limit, default 1mb -// * @property {String} jsonLimit - json body size limit, default 1mb -// * @property {String} textLimit - json body size limit, default 1mb -// * @property {Boolean} strict - json body strict mode, if set strict value true, then only receive object and array json body -// * @property {Number} queryString.arrayLimit - from item array length limit, default 100 -// * @property {Number} queryString.depth - json value deep length, default 5 -// * @property {Number} queryString.parameterLimit - parameter number limit, default 1000 -// * @property {String[]} enableTypes - parser will only parse when request type hits enableTypes, default is ['json', 'form'] -// * @property {Object} extendTypes - support extend types -// * @property {String} onProtoPoisoning - Defines what action must take when parsing a JSON object with `__proto__`. Possible values are `'error'`, `'remove'` and `'ignore'`. Default is `'error'`, it will return `400` response when `Prototype-Poisoning` happen. -// */ -// bodyParser: { -// enable: boolean; -// encoding: string; -// formLimit: string; -// jsonLimit: string; -// textLimit: string; -// strict: boolean; -// queryString: { -// arrayLimit: number; -// depth: number; -// parameterLimit: number; -// }; -// ignore: IgnoreOrMatch; -// match: IgnoreOrMatch; -// enableTypes: string[]; -// extendTypes: { -// json: string[]; -// form: string[]; -// text: string[]; -// }; -// /** Default is `'error'`, it will return `400` response when `Prototype-Poisoning` happen. */ -// onProtoPoisoning: 'error' | 'remove' | 'ignore'; -// }; - -// /** -// * logger options -// * @member Config#logger -// * @property {String} dir - directory of log files -// * @property {String} encoding - log file encloding, defaults to utf8 -// * @property {String} level - default log level, could be: DEBUG, INFO, WARN, ERROR or NONE, defaults to INFO in production -// * @property {String} consoleLevel - log level of stdout, defaults to INFO in local serverEnv, defaults to WARN in unittest, defaults to NONE elsewise -// * @property {Boolean} disableConsoleAfterReady - disable logger console after app ready. defaults to `false` on local and unittest env, others is `true`. -// * @property {Boolean} outputJSON - log as JSON or not, defaults to false -// * @property {Boolean} buffer - if enabled, flush logs to disk at a certain frequency to improve performance, defaults to true -// * @property {String} errorLogName - file name of errorLogger -// * @property {String} coreLogName - file name of coreLogger -// * @property {String} agentLogName - file name of agent worker log -// * @property {Object} coreLogger - custom config of coreLogger -// * @property {Boolean} allowDebugAtProd - allow debug log at prod, defaults to false -// * @property {Boolean} enableFastContextLogger - using the app logger instead of EggContextLogger, defaults to false -// */ -// logger: EggLoggerConfig; - -// /** custom logger of egg */ -// customLogger: { -// [key: string]: EggLoggerOptions; -// }; - -// /** Configuration of httpclient in egg. */ -// httpclient: HttpClientConfig; - -// development: { -// /** -// * dirs needed watch, when files under these change, application will reload, use relative path -// */ -// watchDirs: string[]; -// /** -// * dirs don't need watch, including subdirectories, use relative path -// */ -// ignoreDirs: string[]; -// /** -// * don't wait all plugins ready, default is true. -// */ -// fastReady: boolean; -// /** -// * whether reload on debug, default is true. -// */ -// reloadOnDebug: boolean; -// /** -// * whether override default watchDirs, default is false. -// */ -// overrideDefault: boolean; -// /** -// * whether override default ignoreDirs, default is false. -// */ -// overrideIgnore: boolean; -// /** -// * whether to reload, use https://github.com/sindresorhus/multimatch -// */ -// reloadPattern: string[] | string; -// }; - -// /** -// * customLoader config -// */ -// customLoader: { -// [key: string]: CustomLoaderConfig; -// }; - -// /** -// * It will ignore special keys when dumpConfig -// */ -// dump: { -// ignore: Set; -// }; - -// /** -// * The environment of egg -// */ -// env: EggEnvType; - -// /** -// * The current HOME directory -// */ -// HOME: string; - -// hostHeaders: string; - -// /** -// * Detect request' ip from specified headers, not case-sensitive. Only worked when config.proxy set to true. -// */ -// ipHeaders: string; - -// /** -// * The key that signing cookies. It can contain multiple keys seperated by . -// */ -// keys: string; - -// /** -// * The name of the application -// */ -// name: string; - -// /** -// * package.json -// */ -// pkg: any; - -// rundir: string; - -// security: { -// domainWhiteList: string[]; -// protocolWhiteList: string[]; -// defaultMiddleware: string; -// csrf: any; -// ssrf: { -// ipBlackList: string[]; -// ipExceptionList: string[]; -// checkAddress?(ip: string): boolean; -// }; -// xframe: { -// enable: boolean; -// value: 'SAMEORIGIN' | 'DENY' | 'ALLOW-FROM'; -// }; -// hsts: any; -// methodnoallow: { enable: boolean }; -// noopen: { enable: boolean; } -// xssProtection: any; -// csp: any; -// }; - -// siteFile: PlainObject; - -// watcher: PlainObject; - -// onClientError(err: Error, socket: Socket, app: EggApplication): ClientErrorResponse | Promise; - -// /** -// * server timeout in milliseconds, default to 0 (no timeout). -// * -// * for special request, just use `ctx.req.setTimeout(ms)` -// * -// * @see https://nodejs.org/api/http.html#http_server_timeout -// */ -// serverTimeout: number | null; - -// [prop: string]: any; -// } - -// export interface ClientErrorResponse { -// body: string | Buffer; -// status: number; -// headers: { [key: string]: string }; -// } - -// export interface Router extends Omit, 'url'> { -// /** -// * restful router api -// */ -// resources(name: string, prefix: string, ...middleware: any[]): Router; - -// /** -// * @param {String} name - Router name -// * @param {Object} [params] - more parameters -// * @example -// * ```js -// * router.url('edit_post', { id: 1, name: 'foo', page: 2 }) -// * => /posts/1/edit?name=foo&page=2 -// * router.url('posts', { name: 'foo&1', page: 2 }) -// * => /posts?name=foo%261&page=2 -// * ``` -// * @return {String} url by path name and query params. -// * @since 1.0.0 -// */ -// url(name: string, params?: any): string; -// /** -// * Alias for the url method -// */ -// pathFor(name: string, params?: any): string; -// methods: string[]; -// } - -// export interface EggApplication extends Omit, 'ctxStorage' | 'currentContext'> { -// /** -// * HttpClient instance -// */ -// httpclient: EggHttpClient; - -// /** -// * Logger for Application, wrapping app.coreLogger with context infomation -// * -// * @member {ContextLogger} Context#logger -// * @since 1.0.0 -// * @example -// * ```js -// * this.logger.info('some request data: %j', this.request.body); -// * this.logger.warn('WARNING!!!!'); -// * ``` -// */ -// logger: EggLogger; - -// /** -// * core logger for framework and plugins, log file is $HOME/logs/{appname}/egg-web -// */ -// coreLogger: EggLogger; - -// /** -// * All loggers contain logger, coreLogger and customLogger -// */ -// loggers: EggLoggers; - -// /** -// * messenger instance -// */ -// messenger: Messenger; - -// /** -// * get router -// */ -// router: Router; - -// /** -// * create a singleton instance -// */ -// addSingleton(name: string, create: any): void; - -// runSchedule(schedulePath: string, ...args: any[]): Promise; - -// /** -// * http request helper base on httpclient, it will auto save httpclient log. -// * Keep the same api with httpclient.request(url, args). -// * See https://github.com/node-modules/urllib#api-doc for more details. -// */ -// curl: EggHttpClient['request']; - -// /** -// * Get logger by name, it's equal to app.loggers['name'], but you can extend it with your own logical -// */ -// getLogger(name: string): EggLogger; - -// /** -// * print the infomation when console.log(app) -// */ -// inspect(): any; - -// /** -// * Alias to Router#url -// */ -// url(name: string, params: any): any; - -// /** -// * Create an anonymous context, the context isn't request level, so the request is mocked. -// * then you can use context level API like `ctx.service` -// * @member {String} EggApplication#createAnonymousContext -// * @param {Request} req - if you want to mock request like querystring, you can pass an object to this function. -// * @return {Context} context -// */ -// createAnonymousContext(req?: Request): Context; - -// /** -// * export context base classes, let framework can impl sub class and over context extend easily. -// */ -// ContextCookies: typeof EggCookies; -// ContextLogger: typeof EggContextLogger; -// ContextHttpClient: EggContextHttpClientConstructor; -// HttpClient: EggHttpConstructor; -// Subscription: typeof Subscription; -// Controller: typeof Controller; -// Service: typeof Service; -// } - -// // compatible -// export class EggApplication { -// constructor(options?: CoreOptions); -// } - -// export type RouterPath = string | RegExp; - -// export class Application extends EggApplication { -// /** -// * global locals for view -// * @see Context#locals -// */ -// locals: IApplicationLocals; - -// /** -// * HTTP get method -// */ -// get(path: RouterPath, fn: string): void; -// get(path: RouterPath, ...middleware: any[]): void; - -// /** -// * HTTP post method -// */ -// post(path: RouterPath, fn: string): void; -// post(path: RouterPath, ...middleware: any[]): void; - -// /** -// * HTTP put method -// */ -// put(path: RouterPath, fn: string): void; -// put(path: RouterPath, ...middleware: any[]): void; - -// /** -// * HTTP patch method -// */ -// patch(path: RouterPath, fn: string): void; -// patch(path: RouterPath, ...middleware: any[]): void; - -// /** -// * HTTP delete method -// */ -// delete(path: RouterPath, fn: string): void; -// delete(path: RouterPath, ...middleware: any[]): void; - -// /** -// * restful router api -// */ -// resources(name: string, prefix: string, fn: string): Router; -// resources(path: string, prefix: string, ...middleware: any[]): Router; - -// redirect(path: string, redirectPath: string): void; - -// controller: IController; - -// middleware: KoaApplication.Middleware[] & IMiddleware; - -// /** -// * Run async function in the background -// * @see Context#runInBackground -// * @param {Function} scope - the first args is an anonymous ctx -// */ -// runInBackground(scope: (ctx: Context) => void): void; - -// /** -// * Run async function in the anonymous context scope -// * @see Context#runInAnonymousContextScope -// * @param {Function} scope - the first args is an anonymous ctx, scope should be async function -// * @param {Request} req - if you want to mock request like querystring, you can pass an object to this function. -// */ -// runInAnonymousContextScope(scope: (ctx: Context) => Promise, req?: Request): Promise; - -// /** -// * Get current execute ctx async local storage -// * @return {AsyncLocalStorage} localStorage - store current execute Context -// */ -// get ctxStorage(): AsyncLocalStorage; - -// /** -// * Get current execute ctx, maybe undefined -// * @return {Context} ctx - current execute Context -// */ -// get currentContext(): Context; -// } - -// export interface IApplicationLocals extends PlainObject { } - -// export interface FileStream extends Readable { // tslint:disable-line -// fields: any; - -// filename: string; - -// fieldname: string; - -// mime: string; - -// mimeType: string; - -// transferEncoding: string; - -// encoding: string; - -// truncated: boolean; -// } - -// interface GetFileStreamOptions { -// requireFile?: boolean; // required file submit, default is true -// defCharset?: string; -// limits?: { -// fieldNameSize?: number; -// fieldSize?: number; -// fields?: number; -// fileSize?: number; -// files?: number; -// parts?: number; -// headerPairs?: number; -// }; -// checkFile?( -// fieldname: string, -// file: any, -// filename: string, -// encoding: string, -// mimetype: string -// ): void | Error; -// } - -// /** -// * KoaApplication's Context will carry the default 'cookie' property in -// * the egg's Context interface, which is wrong here because we have our own -// * special properties (e.g: encrypted). So we must remove this property and -// * create our own with the same name. -// * @see https://github.com/eggjs/egg/pull/2958 -// * -// * However, the latest version of Koa has "[key: string]: any" on the -// * context, and there'll be a type error for "keyof koa.Context". -// * So we have to directly inherit from "KoaApplication.BaseContext" and -// * rewrite all the properties to be compatible with types in Koa. -// * @see https://github.com/eggjs/egg/pull/3329 -// */ -// export interface Context extends KoaApplication.BaseContext { -// [key: string]: any; -// body: ResponseBodyT; - -// app: Application; - -// // properties of koa.Context -// req: IncomingMessage; -// res: ServerResponse; -// originalUrl: string; -// respond?: boolean; - -// service: IService; - -// request: Request; - -// response: Response; - -// // The new 'cookies' instead of Koa's. -// cookies: EggCookies; - -// helper: IHelper; - -// /** -// * Resource Parameters -// * @example -// * ##### ctx.params.id {string} -// * -// * `GET /api/users/1` => `'1'` -// * -// * ##### ctx.params.ids {Array} -// * -// * `GET /api/users/1,2,3` => `['1', '2', '3']` -// * -// * ##### ctx.params.fields {Array} -// * -// * Expect request return data fields, for example -// * `GET /api/users/1?fields=name,title` => `['name', 'title']`. -// * -// * ##### ctx.params.data {Object} -// * -// * Tht request data object -// * -// * ##### ctx.params.page {Number} -// * -// * Page number, `GET /api/users?page=10` => `10` -// * -// * ##### ctx.params.per_page {Number} -// * -// * The number of every page, `GET /api/users?per_page=20` => `20` -// */ -// params: any; - -// /** -// * @see Request#query -// */ -// query: PlainObject; - -// /** -// * @see Request#queries -// */ -// queries: PlainObject; - -// /** -// * @see Request#accept -// */ -// accept: accepts.Accepts; - -// /** -// * @see Request#acceptJSON -// */ -// acceptJSON: boolean; - -// /** -// * @see Request#ip -// */ -// ip: string; - -// /** -// * @see Response#realStatus -// */ -// realStatus: number; - -// /** -// * Set the ctx.body.data value -// * -// * @member {Object} Context#data= -// * @example -// * ```js -// * ctx.data = { -// * id: 1, -// * name: 'fengmk2' -// * }; -// * ``` -// * -// * will get responce -// * -// * ```js -// * HTTP/1.1 200 OK -// * -// * { -// * "data": { -// * "id": 1, -// * "name": "fengmk2" -// * } -// * } -// * ``` -// */ -// data: any; - -// /** -// * set ctx.body.meta value -// * -// * @example -// * ```js -// * ctx.meta = { -// * count: 100 -// * }; -// * ``` -// * will get responce -// * -// * ```js -// * HTTP/1.1 200 OK -// * -// * { -// * "meta": { -// * "count": 100 -// * } -// * } -// * ``` -// */ -// meta: any; - -// /** -// * locals is an object for view, you can use `app.locals` and `ctx.locals` to set variables, -// * which will be used as data when view is rendering. -// * The difference between `app.locals` and `ctx.locals` is the context level, `app.locals` is global level, and `ctx.locals` is request level. when you get `ctx.locals`, it will merge `app.locals`. -// * -// * when you set locals, only object is available -// * -// * ```js -// * this.locals = { -// * a: 1 -// * }; -// * this.locals = { -// * b: 1 -// * }; -// * this.locals.c = 1; -// * console.log(this.locals); -// * { -// * a: 1, -// * b: 1, -// * c: 1, -// * }; -// * ``` -// * -// * `ctx.locals` has cache, it only merges `app.locals` once in one request. -// * -// * @member {Object} Context#locals -// */ -// locals: IApplicationLocals & IContextLocals; - -// /** -// * alias to {@link locals}, compatible with koa that use this variable -// */ -// state: any; - -// /** -// * Logger for Application, wrapping app.coreLogger with context infomation -// * -// * @member {ContextLogger} Context#logger -// * @since 1.0.0 -// * @example -// * ```js -// * this.logger.info('some request data: %j', this.request.body); -// * this.logger.warn('WARNING!!!!'); -// * ``` -// */ -// logger: EggLogger; - -// /** -// * Get logger by name, it's equal to app.loggers['name'], but you can extend it with your own logical -// */ -// getLogger(name: string): EggLogger; - -// /** -// * Request start time -// */ -// starttime: number; - -// /** -// * Request start timer using `performance.now()` -// */ -// performanceStarttime?: number; - -// /** -// * http request helper base on httpclient, it will auto save httpclient log. -// * Keep the same api with httpclient.request(url, args). -// * See https://github.com/node-modules/urllib#api-doc for more details. -// */ -// curl: EggHttpClient['request']; - -// /** -// * get upload file stream -// * @example -// * ```js -// * const stream = await this.getFileStream(); -// * // get other fields -// * console.log(stream.fields); -// * ``` -// * @function Context#getFileStream -// * @param {Object} options -// * @return {ReadStream} stream -// * @since 1.0.0 -// */ -// getFileStream(options?: GetFileStreamOptions): Promise; - -// /** -// * @see Responce.redirect -// */ -// redirect(url: string, alt?: string): void; - -// httpclient: EggContextHttpClient; -// } - -// export interface IContextLocals extends PlainObject { } - -// export class Controller extends BaseContextClass { } - -// export class Service extends BaseContextClass { } - -// export class Subscription extends BaseContextClass { } - -// /** -// * The empty interface `IService` is a placeholder, for egg -// * to auto injection service to ctx.service -// * -// * @example -// * -// * import { Service } from 'egg'; -// * class FooService extends Service { -// * async bar() {} -// * } -// * -// * declare module 'egg' { -// * export interface IService { -// * foo: FooService; -// * } -// * } -// * -// * Now I can get ctx.service.foo at controller and other service file. -// */ -// export interface IService extends PlainObject { } // tslint:disable-line - -// export interface IController extends PlainObject { } // tslint:disable-line - -// export interface IMiddleware extends PlainObject { } // tslint:disable-line - -// export interface IHelper extends PlainObject, BaseContextClass { -// /** -// * Generate URL path(without host) for route. Takes the route name and a map of named params. -// * @function Helper#pathFor -// * @param {String} name - Router Name -// * @param {Object} params - Other params -// * -// * @example -// * ```js -// * app.get('home', '/index.htm', 'home.index'); -// * ctx.helper.pathFor('home', { by: 'recent', limit: 20 }) -// * => /index.htm?by=recent&limit=20 -// * ``` -// * @return {String} url path(without host) -// */ -// pathFor(name: string, params?: PlainObject): string; - -// /** -// * Generate full URL(with host) for route. Takes the route name and a map of named params. -// * @function Helper#urlFor -// * @param {String} name - Router name -// * @param {Object} params - Other params -// * @example -// * ```js -// * app.get('home', '/index.htm', 'home.index'); -// * ctx.helper.urlFor('home', { by: 'recent', limit: 20 }) -// * => http://127.0.0.1:7001/index.htm?by=recent&limit=20 -// * ``` -// * @return {String} full url(with host) -// */ -// urlFor(name: string, params?: PlainObject): string; -// } - -// /** -// * Singleton instance in Agent Worker, extend {@link EggApplication} -// */ -// export class Agent extends EggApplication { -// } - -// export interface ClusterOptions { -// /** specify framework that can be absolute path or npm package */ -// framework?: string; -// /** directory of application, default to `process.cwd()` */ -// baseDir?: string; -// /** customized plugins, for unittest */ -// plugins?: object | null; -// /** numbers of app workers, default to `os.cpus().length` */ -// workers?: number; -// /** listening port, default to 7001(http) or 8443(https) */ -// port?: number; -// /** https or not */ -// https?: boolean; -// /** ssl key */ -// key?: string; -// /** ssl cert */ -// cert?: string; -// [prop: string]: any; -// } - -// export function startCluster(options: ClusterOptions, callback: (...args: any[]) => any): void; - -// export interface StartOptions { -// /** specify framework that can be absolute path or npm package */ -// framework?: string; -// /** directory of application, default to `process.cwd()` */ -// baseDir?: string; -// /** ignore single process mode warning */ -// ignoreWarning?: boolean; -// } - -// export function start(options?: StartOptions): Promise; - -// // send data can be number|string|boolean|object but not Set|Map -// export interface Messenger extends EventEmitter { -// /** -// * broadcast to all agent/app processes including itself -// */ -// broadcast(action: string, data: any): void; - -// /** -// * send to agent from the app, -// * send to an random app from the agent -// */ -// sendRandom(action: string, data: any): void; - -// /** -// * send to specified process -// */ -// sendTo(pid: number, action: string, data: any): void; - -// /** -// * send to agent from the app, -// * send to itself from the agent -// */ -// sendToAgent(action: string, data: any): void; - -// /** -// * send to all app including itself from the app, -// * send to all app from the agent -// */ -// sendToApp(action: string, data: any): void; -// } - -// // compatible -// export interface EggLoaderOptions extends CoreLoaderOptions { } -// export interface EggLoader extends CoreLoader { } - -// /** -// * App worker process Loader, will load plugins -// * @see https://github.com/eggjs/egg-core -// */ -// export class AppWorkerLoader extends CoreLoader { -// loadConfig(): void; -// load(): void; -// } - -// /** -// * Agent worker process loader -// * @see https://github.com/eggjs/egg-loader -// */ -// export class AgentWorkerLoader extends CoreLoader { -// loadConfig(): void; -// load(): void; -// } - -// export interface IBoot { -// /** -// * Ready to call configDidLoad, -// * Config, plugin files are referred, -// * this is the last chance to modify the config. -// */ -// configWillLoad?(): void; - -// /** -// * Config, plugin files have loaded -// */ -// configDidLoad?(): void; - -// /** -// * All files have loaded, start plugin here -// */ -// didLoad?(): Promise; - -// /** -// * All plugins have started, can do some thing before app ready -// */ -// willReady?(): Promise; - -// /** -// * Worker is ready, can do some things, -// * don't need to block the app boot -// */ -// didReady?(): Promise; - -// /** -// * Server is listening -// */ -// serverDidReady?(): Promise; - -// /** -// * Do some thing before app close -// */ -// beforeClose?(): Promise; -// } - -// export interface Singleton { -// get(id: string): T; -// } -// }