@taicode/common-server 4.0.0-beta.2 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/output/app/boot.d.ts +9 -4
- package/output/app/boot.d.ts.map +1 -1
- package/output/app/boot.js +30 -12
- package/output/app/context.d.ts +4 -0
- package/output/app/context.d.ts.map +1 -1
- package/output/app/context.js +9 -3
- package/output/app/entries.d.ts +2 -0
- package/output/app/entries.d.ts.map +1 -1
- package/output/app/entries.js +2 -0
- package/output/app/lifecycle-logging.test.js +23 -3
- package/output/app/lifecycle.d.ts +32 -6
- package/output/app/lifecycle.d.ts.map +1 -1
- package/output/app/lifecycle.js +34 -6
- package/output/app/lifecycle.test.js +4 -3
- package/output/app/middleware-order.test.js +4 -3
- package/output/app/redis.d.ts +5 -1
- package/output/app/redis.d.ts.map +1 -1
- package/output/app/redis.js +5 -1
- package/output/app/resources.test.js +43 -20
- package/output/app/shutdown.d.ts +9 -4
- package/output/app/shutdown.d.ts.map +1 -1
- package/output/app/shutdown.js +16 -4
- package/output/app/shutdown.test.js +124 -17
- package/output/app/types.d.ts +28 -5
- package/output/app/types.d.ts.map +1 -1
- package/package.json +3 -3
package/output/app/boot.d.ts
CHANGED
|
@@ -27,13 +27,18 @@ export interface BootedServer<R, S> {
|
|
|
27
27
|
* 步序(N1 §5):
|
|
28
28
|
* ```
|
|
29
29
|
* 1. 加载 env(框架固定,调用方已做) → env
|
|
30
|
-
* 2. resources(ctx={env,log})
|
|
31
|
-
* 3. config(ctx={env,log,resources})
|
|
30
|
+
* 2. resources(ctx={env,log,disposer}) → R(只构造,不建连)
|
|
31
|
+
* 3. config(ctx={env,log,disposer,resources}) → C(仅 start 路径;出 spec 路径不跑)
|
|
32
32
|
* 4. services(ctx={log,disposer,resources,config}) → S,随后按声明序 init()(仅 start 路径)
|
|
33
|
-
* 5. middlewares(ctx={app,log,services,config})
|
|
34
|
-
* 6. routers(ctx={app,log,services})
|
|
33
|
+
* 5. middlewares(ctx={app,log,disposer,services,config}) → 按数组序 register(app)
|
|
34
|
+
* 6. routers(ctx={app,log,disposer,services}) → 注册路由
|
|
35
35
|
* 7. ready / listen
|
|
36
36
|
* ```
|
|
37
|
+
*
|
|
38
|
+
* **每个阶段一个独立 `Disposer`**(构造处注册释放);关闭时按阶段**逆序 flush**
|
|
39
|
+
* `routers → middlewares → services → config → resources`(services 阶段内部先 flush 自己的
|
|
40
|
+
* `disposer`,再逐单元逆序 `dispose`)。资源因此**最后**才断:service 关闭时若还要落库不会
|
|
41
|
+
* 撞死连接。出 spec 路径不关闭,也不执行任何 flush。
|
|
37
42
|
*/
|
|
38
43
|
export declare function bootServer<R, C, S>(input: BootInput<R, C, S>): Promise<BootedServer<R, S>>;
|
|
39
44
|
//# sourceMappingURL=boot.d.ts.map
|
package/output/app/boot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../../source/app/boot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../../source/app/boot.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAgC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AACtG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,UAAU,CAAA;AACvC,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAC3C,mBAAmB;AACnB,MAAM,MAAM,SAAS,GAAG,OAAO,gBAAgB,GAAG,OAAO,kBAAkB,CAAA;AAC3E,aAAa;AACb,eAAO,MAAM,YAAY,YAAY,CAAA;AAErC,MAAM,WAAW,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACvB,GAAG,EAAE,SAAS,CAAA;IACd,KAAK,EAAE,SAAS,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5D;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC;IAChC,GAAG,EAAE,eAAe,CAAA;IACpB,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IAC3B,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC7B;AAqCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CA6HhG"}
|
package/output/app/boot.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Disposer } from '@taicode/common-base';
|
|
2
2
|
import { createConfigContext, createConfigHolder, createMiddlewareContext, createResourcesContext, createRouterContext, createServiceContext, createShutdownContext, liveResources, readConfigKey, requireConfig, } from './context.js';
|
|
3
3
|
import { createFastify, registerCore } from './core/index.js';
|
|
4
|
-
import { disposeServices, initServiceUnits, MIDDLEWARES_LOG_STAGE, middlewareUnitName } from './lifecycle.js';
|
|
4
|
+
import { CONFIG_STAGE, disposeServices, disposeStage, initServiceUnits, MIDDLEWARES_LOG_STAGE, MIDDLEWARES_STAGE, middlewareUnitName, RESOURCES_STAGE, ROUTERS_STAGE, SERVICES_STAGE, } from './lifecycle.js';
|
|
5
5
|
import { runShutdown } from './shutdown.js';
|
|
6
6
|
import { attachSignalHandlers } from './signal.js';
|
|
7
7
|
/** 内部步序(`prepare` → `compose` → `activate`):`startServer` / `generateOpenApi` 共用。 */
|
|
@@ -48,13 +48,18 @@ function combineClassifiers(classifiers) {
|
|
|
48
48
|
* 步序(N1 §5):
|
|
49
49
|
* ```
|
|
50
50
|
* 1. 加载 env(框架固定,调用方已做) → env
|
|
51
|
-
* 2. resources(ctx={env,log})
|
|
52
|
-
* 3. config(ctx={env,log,resources})
|
|
51
|
+
* 2. resources(ctx={env,log,disposer}) → R(只构造,不建连)
|
|
52
|
+
* 3. config(ctx={env,log,disposer,resources}) → C(仅 start 路径;出 spec 路径不跑)
|
|
53
53
|
* 4. services(ctx={log,disposer,resources,config}) → S,随后按声明序 init()(仅 start 路径)
|
|
54
|
-
* 5. middlewares(ctx={app,log,services,config})
|
|
55
|
-
* 6. routers(ctx={app,log,services})
|
|
54
|
+
* 5. middlewares(ctx={app,log,disposer,services,config}) → 按数组序 register(app)
|
|
55
|
+
* 6. routers(ctx={app,log,disposer,services}) → 注册路由
|
|
56
56
|
* 7. ready / listen
|
|
57
57
|
* ```
|
|
58
|
+
*
|
|
59
|
+
* **每个阶段一个独立 `Disposer`**(构造处注册释放);关闭时按阶段**逆序 flush**
|
|
60
|
+
* `routers → middlewares → services → config → resources`(services 阶段内部先 flush 自己的
|
|
61
|
+
* `disposer`,再逐单元逆序 `dispose`)。资源因此**最后**才断:service 关闭时若还要落库不会
|
|
62
|
+
* 撞死连接。出 spec 路径不关闭,也不执行任何 flush。
|
|
58
63
|
*/
|
|
59
64
|
export async function bootServer(input) {
|
|
60
65
|
const { server, env, phase } = input;
|
|
@@ -65,7 +70,14 @@ export async function bootServer(input) {
|
|
|
65
70
|
const app = createFastify(env);
|
|
66
71
|
const instance = app;
|
|
67
72
|
const log = app.log;
|
|
68
|
-
|
|
73
|
+
// 每个阶段一个 Disposer:创建与释放同处一地,关闭时按阶段逆序 flush
|
|
74
|
+
const disposers = {
|
|
75
|
+
resources: new Disposer(),
|
|
76
|
+
config: new Disposer(),
|
|
77
|
+
services: new Disposer(),
|
|
78
|
+
middlewares: new Disposer(),
|
|
79
|
+
routers: new Disposer(),
|
|
80
|
+
};
|
|
69
81
|
const holder = createConfigHolder();
|
|
70
82
|
// ② resources:只构造,不建连(出 spec 路径同样执行)
|
|
71
83
|
// 资源错误分类器由各资源通过 `ctx.registerClassifyError` 各自注册:在调 `resources(ctx)` 前建局部收集器,
|
|
@@ -74,6 +86,7 @@ export async function bootServer(input) {
|
|
|
74
86
|
const resources = (await server.resources(createResourcesContext({
|
|
75
87
|
env,
|
|
76
88
|
log,
|
|
89
|
+
disposer: disposers.resources,
|
|
77
90
|
registerClassifyError: (classifier) => {
|
|
78
91
|
classifiers.push(classifier);
|
|
79
92
|
},
|
|
@@ -89,12 +102,12 @@ export async function bootServer(input) {
|
|
|
89
102
|
// ── compose ──────────────────────────────────────────────────────────────
|
|
90
103
|
// ① config(仅 start 路径;出 spec 路径不跑 → `ctx.config` 读到即抛 CONFIG_UNAVAILABLE)
|
|
91
104
|
if (isStart) {
|
|
92
|
-
holder.current = (await server.config(createConfigContext({ env, log, resources: resourcesView })));
|
|
105
|
+
holder.current = (await server.config(createConfigContext({ env, log, disposer: disposers.config, resources: resourcesView })));
|
|
93
106
|
}
|
|
94
107
|
// ② services(两个入口都执行;ctx 无 env、无 services)
|
|
95
108
|
const services = server.services(createServiceContext({
|
|
96
109
|
log,
|
|
97
|
-
disposer,
|
|
110
|
+
disposer: disposers.services,
|
|
98
111
|
resources: resourcesView,
|
|
99
112
|
holder,
|
|
100
113
|
}));
|
|
@@ -103,20 +116,21 @@ export async function bootServer(input) {
|
|
|
103
116
|
await initServiceUnits({ service, log, services: services });
|
|
104
117
|
}
|
|
105
118
|
// ④ middlewares(两个入口都执行:出 spec 也照注册,保证 schema 一致)
|
|
106
|
-
const middlewares = server.middlewares(createMiddlewareContext({ app: instance, log, services, holder }));
|
|
119
|
+
const middlewares = server.middlewares(createMiddlewareContext({ app: instance, log, disposer: disposers.middlewares, services, holder }));
|
|
107
120
|
for (const [index, unit] of middlewares.entries()) {
|
|
108
121
|
await unit.register(instance);
|
|
109
122
|
log.debug({ service, unit: middlewareUnitName(service, unit, index), stage: MIDDLEWARES_LOG_STAGE }, '[middleware] registered');
|
|
110
123
|
}
|
|
111
124
|
// ⑤ routers(两个入口都执行)
|
|
112
|
-
await server.routers(createRouterContext({ app: instance, log, services }));
|
|
125
|
+
await server.routers(createRouterContext({ app: instance, log, disposer: disposers.routers, services }));
|
|
113
126
|
// ── activate ─────────────────────────────────────────────────────────────
|
|
114
127
|
const runShutdownFlow = async () => {
|
|
115
128
|
if (input.shutdown) {
|
|
129
|
+
// 整体覆盖:自定义 shutdown 自行负责各阶段 flush(框架不再代劳)
|
|
116
130
|
await input.shutdown(createShutdownContext({
|
|
117
131
|
app: instance,
|
|
118
132
|
log,
|
|
119
|
-
disposer,
|
|
133
|
+
disposer: disposers.services,
|
|
120
134
|
resources: resourcesView,
|
|
121
135
|
holder,
|
|
122
136
|
services,
|
|
@@ -124,8 +138,12 @@ export async function bootServer(input) {
|
|
|
124
138
|
return;
|
|
125
139
|
}
|
|
126
140
|
await runShutdown({
|
|
127
|
-
|
|
141
|
+
flushRouters: () => disposeStage({ service, log, stage: ROUTERS_STAGE, disposer: disposers.routers }),
|
|
142
|
+
flushMiddlewares: () => disposeStage({ service, log, stage: MIDDLEWARES_STAGE, disposer: disposers.middlewares }),
|
|
143
|
+
flushServices: () => disposeStage({ service, log, stage: SERVICES_STAGE, disposer: disposers.services }),
|
|
128
144
|
disposeServices: () => disposeServices({ service, log, services: services }),
|
|
145
|
+
flushConfig: () => disposeStage({ service, log, stage: CONFIG_STAGE, disposer: disposers.config }),
|
|
146
|
+
flushResources: () => disposeStage({ service, log, stage: RESOURCES_STAGE, disposer: disposers.resources }),
|
|
129
147
|
}, log);
|
|
130
148
|
};
|
|
131
149
|
app.addHook('onClose', () => runShutdownFlow());
|
package/output/app/context.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export declare function readConfigKey(config: unknown, key: string): unknown;
|
|
|
23
23
|
export interface ResourcesContextParams {
|
|
24
24
|
env: ConfigEnv;
|
|
25
25
|
log: LoggerLike;
|
|
26
|
+
disposer: Disposer;
|
|
26
27
|
/** 收集器:`bootServer` 注入,把各资源注册的分类器按注册顺序收起来 */
|
|
27
28
|
registerClassifyError: (classifier: ErrorClassifier) => void;
|
|
28
29
|
}
|
|
@@ -31,6 +32,7 @@ export declare function createResourcesContext(params: ResourcesContextParams):
|
|
|
31
32
|
export interface ConfigContextParams<R> {
|
|
32
33
|
env: ConfigEnv;
|
|
33
34
|
log: LoggerLike;
|
|
35
|
+
disposer: Disposer;
|
|
34
36
|
resources: ResourcesView<R>;
|
|
35
37
|
}
|
|
36
38
|
export declare function createConfigContext<R>(params: ConfigContextParams<R>): ConfigContext<R>;
|
|
@@ -46,6 +48,7 @@ export declare function createServiceContext<R, C>(params: ServiceContextParams<
|
|
|
46
48
|
export interface MiddlewareContextParams<C, S> {
|
|
47
49
|
app: AppInstance;
|
|
48
50
|
log: LoggerLike;
|
|
51
|
+
disposer: Disposer;
|
|
49
52
|
services: Awaited<S>;
|
|
50
53
|
holder: ConfigHolder<C>;
|
|
51
54
|
}
|
|
@@ -54,6 +57,7 @@ export declare function createMiddlewareContext<C, S>(params: MiddlewareContextP
|
|
|
54
57
|
export interface RouterContextParams<S> {
|
|
55
58
|
app: AppInstance;
|
|
56
59
|
log: LoggerLike;
|
|
60
|
+
disposer: Disposer;
|
|
57
61
|
services: Awaited<S>;
|
|
58
62
|
}
|
|
59
63
|
export declare function createRouterContext<S>(params: RouterContextParams<S>): RouterContext<S>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../source/app/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB;;;;;;GAMG;AAEH,+CAA+C;AAC/C,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQxE;AAED,uDAAuD;AACvD,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;CAChC;AAED,wBAAgB,kBAAkB,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,CAEvD;AAED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CASpE;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,0BAA0B;AAC1B,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,SAAS,CAAA;IACd,GAAG,EAAE,UAAU,CAAA;IACf,6CAA6C;IAC7C,qBAAqB,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAA;CAC7D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../source/app/context.ts"],"names":[],"mappings":"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"}
|
package/output/app/context.js
CHANGED
|
@@ -31,10 +31,15 @@ export function readConfigKey(config, key) {
|
|
|
31
31
|
return config?.[key];
|
|
32
32
|
}
|
|
33
33
|
export function createResourcesContext(params) {
|
|
34
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
env: params.env,
|
|
36
|
+
log: params.log,
|
|
37
|
+
disposer: params.disposer,
|
|
38
|
+
registerClassifyError: params.registerClassifyError,
|
|
39
|
+
};
|
|
35
40
|
}
|
|
36
41
|
export function createConfigContext(params) {
|
|
37
|
-
return { env: params.env, log: params.log, resources: params.resources };
|
|
42
|
+
return { env: params.env, log: params.log, disposer: params.disposer, resources: params.resources };
|
|
38
43
|
}
|
|
39
44
|
export function createServiceContext(params) {
|
|
40
45
|
return {
|
|
@@ -50,6 +55,7 @@ export function createMiddlewareContext(params) {
|
|
|
50
55
|
return {
|
|
51
56
|
app: params.app,
|
|
52
57
|
log: params.log,
|
|
58
|
+
disposer: params.disposer,
|
|
53
59
|
services: params.services,
|
|
54
60
|
get config() {
|
|
55
61
|
return requireConfig(params.holder);
|
|
@@ -57,7 +63,7 @@ export function createMiddlewareContext(params) {
|
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
export function createRouterContext(params) {
|
|
60
|
-
return { app: params.app, log: params.log, services: params.services };
|
|
66
|
+
return { app: params.app, log: params.log, disposer: params.disposer, services: params.services };
|
|
61
67
|
}
|
|
62
68
|
export function createShutdownContext(params) {
|
|
63
69
|
return {
|
package/output/app/entries.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export declare function startServer<R, C, S>(server: Server<R, C, S>, options?:
|
|
|
24
24
|
*
|
|
25
25
|
* **红线**:全程**不得要求配置有效** —— 不跑 `config` 槽位、不要求 `DATABASE_URL` / `REDIS_URL`
|
|
26
26
|
* 有效;`resources`(只构造不建连)/ `services` / 中间件注册 / 路由注册照常执行。
|
|
27
|
+
*
|
|
28
|
+
* 出 spec 路径**不关闭服务**,因此也不会 flush 任何阶段的 `disposer`。
|
|
27
29
|
*/
|
|
28
30
|
export declare function generateOpenApi<R, C, S>(server: Server<R, C, S>): Promise<OpenApiDocument>;
|
|
29
31
|
//# sourceMappingURL=entries.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../source/app/entries.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAMpF;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAMnF;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAavI;AAED
|
|
1
|
+
{"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../source/app/entries.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAMpF;AAED,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAMnF;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAavI;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAiBhG"}
|
package/output/app/entries.js
CHANGED
|
@@ -46,6 +46,8 @@ export async function startServer(server, options = {}) {
|
|
|
46
46
|
*
|
|
47
47
|
* **红线**:全程**不得要求配置有效** —— 不跑 `config` 槽位、不要求 `DATABASE_URL` / `REDIS_URL`
|
|
48
48
|
* 有效;`resources`(只构造不建连)/ `services` / 中间件注册 / 路由注册照常执行。
|
|
49
|
+
*
|
|
50
|
+
* 出 spec 路径**不关闭服务**,因此也不会 flush 任何阶段的 `disposer`。
|
|
49
51
|
*/
|
|
50
52
|
export async function generateOpenApi(server) {
|
|
51
53
|
// 不加载 .env 文件、不读配置
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { Disposer } from '@taicode/common-base';
|
|
2
3
|
import { defineServer, startServer } from './entries.js';
|
|
3
|
-
import { buildDisposeOrder, initServiceUnits, LIFECYCLE_PHASE_DISPOSE, LIFECYCLE_PHASE_INIT, MIDDLEWARES_LOG_STAGE, middlewareUnitName, SERVICES_STAGE, serviceUnitName, } from './lifecycle.js';
|
|
4
|
+
import { buildDisposeOrder, disposeStage, initServiceUnits, LIFECYCLE_PHASE_DISPOSE, LIFECYCLE_PHASE_INIT, MIDDLEWARES_LOG_STAGE, middlewareUnitName, RESOURCES_STAGE, SERVICES_STAGE, serviceUnitName, } from './lifecycle.js';
|
|
4
5
|
import { createRecordingLogger } from './test-support.js';
|
|
5
6
|
/**
|
|
6
7
|
* 逐单元日志。
|
|
7
8
|
*
|
|
8
9
|
* - **注册期**:每个中间件注册后打一条 `debug`,带 `{ service, unit, stage: 'middlewares' }`;
|
|
9
10
|
* - **init / dispose**:逐单元 `debug`(开始 / 完成),失败 `error`;带 `{ service, unit, stage, phase }`;
|
|
11
|
+
* - **阶段 flush**:`disposeStage` 每阶段一条 `debug`(`unit` = 阶段名);
|
|
10
12
|
* - 中间件名字:`unit.name ?? '<service>:middleware[<index>]'`;services 用对象键。
|
|
11
13
|
*
|
|
12
14
|
* 分两层验证:
|
|
@@ -91,6 +93,19 @@ describe('逐单元日志:init / dispose', () => {
|
|
|
91
93
|
expect(middlewareUnitName('svc', {}, 3)).toBe('svc:middleware[3]');
|
|
92
94
|
expect(middlewareUnitName('svc', { name: 'named' }, 1)).toBe('named');
|
|
93
95
|
});
|
|
96
|
+
it('阶段 flush:`unit` / `stage` = 阶段名,`phase` = dispose', async () => {
|
|
97
|
+
const log = createRecordingLogger();
|
|
98
|
+
const disposer = new Disposer();
|
|
99
|
+
await disposeStage({ service: 'svc', log, stage: RESOURCES_STAGE, disposer });
|
|
100
|
+
const fields = log.entries.map((entry) => entry.obj);
|
|
101
|
+
expect(fields.length).toBeGreaterThan(0);
|
|
102
|
+
for (const field of fields) {
|
|
103
|
+
expect(field.service).toBe('svc');
|
|
104
|
+
expect(field.unit).toBe(RESOURCES_STAGE);
|
|
105
|
+
expect(field.stage).toBe(RESOURCES_STAGE);
|
|
106
|
+
expect(field.phase).toBe(LIFECYCLE_PHASE_DISPOSE);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
94
109
|
});
|
|
95
110
|
// ── 真路径:`startServer` 走完整装配,捕获框架 logger 写入 ────────
|
|
96
111
|
const captured = vi.hoisted(() => ({ lines: [] }));
|
|
@@ -148,8 +163,13 @@ describe('startServer 真路径:注册 / init / dispose 逐单元日志', () =
|
|
|
148
163
|
// init / dispose:services 逐单元 debug(带 `phase`)
|
|
149
164
|
expect(matchRecords(records, { unit: 'alpha', stage: SERVICES_STAGE, phase: LIFECYCLE_PHASE_INIT }).length).toBeGreaterThan(0);
|
|
150
165
|
expect(matchRecords(records, { unit: 'alpha', stage: SERVICES_STAGE, phase: LIFECYCLE_PHASE_DISPOSE }).length).toBeGreaterThan(0);
|
|
151
|
-
//
|
|
166
|
+
// 中间件**没有逐单元生命周期**:没有 middlewares 阶段的 init 日志;dispose 只有阶段 flush
|
|
152
167
|
expect(matchRecords(records, { stage: MIDDLEWARES_LOG_STAGE, phase: LIFECYCLE_PHASE_INIT })).toHaveLength(0);
|
|
153
|
-
|
|
168
|
+
const middlewareDispose = matchRecords(records, {
|
|
169
|
+
stage: MIDDLEWARES_LOG_STAGE,
|
|
170
|
+
phase: LIFECYCLE_PHASE_DISPOSE,
|
|
171
|
+
});
|
|
172
|
+
expect(middlewareDispose.length).toBeGreaterThan(0);
|
|
173
|
+
expect(middlewareDispose.every((record) => record.unit === MIDDLEWARES_LOG_STAGE)).toBe(true);
|
|
154
174
|
});
|
|
155
175
|
});
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
+
import type { Disposer } from '@taicode/common-base';
|
|
1
2
|
import type { LoggerLike } from './types.js';
|
|
2
3
|
/**
|
|
3
4
|
* 生命周期序(N1 §4 / N2 §2.5)。
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
+
* **每个阶段一个 `Disposer`**(`ctx.disposer`,所有槽位都有):在创建某个东西的地方当场
|
|
7
|
+
* `ctx.disposer.add(() => 释放它)` —— 创建与释放同处一地。
|
|
8
|
+
*
|
|
9
|
+
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → config → resources`
|
|
10
|
+
* (后创建的先销毁;Prisma / Redis 这类资源最后才断)。
|
|
11
|
+
*
|
|
12
|
+
* **`services` 额外有逐单元生命周期**:
|
|
6
13
|
* - 单元之间**不得相互依赖**(`services` 的 ctx 没有 `services` 字段,编译期禁止);
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
14
|
+
* - `init` 按**对象声明序**,`dispose` **逆序**;
|
|
15
|
+
* - services 阶段内部**先 flush `ctx.disposer`**(构造期注册的清理),再逐单元 `dispose`。
|
|
16
|
+
*
|
|
17
|
+
* `middlewares` 按数组序注册;它只有 `disposer`,没有逐单元生命周期。
|
|
10
18
|
*/
|
|
11
19
|
export declare const SERVICES_STAGE = "services";
|
|
12
|
-
|
|
20
|
+
export declare const CONFIG_STAGE = "config";
|
|
21
|
+
export declare const MIDDLEWARES_STAGE = "middlewares";
|
|
22
|
+
export declare const ROUTERS_STAGE = "routers";
|
|
23
|
+
export declare const RESOURCES_STAGE = "resources";
|
|
24
|
+
/** `middlewares` 的**注册期日志**阶段标签(与生命周期阶段同名) */
|
|
13
25
|
export declare const MIDDLEWARES_LOG_STAGE = "middlewares";
|
|
14
26
|
/** 生命周期相位(逐单元日志字段 `phase`) */
|
|
15
27
|
export declare const LIFECYCLE_PHASE_INIT = "init";
|
|
@@ -27,7 +39,7 @@ export interface LifecycleLogContext {
|
|
|
27
39
|
/** 记录型 logger(`ctx.log`);缺省 = 不打日志 */
|
|
28
40
|
log?: LoggerLike;
|
|
29
41
|
}
|
|
30
|
-
/** 逐单元日志字段(全拼):`unit` =
|
|
42
|
+
/** 逐单元日志字段(全拼):`unit` = 有效名字(阶段 flush 时为阶段名),`stage` = 阶段名 */
|
|
31
43
|
export interface LifecycleUnitLogFields {
|
|
32
44
|
service: string;
|
|
33
45
|
unit: string;
|
|
@@ -67,4 +79,18 @@ export declare function buildDisposeOrder(params: DisposeServiceUnitsParams): Ar
|
|
|
67
79
|
* 此处仅保证单步不阻断后续。
|
|
68
80
|
*/
|
|
69
81
|
export declare function disposeServices(params: DisposeServiceUnitsParams): Promise<void>;
|
|
82
|
+
/** `disposeStage` 入参(参数签名内联对象类型已按 lint 规则外提) */
|
|
83
|
+
export interface DisposeStageParams extends LifecycleLogContext {
|
|
84
|
+
/** 阶段名(`routers` / `middlewares` / `services` / `config` / `resources`) */
|
|
85
|
+
stage: string;
|
|
86
|
+
/** 该阶段的 `Disposer`(构造期注册的清理函数,**逆注册序**执行) */
|
|
87
|
+
disposer: Disposer;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* 关闭步:flush 某个阶段的 `disposer`。
|
|
91
|
+
*
|
|
92
|
+
* 单元名 = 阶段名(一个阶段一个 `Disposer`,不逐注册项打日志);`phase` 固定 `dispose`。
|
|
93
|
+
* 单个清理函数失败由 `Disposer` 自己捕获记录,不影响同阶段其它函数。
|
|
94
|
+
*/
|
|
95
|
+
export declare function disposeStage(params: DisposeStageParams): Promise<void>;
|
|
70
96
|
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../source/app/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../source/app/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;;;GAeG;AAEH,eAAO,MAAM,cAAc,aAAa,CAAA;AACxC,eAAO,MAAM,YAAY,WAAW,CAAA;AACpC,eAAO,MAAM,iBAAiB,gBAAgB,CAAA;AAC9C,eAAO,MAAM,aAAa,YAAY,CAAA;AACtC,eAAO,MAAM,eAAe,cAAc,CAAA;AAC1C,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,gBAAoB,CAAA;AAEtD,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,SAAS,CAAA;AAC1C,eAAO,MAAM,uBAAuB,YAAY,CAAA;AAChD,MAAM,MAAM,cAAc,GAAG,OAAO,oBAAoB,GAAG,OAAO,uBAAuB,CAAA;AAEzF;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sCAAsC;IACtC,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB;AAED,8DAA8D;AAC9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,cAAc,CAAA;CACtB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxF;AAED,oDAAoD;AACpD,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAmCD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpF;AAED,6EAA6E;AAC7E,MAAM,WAAW,yBAA0B,SAAQ,mBAAmB;IACpE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,yBAAyB,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAMtG;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;AAED,gDAAgD;AAChD,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB;IAC7D,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,6CAA6C;IAC7C,QAAQ,EAAE,QAAQ,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5E"}
|
package/output/app/lifecycle.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 生命周期序(N1 §4 / N2 §2.5)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* **每个阶段一个 `Disposer`**(`ctx.disposer`,所有槽位都有):在创建某个东西的地方当场
|
|
5
|
+
* `ctx.disposer.add(() => 释放它)` —— 创建与释放同处一地。
|
|
6
|
+
*
|
|
7
|
+
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → config → resources`
|
|
8
|
+
* (后创建的先销毁;Prisma / Redis 这类资源最后才断)。
|
|
9
|
+
*
|
|
10
|
+
* **`services` 额外有逐单元生命周期**:
|
|
5
11
|
* - 单元之间**不得相互依赖**(`services` 的 ctx 没有 `services` 字段,编译期禁止);
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
12
|
+
* - `init` 按**对象声明序**,`dispose` **逆序**;
|
|
13
|
+
* - services 阶段内部**先 flush `ctx.disposer`**(构造期注册的清理),再逐单元 `dispose`。
|
|
14
|
+
*
|
|
15
|
+
* `middlewares` 按数组序注册;它只有 `disposer`,没有逐单元生命周期。
|
|
9
16
|
*/
|
|
10
17
|
export const SERVICES_STAGE = 'services';
|
|
11
|
-
|
|
12
|
-
export const
|
|
18
|
+
export const CONFIG_STAGE = 'config';
|
|
19
|
+
export const MIDDLEWARES_STAGE = 'middlewares';
|
|
20
|
+
export const ROUTERS_STAGE = 'routers';
|
|
21
|
+
export const RESOURCES_STAGE = 'resources';
|
|
22
|
+
/** `middlewares` 的**注册期日志**阶段标签(与生命周期阶段同名) */
|
|
23
|
+
export const MIDDLEWARES_LOG_STAGE = MIDDLEWARES_STAGE;
|
|
13
24
|
/** 生命周期相位(逐单元日志字段 `phase`) */
|
|
14
25
|
export const LIFECYCLE_PHASE_INIT = 'init';
|
|
15
26
|
export const LIFECYCLE_PHASE_DISPOSE = 'dispose';
|
|
@@ -97,3 +108,20 @@ export async function disposeServices(params) {
|
|
|
97
108
|
}
|
|
98
109
|
}
|
|
99
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* 关闭步:flush 某个阶段的 `disposer`。
|
|
113
|
+
*
|
|
114
|
+
* 单元名 = 阶段名(一个阶段一个 `Disposer`,不逐注册项打日志);`phase` 固定 `dispose`。
|
|
115
|
+
* 单个清理函数失败由 `Disposer` 自己捕获记录,不影响同阶段其它函数。
|
|
116
|
+
*/
|
|
117
|
+
export async function disposeStage(params) {
|
|
118
|
+
const fields = {
|
|
119
|
+
service: params.service ?? '',
|
|
120
|
+
unit: params.stage,
|
|
121
|
+
stage: params.stage,
|
|
122
|
+
phase: LIFECYCLE_PHASE_DISPOSE,
|
|
123
|
+
};
|
|
124
|
+
params.log?.debug(fields, `[lifecycle] ${LIFECYCLE_PHASE_DISPOSE} "${params.stage}" start`);
|
|
125
|
+
await params.disposer.dispose();
|
|
126
|
+
params.log?.debug(fields, `[lifecycle] ${LIFECYCLE_PHASE_DISPOSE} "${params.stage}" done`);
|
|
127
|
+
}
|
|
@@ -4,10 +4,11 @@ import { buildDisposeOrder, initServiceUnits, serviceUnitName } from './lifecycl
|
|
|
4
4
|
/**
|
|
5
5
|
* 生命周期(N1 §4)。
|
|
6
6
|
*
|
|
7
|
-
* -
|
|
7
|
+
* - **每个阶段一个 `Disposer`**(`ctx.disposer`):创建处注册释放,关闭时按阶段逆序 flush;
|
|
8
|
+
* - **`services` 额外有逐单元生命周期**:按**对象声明序** `init` / **逆序** `dispose`;
|
|
9
|
+
* - services 阶段内部:先 flush 自己的 `disposer`,再逐单元 `dispose`;
|
|
8
10
|
* - **没有 `dependsOn`**(`services` 的 ctx 没有 `services` 字段,编译期禁止互依);
|
|
9
|
-
* - 单元名 =
|
|
10
|
-
* - `middlewares` 按数组序注册,**不参与生命周期**。
|
|
11
|
+
* - 单元名 = **对象键**。
|
|
11
12
|
*/
|
|
12
13
|
describe('init / dispose 顺序:声明序 / 逆序(N1 §4 / §5)', () => {
|
|
13
14
|
it('init:services 按对象声明序', async () => {
|
|
@@ -10,20 +10,21 @@ import { defaultMiddlewares } from './middleware/index.js';
|
|
|
10
10
|
const fakeMiddlewareContext = {
|
|
11
11
|
app: {},
|
|
12
12
|
log: { debug: () => { }, info: () => { }, warn: () => { }, error: () => { } },
|
|
13
|
+
disposer: { add: () => { }, dispose: async () => { } },
|
|
13
14
|
services: {},
|
|
14
15
|
config: {},
|
|
15
16
|
};
|
|
16
17
|
describe('ctx 可见性(N1 §2)', () => {
|
|
17
|
-
it('MiddlewareContext 有 app / log / services / config;没有 resources / env
|
|
18
|
+
it('MiddlewareContext 有 app / log / disposer / services / config;没有 resources / env', () => {
|
|
18
19
|
expectTypeOf().toHaveProperty('app');
|
|
19
20
|
expectTypeOf().toHaveProperty('services');
|
|
20
21
|
expectTypeOf().toHaveProperty('config');
|
|
22
|
+
// `disposer` 是所有槽位的标配(不是 services 专属)
|
|
23
|
+
expectTypeOf().toHaveProperty('disposer');
|
|
21
24
|
// @ts-expect-error 中间件看不到 resources(禁直连数据层)
|
|
22
25
|
void 0..resources;
|
|
23
26
|
// @ts-expect-error 中间件看不到 env
|
|
24
27
|
void 0..env;
|
|
25
|
-
// @ts-expect-error 中间件看不到 disposer(disposer 只属 services)
|
|
26
|
-
void 0..disposer;
|
|
27
28
|
});
|
|
28
29
|
});
|
|
29
30
|
describe('默认预设', () => {
|
package/output/app/redis.d.ts
CHANGED
|
@@ -4,17 +4,21 @@ import type { LoggerLike, RedisHandle } from './types.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* 引导期 Redis / cache helper(N2 §2.13)。
|
|
6
6
|
*
|
|
7
|
-
* 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis
|
|
7
|
+
* 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis);释放**在创建处**登记:
|
|
8
8
|
*
|
|
9
9
|
* ```ts
|
|
10
10
|
* resources: (ctx) => {
|
|
11
11
|
* const redis = createRedis(ctx.env, ctx.log) // 无 REDIS_URL → undefined
|
|
12
|
+
* if (redis) {
|
|
13
|
+
* ctx.disposer.add(() => redis.dispose()) // 关闭时 resources 阶段最后 flush
|
|
14
|
+
* }
|
|
12
15
|
* return { redis, cache: createCache({ env: ctx.env, prefix: 'account', redis }) }
|
|
13
16
|
* }
|
|
14
17
|
* ```
|
|
15
18
|
*
|
|
16
19
|
* `REDIS_URL` 由 helper 直接读 `env`(固定约定),**不**取自 `config`;`redis` / `cache` 一律是
|
|
17
20
|
* **惰性包装**:构造时不建连、不 ping,首次真正使用才建连 —— 「出 spec 不连库/Redis」结构性成立。
|
|
21
|
+
* 释放永远走调用方在 `ctx.disposer` 里的登记,框架不替资源持有句柄。
|
|
18
22
|
*/
|
|
19
23
|
/** 一级/二级缓存默认 TTL(秒),与 `RedisCache` 默认一致 */
|
|
20
24
|
export declare const DEFAULT_REDIS_CACHE_TTL_SECONDS = 300;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../source/app/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEzD
|
|
1
|
+
{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../source/app/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEzD;;;;;;;;;;;;;;;;;;GAkBG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,+BAA+B,MAAM,CAAA;AAElD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,CAmBrF;AAED,uBAAuB;AACvB,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAA;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAYhE"}
|
package/output/app/redis.js
CHANGED
|
@@ -2,17 +2,21 @@ import { createRedisClient, RedisCache } from '../redis-cache/index.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* 引导期 Redis / cache helper(N2 §2.13)。
|
|
4
4
|
*
|
|
5
|
-
* 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis
|
|
5
|
+
* 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis);释放**在创建处**登记:
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
8
|
* resources: (ctx) => {
|
|
9
9
|
* const redis = createRedis(ctx.env, ctx.log) // 无 REDIS_URL → undefined
|
|
10
|
+
* if (redis) {
|
|
11
|
+
* ctx.disposer.add(() => redis.dispose()) // 关闭时 resources 阶段最后 flush
|
|
12
|
+
* }
|
|
10
13
|
* return { redis, cache: createCache({ env: ctx.env, prefix: 'account', redis }) }
|
|
11
14
|
* }
|
|
12
15
|
* ```
|
|
13
16
|
*
|
|
14
17
|
* `REDIS_URL` 由 helper 直接读 `env`(固定约定),**不**取自 `config`;`redis` / `cache` 一律是
|
|
15
18
|
* **惰性包装**:构造时不建连、不 ping,首次真正使用才建连 —— 「出 spec 不连库/Redis」结构性成立。
|
|
19
|
+
* 释放永远走调用方在 `ctx.disposer` 里的登记,框架不替资源持有句柄。
|
|
16
20
|
*/
|
|
17
21
|
/** 一级/二级缓存默认 TTL(秒),与 `RedisCache` 默认一致 */
|
|
18
22
|
export const DEFAULT_REDIS_CACHE_TTL_SECONDS = 300;
|
|
@@ -16,7 +16,7 @@ const assertLiveKeysRemovesFalse = () => {
|
|
|
16
16
|
};
|
|
17
17
|
void assertLiveKeysRemovesFalse;
|
|
18
18
|
describe('resources 槽位(N1 §2 / N2 §2.3)', () => {
|
|
19
|
-
it('① 入参为 env + log + registerClassifyError,且资源嵌套在 ctx.resources 下(config / services 都能用)', async () => {
|
|
19
|
+
it('① 入参为 env + log + disposer + registerClassifyError,且资源嵌套在 ctx.resources 下(config / services 都能用)', async () => {
|
|
20
20
|
let captured;
|
|
21
21
|
const observed = {};
|
|
22
22
|
const server = defineServer({
|
|
@@ -43,9 +43,11 @@ describe('resources 槽位(N1 §2 / N2 §2.3)', () => {
|
|
|
43
43
|
});
|
|
44
44
|
const app = await startServer(server, { listen: false });
|
|
45
45
|
try {
|
|
46
|
-
|
|
46
|
+
// 槽位 ctx:env + log + disposer(+ 专属的 registerClassifyError)
|
|
47
|
+
expect(Object.keys(captured ?? {}).sort()).toEqual(['disposer', 'env', 'log', 'registerClassifyError']);
|
|
47
48
|
expect(captured?.env.ENV).toBeTypeOf('string');
|
|
48
49
|
expect(typeof captured?.log.info).toBe('function');
|
|
50
|
+
expect(typeof captured?.disposer.add).toBe('function');
|
|
49
51
|
// resources 槽位专属能力:每个资源注册自己的错误分类器(函数)
|
|
50
52
|
expect(typeof captured?.registerClassifyError).toBe('function');
|
|
51
53
|
expect(observed.inConfig).toBe('rows:select 1');
|
|
@@ -143,38 +145,59 @@ describe('resources 槽位(N1 §2 / N2 §2.3)', () => {
|
|
|
143
145
|
await app.close();
|
|
144
146
|
}
|
|
145
147
|
});
|
|
146
|
-
it('⑤
|
|
148
|
+
it('⑤ resources 的 disposer 在 services 阶段**之后**执行;阶段内按逆注册序;资源对象自带的 dispose 不被调用', async () => {
|
|
147
149
|
const calls = [];
|
|
148
150
|
const server = defineServer({
|
|
149
151
|
app: { service: 'resources-lifecycle', title: 'Resources Lifecycle', version: '1.0.0' },
|
|
150
|
-
resources: () =>
|
|
151
|
-
|
|
152
|
+
resources: (ctx) => {
|
|
153
|
+
// 资源对象自带的 `dispose` 不参与生命周期:释放只认 `ctx.disposer.add(...)`
|
|
154
|
+
const database = {
|
|
152
155
|
dispose: () => {
|
|
153
|
-
calls.push('dispose:resource');
|
|
156
|
+
calls.push('dispose:resource-object');
|
|
154
157
|
},
|
|
155
|
-
}
|
|
156
|
-
|
|
158
|
+
};
|
|
159
|
+
ctx.disposer.add(() => {
|
|
160
|
+
calls.push('dispose:resource-a');
|
|
161
|
+
});
|
|
162
|
+
ctx.disposer.add(() => {
|
|
163
|
+
calls.push('dispose:resource-b');
|
|
164
|
+
});
|
|
165
|
+
return { database };
|
|
166
|
+
},
|
|
157
167
|
config: () => ({}),
|
|
158
|
-
services: () =>
|
|
159
|
-
|
|
160
|
-
dispose:
|
|
161
|
-
|
|
168
|
+
services: (ctx) => {
|
|
169
|
+
ctx.disposer.add(() => {
|
|
170
|
+
calls.push('dispose:services-disposer');
|
|
171
|
+
});
|
|
172
|
+
return {
|
|
173
|
+
databaseClient: {
|
|
174
|
+
dispose: () => {
|
|
175
|
+
calls.push('dispose:service-unit');
|
|
176
|
+
},
|
|
162
177
|
},
|
|
163
|
-
}
|
|
164
|
-
}
|
|
178
|
+
};
|
|
179
|
+
},
|
|
165
180
|
middlewares: () => [],
|
|
166
181
|
routers: () => undefined,
|
|
167
182
|
});
|
|
168
183
|
const app = await startServer(server, { listen: false });
|
|
169
184
|
await app.close();
|
|
170
|
-
expect(calls).toEqual([
|
|
185
|
+
expect(calls).toEqual([
|
|
186
|
+
'dispose:services-disposer',
|
|
187
|
+
'dispose:service-unit',
|
|
188
|
+
'dispose:resource-b',
|
|
189
|
+
'dispose:resource-a',
|
|
190
|
+
]);
|
|
171
191
|
});
|
|
172
|
-
it('⑥ 出 spec 路径同样跑 resources
|
|
173
|
-
|
|
192
|
+
it('⑥ 出 spec 路径同样跑 resources(只构造,不建连),但不 flush 任何 disposer', async () => {
|
|
193
|
+
const calls = [];
|
|
174
194
|
const server = defineServer({
|
|
175
195
|
app: { service: 'resources-openapi', title: 'Resources OpenApi', version: '1.0.0' },
|
|
176
|
-
resources: () => {
|
|
177
|
-
calls
|
|
196
|
+
resources: (ctx) => {
|
|
197
|
+
calls.push('resources');
|
|
198
|
+
ctx.disposer.add(() => {
|
|
199
|
+
calls.push('dispose:resources');
|
|
200
|
+
});
|
|
178
201
|
return {};
|
|
179
202
|
},
|
|
180
203
|
config: () => ({}),
|
|
@@ -183,7 +206,7 @@ describe('resources 槽位(N1 §2 / N2 §2.3)', () => {
|
|
|
183
206
|
routers: () => undefined,
|
|
184
207
|
});
|
|
185
208
|
await generateOpenApi(server);
|
|
186
|
-
expect(calls).
|
|
209
|
+
expect(calls).toEqual(['resources']);
|
|
187
210
|
});
|
|
188
211
|
});
|
|
189
212
|
/** `ResourcesView` 自证:`Awaited<>` 归一(同步 / 异步两写法等价) */
|
package/output/app/shutdown.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 固定关闭步(N1 §5)——**顺序写死在数组里**。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `disposer` 是所有槽位 ctx 的标配:每个阶段一个 `Disposer`,在**创建处**注册释放。
|
|
5
|
+
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → config → resources`
|
|
6
|
+
* (后创建的先销毁;Prisma / Redis 这类资源**最后**才断)。
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* `services` 阶段内部保持既有语义:先 flush 自己的 `disposer`(原 `stopProducers`),
|
|
9
|
+
* 再 `disposeServices`(单元**逆序** `dispose`)。配置失效通道已删除,故没有 `unsubscribeConfig` 步。
|
|
10
|
+
*
|
|
11
|
+
* 固定六步:`flushRouters` → `flushMiddlewares` → `flushServices` → `disposeServices`
|
|
12
|
+
* → `flushConfig` → `flushResources`。
|
|
8
13
|
*
|
|
9
14
|
* 每步失败记日志、不阻断后续,最后聚合上报。
|
|
10
15
|
*/
|
|
11
|
-
export declare const SHUTDOWN_ORDER: readonly ["
|
|
16
|
+
export declare const SHUTDOWN_ORDER: readonly ["flushRouters", "flushMiddlewares", "flushServices", "disposeServices", "flushConfig", "flushResources"];
|
|
12
17
|
export type ShutdownStepName = (typeof SHUTDOWN_ORDER)[number];
|
|
13
18
|
export type ShutdownHandles = Record<ShutdownStepName, () => void | Promise<void>>;
|
|
14
19
|
export interface ShutdownLogger {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../source/app/shutdown.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../source/app/shutdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,oHAOjB,CAAA;AAEV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,gBAAgB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;AAElF,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAe9F"}
|
package/output/app/shutdown.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 固定关闭步(N1 §5)——**顺序写死在数组里**。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* `disposer` 是所有槽位 ctx 的标配:每个阶段一个 `Disposer`,在**创建处**注册释放。
|
|
5
|
+
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → config → resources`
|
|
6
|
+
* (后创建的先销毁;Prisma / Redis 这类资源**最后**才断)。
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* `services` 阶段内部保持既有语义:先 flush 自己的 `disposer`(原 `stopProducers`),
|
|
9
|
+
* 再 `disposeServices`(单元**逆序** `dispose`)。配置失效通道已删除,故没有 `unsubscribeConfig` 步。
|
|
10
|
+
*
|
|
11
|
+
* 固定六步:`flushRouters` → `flushMiddlewares` → `flushServices` → `disposeServices`
|
|
12
|
+
* → `flushConfig` → `flushResources`。
|
|
8
13
|
*
|
|
9
14
|
* 每步失败记日志、不阻断后续,最后聚合上报。
|
|
10
15
|
*/
|
|
11
|
-
export const SHUTDOWN_ORDER = [
|
|
16
|
+
export const SHUTDOWN_ORDER = [
|
|
17
|
+
'flushRouters',
|
|
18
|
+
'flushMiddlewares',
|
|
19
|
+
'flushServices',
|
|
20
|
+
'disposeServices',
|
|
21
|
+
'flushConfig',
|
|
22
|
+
'flushResources',
|
|
23
|
+
];
|
|
12
24
|
/**
|
|
13
25
|
* 顺序执行关闭步骤;单步抛错记录后**继续**后续步骤,最后聚合上报
|
|
14
26
|
* (避免「一步报错导致后面资源没关」)。
|
|
@@ -13,13 +13,24 @@ function createRecordingHandles(record, throwOn) {
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
return {
|
|
16
|
-
|
|
16
|
+
flushRouters: step('flushRouters'),
|
|
17
|
+
flushMiddlewares: step('flushMiddlewares'),
|
|
18
|
+
flushServices: step('flushServices'),
|
|
17
19
|
disposeServices: step('disposeServices'),
|
|
20
|
+
flushConfig: step('flushConfig'),
|
|
21
|
+
flushResources: step('flushResources'),
|
|
18
22
|
};
|
|
19
23
|
}
|
|
20
24
|
describe('SHUTDOWN_ORDER', () => {
|
|
21
|
-
it('
|
|
22
|
-
expect(SHUTDOWN_ORDER).toEqual([
|
|
25
|
+
it('固定六步且顺序不可变:阶段逆序 flush,services 内部先 flush 再逐单元 dispose', () => {
|
|
26
|
+
expect(SHUTDOWN_ORDER).toEqual([
|
|
27
|
+
'flushRouters',
|
|
28
|
+
'flushMiddlewares',
|
|
29
|
+
'flushServices',
|
|
30
|
+
'disposeServices',
|
|
31
|
+
'flushConfig',
|
|
32
|
+
'flushResources',
|
|
33
|
+
]);
|
|
23
34
|
});
|
|
24
35
|
});
|
|
25
36
|
describe('runShutdown', () => {
|
|
@@ -32,7 +43,7 @@ describe('runShutdown', () => {
|
|
|
32
43
|
it('单步抛错不阻断后续,并聚合上报', async () => {
|
|
33
44
|
const record = [];
|
|
34
45
|
const { entries, log } = createShutdownLog();
|
|
35
|
-
await runShutdown(createRecordingHandles(record, '
|
|
46
|
+
await runShutdown(createRecordingHandles(record, 'flushRouters'), log);
|
|
36
47
|
expect(record).toEqual([...SHUTDOWN_ORDER]);
|
|
37
48
|
const aggregate = entries.find((entry) => entry.msg === '[shutdown] completed with errors');
|
|
38
49
|
expect(aggregate).toBeDefined();
|
|
@@ -42,15 +53,25 @@ describe('runShutdown', () => {
|
|
|
42
53
|
});
|
|
43
54
|
});
|
|
44
55
|
describe('关闭流程集成', () => {
|
|
45
|
-
it('
|
|
56
|
+
it('各阶段 disposer 按阶段逆序执行:routers → middlewares → services → config → resources', async () => {
|
|
46
57
|
const calls = [];
|
|
47
58
|
const server = defineServer({
|
|
48
|
-
app: { service: 'shutdown-
|
|
49
|
-
resources: () =>
|
|
50
|
-
|
|
59
|
+
app: { service: 'shutdown-stages', title: 'Shutdown Stages', version: '1.0.0' },
|
|
60
|
+
resources: (ctx) => {
|
|
61
|
+
ctx.disposer.add(() => {
|
|
62
|
+
calls.push('flushResources');
|
|
63
|
+
});
|
|
64
|
+
return {};
|
|
65
|
+
},
|
|
66
|
+
config: (ctx) => {
|
|
67
|
+
ctx.disposer.add(() => {
|
|
68
|
+
calls.push('flushConfig');
|
|
69
|
+
});
|
|
70
|
+
return {};
|
|
71
|
+
},
|
|
51
72
|
services: (ctx) => {
|
|
52
73
|
ctx.disposer.add(() => {
|
|
53
|
-
calls.push('
|
|
74
|
+
calls.push('flushServices');
|
|
54
75
|
});
|
|
55
76
|
return {
|
|
56
77
|
a: {
|
|
@@ -58,26 +79,111 @@ describe('关闭流程集成', () => {
|
|
|
58
79
|
calls.push('disposeServiceA');
|
|
59
80
|
},
|
|
60
81
|
},
|
|
61
|
-
b: {
|
|
62
|
-
dispose: () => {
|
|
63
|
-
calls.push('disposeServiceB');
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
82
|
};
|
|
67
83
|
},
|
|
84
|
+
middlewares: (ctx) => {
|
|
85
|
+
ctx.disposer.add(() => {
|
|
86
|
+
calls.push('flushMiddlewares');
|
|
87
|
+
});
|
|
88
|
+
return [];
|
|
89
|
+
},
|
|
90
|
+
routers: (ctx) => {
|
|
91
|
+
ctx.disposer.add(() => {
|
|
92
|
+
calls.push('flushRouters');
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const app = await startServer(server, { listen: false });
|
|
97
|
+
await app.close();
|
|
98
|
+
expect(calls).toEqual([
|
|
99
|
+
'flushRouters',
|
|
100
|
+
'flushMiddlewares',
|
|
101
|
+
'flushServices',
|
|
102
|
+
'disposeServiceA',
|
|
103
|
+
'flushConfig',
|
|
104
|
+
'flushResources',
|
|
105
|
+
]);
|
|
106
|
+
});
|
|
107
|
+
it('阶段内多个 disposer 按**逆注册序**(LIFO)执行', async () => {
|
|
108
|
+
const calls = [];
|
|
109
|
+
const server = defineServer({
|
|
110
|
+
app: { service: 'shutdown-lifo', title: 'Shutdown LIFO', version: '1.0.0' },
|
|
111
|
+
resources: (ctx) => {
|
|
112
|
+
// 先建 A,再建 B(B 可能依赖 A)→ 关闭时先 B 后 A
|
|
113
|
+
ctx.disposer.add(() => {
|
|
114
|
+
calls.push('disposeA');
|
|
115
|
+
});
|
|
116
|
+
ctx.disposer.add(() => {
|
|
117
|
+
calls.push('disposeB');
|
|
118
|
+
});
|
|
119
|
+
return {};
|
|
120
|
+
},
|
|
121
|
+
config: () => ({}),
|
|
122
|
+
services: () => ({}),
|
|
68
123
|
middlewares: () => [],
|
|
69
124
|
routers: () => undefined,
|
|
70
125
|
});
|
|
71
126
|
const app = await startServer(server, { listen: false });
|
|
72
127
|
await app.close();
|
|
73
|
-
|
|
74
|
-
|
|
128
|
+
expect(calls).toEqual(['disposeB', 'disposeA']);
|
|
129
|
+
});
|
|
130
|
+
it('某个 disposer 抛错不影响同阶段其它 disposer(且 `close()` 不抛)', async () => {
|
|
131
|
+
const calls = [];
|
|
132
|
+
const server = defineServer({
|
|
133
|
+
app: { service: 'shutdown-disposer-error', title: 'Shutdown Disposer Error', version: '1.0.0' },
|
|
134
|
+
resources: (ctx) => {
|
|
135
|
+
ctx.disposer.add(() => {
|
|
136
|
+
calls.push('disposeA');
|
|
137
|
+
});
|
|
138
|
+
ctx.disposer.add(() => {
|
|
139
|
+
calls.push('disposeB:throw');
|
|
140
|
+
throw new Error('disposer boom');
|
|
141
|
+
});
|
|
142
|
+
ctx.disposer.add(() => {
|
|
143
|
+
calls.push('disposeC');
|
|
144
|
+
});
|
|
145
|
+
return {};
|
|
146
|
+
},
|
|
147
|
+
config: () => ({}),
|
|
148
|
+
services: () => ({}),
|
|
149
|
+
middlewares: () => [],
|
|
150
|
+
routers: () => undefined,
|
|
151
|
+
});
|
|
152
|
+
const app = await startServer(server, { listen: false });
|
|
153
|
+
await expect(app.close()).resolves.toBeUndefined();
|
|
154
|
+
// LIFO:C → B(抛错)→ A;B 的错误不阻断 A
|
|
155
|
+
expect(calls).toEqual(['disposeC', 'disposeB:throw', 'disposeA']);
|
|
156
|
+
});
|
|
157
|
+
it('未注册任何 disposer 时无操作:关闭照常完成', async () => {
|
|
158
|
+
const calls = [];
|
|
159
|
+
const server = defineServer({
|
|
160
|
+
app: { service: 'shutdown-empty', title: 'Shutdown Empty', version: '1.0.0' },
|
|
161
|
+
resources: () => ({}),
|
|
162
|
+
config: () => ({}),
|
|
163
|
+
services: () => ({
|
|
164
|
+
a: {
|
|
165
|
+
dispose: () => {
|
|
166
|
+
calls.push('disposeServiceA');
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}),
|
|
170
|
+
middlewares: () => [],
|
|
171
|
+
routers: () => undefined,
|
|
172
|
+
});
|
|
173
|
+
const app = await startServer(server, { listen: false });
|
|
174
|
+
await expect(app.close()).resolves.toBeUndefined();
|
|
175
|
+
expect(calls).toEqual(['disposeServiceA']);
|
|
75
176
|
});
|
|
76
177
|
it('StartServerOptions.shutdown 整体覆盖默认关闭流程', async () => {
|
|
77
178
|
const calls = [];
|
|
78
179
|
const server = defineServer({
|
|
79
180
|
app: { service: 'shutdown-override', title: 'Shutdown Override', version: '1.0.0' },
|
|
80
|
-
resources: () =>
|
|
181
|
+
resources: (ctx) => {
|
|
182
|
+
ctx.disposer.add(() => {
|
|
183
|
+
calls.push('flushResources');
|
|
184
|
+
});
|
|
185
|
+
return {};
|
|
186
|
+
},
|
|
81
187
|
config: () => ({}),
|
|
82
188
|
services: () => ({
|
|
83
189
|
a: {
|
|
@@ -98,6 +204,7 @@ describe('关闭流程集成', () => {
|
|
|
98
204
|
},
|
|
99
205
|
});
|
|
100
206
|
await app.close();
|
|
207
|
+
// 整体覆盖:默认六步(含各阶段 flush)都不执行
|
|
101
208
|
expect(calls).toEqual(['custom']);
|
|
102
209
|
});
|
|
103
210
|
});
|
package/output/app/types.d.ts
CHANGED
|
@@ -21,8 +21,16 @@ import type { SystemError } from './errors.js';
|
|
|
21
21
|
* 槽位只能引用**排在它前面**的产物(向下引用是编译错误);`env` 只属引导期
|
|
22
22
|
* (`resources` / `config` 可见,运行期三层一律看不到)。
|
|
23
23
|
*
|
|
24
|
+
* **`disposer` 是每个槽位 ctx 的标配**(不是某个槽位专属):在**创建某个东西的地方**当场
|
|
25
|
+
* `ctx.disposer.add(() => 释放它)`,创建与释放同处一地;`disposer` 是**出口**(不产出任何依赖),
|
|
26
|
+
* 因此不会造成循环,也不削弱「只给该阶段该看的字段」的约束。
|
|
27
|
+
*
|
|
28
|
+
* 生命周期:每个阶段一个独立的 `Disposer` 实例;关闭时**按阶段逆序 flush**
|
|
29
|
+
* `routers → middlewares → services → config → resources`(services 阶段内部:先 flush
|
|
30
|
+
* disposer,再逐单元逆序 `dispose`)。详见 `lifecycle.ts` / `shutdown.ts`。
|
|
31
|
+
*
|
|
24
32
|
* 类型安全机制(N2 §1):
|
|
25
|
-
* - **M1 分阶段 ctx
|
|
33
|
+
* - **M1 分阶段 ctx**:每个工厂只拿到「该阶段该看的字段」(**外加每个阶段都有的 `disposer`**);
|
|
26
34
|
* - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) => R`(不是 `R | Promise<R>`),下游一律
|
|
27
35
|
* `Awaited<R>` / `Awaited<C>` / `Awaited<S>`;
|
|
28
36
|
* - **M3 资源嵌套**:资源包挂在 `ctx.resources` 上(不再摊平到 ctx,键冲突从根上消失);
|
|
@@ -91,13 +99,21 @@ export type ResourcesView<R> = Live<Awaited<R>>;
|
|
|
91
99
|
*/
|
|
92
100
|
export type ErrorClassifier = (error: unknown) => SystemError | null;
|
|
93
101
|
/**
|
|
94
|
-
* `resources(ctx)`
|
|
102
|
+
* `resources(ctx)` 的入参:**`env` + `log` + `disposer`**(外加槽位专属的注册能力)。
|
|
95
103
|
*
|
|
96
104
|
* 结构上排除 `config` / `services` / `app` → 「资源依赖配置」这类循环**编译期写不出来**。
|
|
105
|
+
* `disposer` 是**出口**(只能注册释放函数,不产出依赖),因此不构成新的循环面:
|
|
106
|
+
* 资源在构造处当场 `ctx.disposer.add(() => 释放它)`,关闭时框架最后 flush。
|
|
97
107
|
*/
|
|
98
108
|
export interface ResourcesContext {
|
|
99
109
|
readonly env: ConfigEnv;
|
|
100
110
|
readonly log: LoggerLike;
|
|
111
|
+
/**
|
|
112
|
+
* 本阶段的释放器:**在创建资源的地方当场注册释放**。
|
|
113
|
+
*
|
|
114
|
+
* 关闭序中 `resources` 阶段**最后** flush(所有其它阶段都还活着)。
|
|
115
|
+
*/
|
|
116
|
+
readonly disposer: Disposer;
|
|
101
117
|
/**
|
|
102
118
|
* **每个资源注册自己的错误分类器**(响应层用)。
|
|
103
119
|
*
|
|
@@ -109,13 +125,15 @@ export interface ResourcesContext {
|
|
|
109
125
|
}
|
|
110
126
|
export type ResourcesFactory<R> = (ctx: ResourcesContext) => R;
|
|
111
127
|
/**
|
|
112
|
-
* `config(ctx)` 的入参:`env` + `log` + 资源视图。
|
|
128
|
+
* `config(ctx)` 的入参:`env` + `log` + `disposer` + 资源视图。
|
|
113
129
|
*
|
|
114
130
|
* **没有** `services`(此时尚未构造)/ `app`(此时尚未装配)。
|
|
115
131
|
*/
|
|
116
132
|
export interface ConfigContext<R> {
|
|
117
133
|
readonly env: ConfigEnv;
|
|
118
134
|
readonly log: LoggerLike;
|
|
135
|
+
/** 本阶段的释放器(关闭时在 services 之后、resources 之前 flush) */
|
|
136
|
+
readonly disposer: Disposer;
|
|
119
137
|
readonly resources: ResourcesView<R>;
|
|
120
138
|
}
|
|
121
139
|
export type ConfigFactory<R, C> = (ctx: ConfigContext<R>) => C;
|
|
@@ -136,6 +154,7 @@ export interface ServiceUnit {
|
|
|
136
154
|
*/
|
|
137
155
|
export interface ServiceContext<R, C> {
|
|
138
156
|
readonly log: LoggerLike;
|
|
157
|
+
/** 本阶段的释放器(关闭时**先** flush 它,再逐单元逆序 `dispose`) */
|
|
139
158
|
readonly disposer: Disposer;
|
|
140
159
|
readonly resources: ResourcesView<R>;
|
|
141
160
|
readonly config: Awaited<C>;
|
|
@@ -151,25 +170,29 @@ export interface MiddlewareUnit {
|
|
|
151
170
|
register(app: AppInstance): void | Promise<void>;
|
|
152
171
|
}
|
|
153
172
|
/**
|
|
154
|
-
* `middlewares(ctx)` 的入参:`app` + `log` + `services` + `config`。
|
|
173
|
+
* `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config`。
|
|
155
174
|
*
|
|
156
175
|
* ❗**故意没有 `resources` / `env`** —— 中间件要访问数据层必须穿过 service。
|
|
157
176
|
*/
|
|
158
177
|
export interface MiddlewareContext<C, S> {
|
|
159
178
|
readonly app: AppInstance;
|
|
160
179
|
readonly log: LoggerLike;
|
|
180
|
+
/** 本阶段的释放器(关闭时最早 flush:在 services / config / resources 之前) */
|
|
181
|
+
readonly disposer: Disposer;
|
|
161
182
|
readonly services: Awaited<S>;
|
|
162
183
|
readonly config: Awaited<C>;
|
|
163
184
|
}
|
|
164
185
|
export type MiddlewaresFactory<C, S> = (ctx: MiddlewareContext<C, S>) => MiddlewareUnit[];
|
|
165
186
|
/**
|
|
166
|
-
* `routers(ctx)` 的入参:`app` + `log` + `services`。
|
|
187
|
+
* `routers(ctx)` 的入参:`app` + `log` + `disposer` + `services`。
|
|
167
188
|
*
|
|
168
189
|
* ❗**故意没有 `resources` / `env` / `config`** —— 路由**只能穿过 service**,绕不过层直连数据层。
|
|
169
190
|
*/
|
|
170
191
|
export interface RouterContext<S> {
|
|
171
192
|
readonly app: AppInstance;
|
|
172
193
|
readonly log: LoggerLike;
|
|
194
|
+
/** 本阶段的释放器(关闭时**最先** flush) */
|
|
195
|
+
readonly disposer: Disposer;
|
|
173
196
|
readonly services: Awaited<S>;
|
|
174
197
|
}
|
|
175
198
|
export type RoutersFactory<S> = (ctx: RouterContext<S>) => void | Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/app/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAE9C
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/app/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taicode/common-server",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.3",
|
|
4
4
|
"author": "Alain",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"description": "",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"fastify": "^5.5.0",
|
|
37
37
|
"redis": "^5.11.0",
|
|
38
|
-
"@taicode/common-base": "^4.0.0-beta.
|
|
38
|
+
"@taicode/common-base": "^4.0.0-beta.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@eslint/js": "^9.39.3",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"redis": "^5.11.0",
|
|
50
50
|
"typescript-eslint": "^8.56.1",
|
|
51
51
|
"vitest": "^3.2.4",
|
|
52
|
-
"@taicode/common-base": "^4.0.0-beta.
|
|
52
|
+
"@taicode/common-base": "^4.0.0-beta.3"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"test": "vitest run",
|