@taicode/common-server 4.0.0-beta.4 → 4.0.0-beta.6
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/LICENSE +21 -0
- package/output/app/index.d.ts +2 -1
- package/output/app/index.d.ts.map +1 -1
- package/output/app/index.js +47 -14
- package/output/auth/index.d.ts +3 -3
- package/output/auth/index.d.ts.map +1 -1
- package/output/auth/index.js +3 -2
- package/output/core/boot/context.d.ts +54 -34
- package/output/core/boot/context.d.ts.map +1 -1
- package/output/core/boot/context.js +42 -4
- package/output/core/boot/lifecycle.d.ts +39 -7
- package/output/core/boot/lifecycle.d.ts.map +1 -1
- package/output/core/boot/lifecycle.js +59 -19
- package/output/core/boot/shutdown.d.ts +9 -5
- package/output/core/boot/shutdown.d.ts.map +1 -1
- package/output/core/boot/shutdown.js +9 -4
- package/output/core/boot/signal.d.ts +1 -1
- package/output/core/boot/signal.js +1 -1
- package/output/core/boot/steps.d.ts +60 -29
- package/output/core/boot/steps.d.ts.map +1 -1
- package/output/core/boot/steps.js +72 -13
- package/output/core/entries.d.ts +49 -22
- package/output/core/entries.d.ts.map +1 -1
- package/output/core/entries.js +64 -30
- package/output/core/entries.test.js +251 -10
- package/output/core/env.d.ts +21 -0
- package/output/core/env.d.ts.map +1 -1
- package/output/core/env.js +28 -0
- package/output/core/resource.d.ts +56 -0
- package/output/core/resource.d.ts.map +1 -0
- package/output/core/resource.js +56 -0
- package/output/core/test-support/database.d.ts +2 -9
- package/output/core/test-support/database.d.ts.map +1 -1
- package/output/core/test-support/database.js +3 -16
- package/output/core/test-support/database.test.js +2 -1
- package/output/core/test-support/environment.d.ts +8 -3
- package/output/core/test-support/environment.d.ts.map +1 -1
- package/output/core/test-support/environment.js +10 -7
- package/output/core/test-support/test-server.d.ts +49 -19
- package/output/core/test-support/test-server.d.ts.map +1 -1
- package/output/core/test-support/test-server.js +22 -6
- package/output/core/test-support/test-server.test.js +7 -5
- package/output/core/test-support/testdata.d.ts +148 -0
- package/output/core/test-support/testdata.d.ts.map +1 -0
- package/output/core/test-support/testdata.js +202 -0
- package/output/core/test-support/testdata.test.d.ts +2 -0
- package/output/core/test-support/testdata.test.d.ts.map +1 -0
- package/output/core/test-support/testdata.test.js +220 -0
- package/output/core/types.d.ts +235 -71
- package/output/core/types.d.ts.map +1 -1
- package/output/cors/index.d.ts +2 -2
- package/output/cors/index.d.ts.map +1 -1
- package/output/cors/index.js +2 -1
- package/output/rate-limit/index.d.ts +2 -2
- package/output/rate-limit/index.d.ts.map +1 -1
- package/output/rate-limit/index.js +2 -1
- package/package.json +14 -15
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - `init` 按**对象声明序**,`dispose` **逆序**;
|
|
16
16
|
* - services 阶段内部**先 flush `ctx.disposer`**(构造期注册的清理),再逐单元 `dispose`。
|
|
17
17
|
*
|
|
18
|
-
* `middlewares`
|
|
18
|
+
* `middlewares` 按**数组顺序**注册;它只有 `disposer`,没有逐单元生命周期。
|
|
19
19
|
*/
|
|
20
20
|
export const RESOURCES_STAGE = 'resources';
|
|
21
21
|
export const CONFIG_STAGE = 'config';
|
|
@@ -23,27 +23,20 @@ export const CLIENTS_STAGE = 'clients';
|
|
|
23
23
|
export const SERVICES_STAGE = 'services';
|
|
24
24
|
export const MIDDLEWARES_STAGE = 'middlewares';
|
|
25
25
|
export const ROUTERS_STAGE = 'routers';
|
|
26
|
-
/** `middlewares` 的**注册期日志**阶段标签(与生命周期阶段同名) */
|
|
27
|
-
export const MIDDLEWARES_LOG_STAGE = MIDDLEWARES_STAGE;
|
|
28
26
|
/** 生命周期相位(逐单元日志字段 `phase`) */
|
|
27
|
+
export const LIFECYCLE_PHASE_SETUP = 'setup';
|
|
29
28
|
export const LIFECYCLE_PHASE_INIT = 'init';
|
|
30
29
|
export const LIFECYCLE_PHASE_DISPOSE = 'dispose';
|
|
31
30
|
function isObject(value) {
|
|
32
31
|
return typeof value === 'object' && value !== null;
|
|
33
32
|
}
|
|
34
33
|
/**
|
|
35
|
-
* `
|
|
36
|
-
*/
|
|
37
|
-
function serviceUnitName(key) {
|
|
38
|
-
return key;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* `middlewares` 日志名字:**对象键即单元名**(与 `services` 同口径)。
|
|
34
|
+
* `middlewares` 日志名字:**数组项的名字**(`MiddlewareUnit.name`,或裸函数的占位名 `middleware[i]`)。
|
|
42
35
|
*
|
|
43
|
-
*
|
|
36
|
+
* 名字为空串时退路 `<service>:middleware`(仅用于注册期日志,不影响注册序——顺序由数组位置决定)。
|
|
44
37
|
*/
|
|
45
|
-
export function middlewareUnitName(service,
|
|
46
|
-
return
|
|
38
|
+
export function middlewareUnitName(service, name) {
|
|
39
|
+
return name.length > 0 ? name : `${service}:middleware`;
|
|
47
40
|
}
|
|
48
41
|
async function callInit(unit) {
|
|
49
42
|
if (isObject(unit) && typeof unit.init === 'function') {
|
|
@@ -82,18 +75,65 @@ async function runLifecycleUnit(params, unit, value, phase) {
|
|
|
82
75
|
*/
|
|
83
76
|
export async function initServiceUnits(params) {
|
|
84
77
|
for (const key of Object.keys(params.services ?? {})) {
|
|
85
|
-
await runLifecycleUnit(params,
|
|
78
|
+
await runLifecycleUnit(params, key, params.services?.[key], LIFECYCLE_PHASE_INIT);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** 跑一个资源单元生命周期步骤(统一日志,失败冒泡给调用方决定) */
|
|
82
|
+
async function runResourceUnit(params, unit, phase) {
|
|
83
|
+
const step = phase === LIFECYCLE_PHASE_SETUP ? unit.setup : unit.dispose;
|
|
84
|
+
if (!step) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const fields = { service: params.service ?? '', unit: unit.name, stage: RESOURCES_STAGE, phase };
|
|
88
|
+
params.log?.debug(fields, `[lifecycle] ${phase} "${unit.name}" start`);
|
|
89
|
+
try {
|
|
90
|
+
await step();
|
|
91
|
+
params.log?.debug(fields, `[lifecycle] ${phase} "${unit.name}" done`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
params.log?.error({ ...fields, error }, `[lifecycle] ${phase} "${unit.name}" failed`);
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* `resources` 的装配期准备:按**对象声明序**逐项 `await setup()`;逐项打日志。
|
|
100
|
+
*
|
|
101
|
+
* 与 `initServiceUnits` 同形,差别在单元来源:资源值往往是第三方对象(没有 `setup` 方法),
|
|
102
|
+
* 单元外壳由 `buildResources` 在解包时收集成 `{ name, setup, dispose }`。
|
|
103
|
+
*
|
|
104
|
+
* **失败即中断**:资源准备失败时后续资源、`config`、`services` 都没有意义,直接冒泡给入口,
|
|
105
|
+
* 由入口把已构造的资源释放掉(与「端口占用」同一处理)。不放进关闭流程聚合——那不是关闭失败。
|
|
106
|
+
*/
|
|
107
|
+
export async function setupResourceUnits(params) {
|
|
108
|
+
for (const unit of params.units ?? []) {
|
|
109
|
+
await runResourceUnit(params, unit, LIFECYCLE_PHASE_SETUP);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `resources` 的关闭:按声明序**逆序**逐项 `await dispose()`。
|
|
114
|
+
*
|
|
115
|
+
* 与 `disposeServices` 同语义:单元级失败已 `log.error`,此处吞掉以保证单个资源不阻断其他资源。
|
|
116
|
+
*
|
|
117
|
+
* 在关闭流程中排在 `flushResources`(阶段 `Disposer`)**之前**:单元 `dispose` 是资源自己的
|
|
118
|
+
* 正事(断连),而 `disposer` 是本阶段临时产物的逃生口,后者不应挡住前者。
|
|
119
|
+
*/
|
|
120
|
+
export async function disposeResourceUnits(params) {
|
|
121
|
+
for (const unit of [...(params.units ?? [])].reverse()) {
|
|
122
|
+
try {
|
|
123
|
+
await runResourceUnit(params, unit, LIFECYCLE_PHASE_DISPOSE);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// 单元级已 log.error;此处仅保证不阻断其他资源
|
|
127
|
+
}
|
|
86
128
|
}
|
|
87
129
|
}
|
|
88
130
|
/**
|
|
89
131
|
* 关闭序:`services` 按声明序**逆序**逐项 `dispose?.()`。
|
|
90
132
|
*/
|
|
91
133
|
function buildDisposeOrder(params) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
return steps;
|
|
134
|
+
return Object.keys(params.services ?? {})
|
|
135
|
+
.reverse()
|
|
136
|
+
.map((key) => () => runLifecycleUnit(params, key, params.services?.[key], LIFECYCLE_PHASE_DISPOSE));
|
|
97
137
|
}
|
|
98
138
|
/**
|
|
99
139
|
* 关闭步 `disposeServices`:`services` 声明序逆序释放。
|
|
@@ -5,17 +5,21 @@
|
|
|
5
5
|
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → clients → config → resources`
|
|
6
6
|
* (后创建的先销毁;Prisma / Redis 这类资源**最后**才断)。
|
|
7
7
|
*
|
|
8
|
-
* `services`
|
|
9
|
-
*
|
|
8
|
+
* `services` / `resources` 阶段内部保持同一套语义:先 flush 自己的 `disposer`(构造期注册的
|
|
9
|
+
* 临时产物),再逐单元 `dispose`(声明序**逆序**)。
|
|
10
|
+
*
|
|
11
|
+
* ❗`resources` 阶段**顺序与 `services` 相反**:先 `disposeResources`(单元自己的连接)再
|
|
12
|
+
* `flushResources`(`ctx.disposer` 里的逃生口清理)。理由:单元 `dispose` 是「资源自身怎么关」
|
|
13
|
+
* 的正事,`disposer` 是临时产物;临时产物可能在 `dispose` 过程中被用到,不能先拆。
|
|
10
14
|
*
|
|
11
15
|
* `clients` 排在 `services` **之后**:服务单元 `dispose()` 时可能还要落一次外部调用。
|
|
12
16
|
*
|
|
13
|
-
*
|
|
14
|
-
* → `flushClients` → `flushConfig` → `flushResources`。
|
|
17
|
+
* 固定八步:`flushRouters` → `flushMiddlewares` → `flushServices` → `disposeServices`
|
|
18
|
+
* → `flushClients` → `flushConfig` → `disposeResources` → `flushResources`。
|
|
15
19
|
*
|
|
16
20
|
* 每步失败记日志、不阻断后续,最后聚合上报。
|
|
17
21
|
*/
|
|
18
|
-
declare const SHUTDOWN_ORDER: readonly ["flushRouters", "flushMiddlewares", "flushServices", "disposeServices", "flushClients", "flushConfig", "flushResources"];
|
|
22
|
+
declare const SHUTDOWN_ORDER: readonly ["flushRouters", "flushMiddlewares", "flushServices", "disposeServices", "flushClients", "flushConfig", "disposeResources", "flushResources"];
|
|
19
23
|
type ShutdownStepName = (typeof SHUTDOWN_ORDER)[number];
|
|
20
24
|
type ShutdownHandles = Record<ShutdownStepName, () => void | Promise<void>>;
|
|
21
25
|
export interface ShutdownLogger {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../../source/core/boot/shutdown.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../../source/core/boot/shutdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,QAAA,MAAM,cAAc,wJASV,CAAA;AAEV,KAAK,gBAAgB,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvD,KAAK,eAAe,GAAG,MAAM,CAAC,gBAAgB,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;AAE3E,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"}
|
|
@@ -5,13 +5,17 @@
|
|
|
5
5
|
* 关闭时按阶段**逆序 flush**:`routers → middlewares → services → clients → config → resources`
|
|
6
6
|
* (后创建的先销毁;Prisma / Redis 这类资源**最后**才断)。
|
|
7
7
|
*
|
|
8
|
-
* `services`
|
|
9
|
-
*
|
|
8
|
+
* `services` / `resources` 阶段内部保持同一套语义:先 flush 自己的 `disposer`(构造期注册的
|
|
9
|
+
* 临时产物),再逐单元 `dispose`(声明序**逆序**)。
|
|
10
|
+
*
|
|
11
|
+
* ❗`resources` 阶段**顺序与 `services` 相反**:先 `disposeResources`(单元自己的连接)再
|
|
12
|
+
* `flushResources`(`ctx.disposer` 里的逃生口清理)。理由:单元 `dispose` 是「资源自身怎么关」
|
|
13
|
+
* 的正事,`disposer` 是临时产物;临时产物可能在 `dispose` 过程中被用到,不能先拆。
|
|
10
14
|
*
|
|
11
15
|
* `clients` 排在 `services` **之后**:服务单元 `dispose()` 时可能还要落一次外部调用。
|
|
12
16
|
*
|
|
13
|
-
*
|
|
14
|
-
* → `flushClients` → `flushConfig` → `flushResources`。
|
|
17
|
+
* 固定八步:`flushRouters` → `flushMiddlewares` → `flushServices` → `disposeServices`
|
|
18
|
+
* → `flushClients` → `flushConfig` → `disposeResources` → `flushResources`。
|
|
15
19
|
*
|
|
16
20
|
* 每步失败记日志、不阻断后续,最后聚合上报。
|
|
17
21
|
*/
|
|
@@ -22,6 +26,7 @@ const SHUTDOWN_ORDER = [
|
|
|
22
26
|
'disposeServices',
|
|
23
27
|
'flushClients',
|
|
24
28
|
'flushConfig',
|
|
29
|
+
'disposeResources',
|
|
25
30
|
'flushResources',
|
|
26
31
|
];
|
|
27
32
|
/**
|
|
@@ -4,7 +4,7 @@ import type { FastifyInstance } from 'fastify';
|
|
|
4
4
|
* 内置信号处理(设计 §7.1 `activate` 尾部 / §7.2)。
|
|
5
5
|
*
|
|
6
6
|
* `SIGTERM` / `SIGINT` / `uncaughtException` / `unhandledRejection` ⇒ `app.close()`(触发 `onClose`
|
|
7
|
-
* 的关闭
|
|
7
|
+
* 的关闭 7 步)⇒ `process.exit(0/1)`。**只有 `startServer` 装信号处理**(它一定 `listen`);
|
|
8
8
|
* `createTestServer` 不装——它跑在测试进程里,退出由测试框架决定。
|
|
9
9
|
*
|
|
10
10
|
* @returns detach 函数(移除本次注册的监听器);`startServer` 已把它挂到 `onClose` 上
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 内置信号处理(设计 §7.1 `activate` 尾部 / §7.2)。
|
|
3
3
|
*
|
|
4
4
|
* `SIGTERM` / `SIGINT` / `uncaughtException` / `unhandledRejection` ⇒ `app.close()`(触发 `onClose`
|
|
5
|
-
* 的关闭
|
|
5
|
+
* 的关闭 7 步)⇒ `process.exit(0/1)`。**只有 `startServer` 装信号处理**(它一定 `listen`);
|
|
6
6
|
* `createTestServer` 不装——它跑在测试进程里,退出由测试框架决定。
|
|
7
7
|
*
|
|
8
8
|
* @returns detach 函数(移除本次注册的监听器);`startServer` 已把它挂到 `onClose` 上
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { Disposer } from '@taicode/common-base';
|
|
2
2
|
import type { ConfigAccess } from './context.js';
|
|
3
|
+
import type { ResourceSetupUnit } from './lifecycle.js';
|
|
3
4
|
import type { ConfigEnv } from '../env.js';
|
|
4
|
-
import type { AppInstance, ClientsView, ErrorClassifier, LoggerLike, ResourcesView, Server } from '../types.js';
|
|
5
|
+
import type { AppEnv, AppInstance, ClientsView, ErrorClassifier, LoggerLike, ResourcesView, Server } from '../types.js';
|
|
5
6
|
import type { FastifyInstance } from 'fastify';
|
|
6
7
|
/**
|
|
7
8
|
* 装配**步料**(不是装配流程)。
|
|
8
9
|
*
|
|
9
|
-
* `startServer` / `generateOpenApi
|
|
10
|
+
* `startServer` / `generateOpenApi`(`entries.ts`)各自写明自己的步序——
|
|
10
11
|
* 这里只放「一步」的实现,**不含任何 `phase` / `mode` / 跳过集**。哪个入口跑哪几步,
|
|
11
|
-
*
|
|
12
|
+
* 在入口函数里一眼可见,不需要在数据结构和分支之间来回对照。
|
|
12
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* 结算 `ctx.env`:**完全按内置逻辑加载**(dotenv 分层 + 进程注入已由 `loadEnvironment` /
|
|
16
|
+
* `prepareTestDatabase` 做完,结果在 `base` 与 `process.env` 里),这里只把两者合成最终视图。
|
|
17
|
+
*
|
|
18
|
+
* - `base`:框架固定约定值(`ENV` / `API_PORT` / `DATABASE_URL` / ...,已归一化);
|
|
19
|
+
* - 应用声明的扩展字段(`Env`,`defineServer` 的第一个泛型):运行期取**内置加载后的**
|
|
20
|
+
* `process.env[key]` 字符串直通——框架**不提供自定义加载**,也不对扩展字段做映射 / 校验 / 默认值。
|
|
21
|
+
*
|
|
22
|
+
* 返回「`process.env` 直通 + 固定约定覆盖」:固定键以归一化后的 `base` 为准(放最后覆盖),
|
|
23
|
+
* 其余键保持内置加载后的原始字符串。类型上窄化为 `AppEnv<Env>`(只暴露固定键 + 声明的扩展键)。
|
|
24
|
+
*
|
|
25
|
+
* `Env` 由调用方(`startServer` / `createTestServer`)显式传入其自身的 `Env` 泛型。
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveAppEnv<Env>(base: ConfigEnv): AppEnv<Env>;
|
|
13
28
|
/** 每个阶段一个 `Disposer`(创建与释放同处一地;关闭时按阶段逆序 flush) */
|
|
14
29
|
export interface StageDisposers {
|
|
15
30
|
resources: Disposer;
|
|
@@ -20,9 +35,9 @@ export interface StageDisposers {
|
|
|
20
35
|
routers: Disposer;
|
|
21
36
|
}
|
|
22
37
|
/** 一次装配的**运行期上下文**(步料之间传递的、不放公共类型里的东西) */
|
|
23
|
-
export interface BootContext<
|
|
24
|
-
server: Server<
|
|
25
|
-
env:
|
|
38
|
+
export interface BootContext<Resources, Config, Clients, Services, Env> {
|
|
39
|
+
server: Server<Resources, Config, Clients, Services, Env>;
|
|
40
|
+
env: AppEnv<Env>;
|
|
26
41
|
/** 原生 Fastify 实例(对外返回的就是它) */
|
|
27
42
|
app: FastifyInstance;
|
|
28
43
|
/** 同一实例的框架视图(`request.ctx` 等类型增强的载体) */
|
|
@@ -31,11 +46,11 @@ export interface BootContext<R, C, CL, S> {
|
|
|
31
46
|
/** 身份块的 `service`(日志标签用) */
|
|
32
47
|
service: string;
|
|
33
48
|
/** `config` 槽位的**读取能力**(出 spec 路径未产出 → `read()` 抛错) */
|
|
34
|
-
configAccess: ConfigAccess<
|
|
49
|
+
configAccess: ConfigAccess<Config>;
|
|
35
50
|
disposers: StageDisposers;
|
|
36
51
|
}
|
|
37
52
|
/** 建 app + 六个阶段 `Disposer`(两个入口的**第一步**,内容完全相同) */
|
|
38
|
-
export declare function createBootContext<
|
|
53
|
+
export declare function createBootContext<Resources, Config, Clients, Services, Env>(server: Server<Resources, Config, Clients, Services, Env>, env: AppEnv<Env>): BootContext<Resources, Config, Clients, Services, Env>;
|
|
39
54
|
/**
|
|
40
55
|
* 出文档用的上下文:**同一个 `BootContext` 形状,但实例是最小 Fastify**(无 logger /
|
|
41
56
|
* 无 requestId / 无 trustProxy,见 `createDocumentFastify`)。
|
|
@@ -43,29 +58,43 @@ export declare function createBootContext<R, C, CL, S>(server: Server<R, C, CL,
|
|
|
43
58
|
* 形状不变是刻意的——`buildResources` / `registerRouters` 这些单步实现
|
|
44
59
|
* 因此**不需要知道自己在哪个入口跑**,轻量只体现在「实例怎么造」和「跑哪几步」上。
|
|
45
60
|
*/
|
|
46
|
-
export declare function createDocumentContext<
|
|
61
|
+
export declare function createDocumentContext<Resources, Config, Clients, Services, Env>(server: Server<Resources, Config, Clients, Services, Env>, env: AppEnv<Env>): BootContext<Resources, Config, Clients, Services, Env>;
|
|
47
62
|
/**
|
|
48
63
|
* 取**已加载**配置的访问器(框架内置单元消费配置的唯一入口)。
|
|
49
64
|
*
|
|
50
65
|
* 出 spec 路径下 `config` 槽位未产出,读到即抛 `CONFIG_UNAVAILABLE`——
|
|
51
66
|
* 「构造期不得读配置」因此有运行期护栏,而不只是文档约定。
|
|
52
67
|
*/
|
|
53
|
-
export declare function createConfigReader<
|
|
54
|
-
export interface ResourceBuildResult<
|
|
55
|
-
/** 剔除了「编译期静态关闭」(值为 `false
|
|
56
|
-
resources: ResourcesView<
|
|
68
|
+
export declare function createConfigReader<Config>(configAccess: ConfigAccess<Config>): (key: string) => unknown;
|
|
69
|
+
export interface ResourceBuildResult<Resources> {
|
|
70
|
+
/** 剔除了「编译期静态关闭」(值为 `false`)的键,并完成单元解包 */
|
|
71
|
+
resources: ResourcesView<Resources>;
|
|
72
|
+
/** 待执行的资源 `setup`(按声明序;空数组 = 无需准备) */
|
|
73
|
+
units: ResourceSetupUnit[];
|
|
57
74
|
/** 各资源注册的分类器合成结果(零个 = `undefined`) */
|
|
58
75
|
classifyResourceError: ErrorClassifier | undefined;
|
|
59
76
|
}
|
|
60
|
-
/**
|
|
61
|
-
|
|
77
|
+
/**
|
|
78
|
+
* `resources(ctx)`:**只构造、不建连**(仅运行期入口执行)。
|
|
79
|
+
*
|
|
80
|
+
* 只看两件事:调用工厂、拆解单元(视图 + 待执行的 `setup`)。**不执行** `setup` —— 那是下一个
|
|
81
|
+
* 单步 `setupResources`,两个入口各自显式写明(与整个 `steps.ts` 【步料不是流程】的定位一致)。
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildResources<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>): Promise<ResourceBuildResult<Resources>>;
|
|
84
|
+
/**
|
|
85
|
+
* 资源单元 `setup` 步:建连验证 / 同步库结构 / 预热,**在 `config` 之前**。
|
|
86
|
+
*
|
|
87
|
+
* 为什么必须是独立一步而不是 `buildResources` 内部:它是一道**有失败可能的装配动作**,
|
|
88
|
+
* 入口需要能把它跟「构造」分开看(失败时已构造的资源照样要释放)。
|
|
89
|
+
*/
|
|
90
|
+
export declare function setupResources<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, units: readonly ResourceSetupUnit[]): Promise<void>;
|
|
62
91
|
/**
|
|
63
92
|
* core 装配:`identity` → `schema`(+ 路由契约守卫)→ `requestContext` → `cookie` →
|
|
64
93
|
* `response` → `openapi` → `requestId` 回吐。
|
|
65
94
|
*
|
|
66
95
|
* 仅运行期入口执行——它装的是「请求怎么被处理」,构建期没有请求。
|
|
67
96
|
*/
|
|
68
|
-
export declare function registerCoreStep<
|
|
97
|
+
export declare function registerCoreStep<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, classifyResourceError?: ErrorClassifier): Promise<void>;
|
|
69
98
|
/**
|
|
70
99
|
* **文档 core** 装配:`identity` → `schema`(+ 路由契约守卫)→ `openapi`。
|
|
71
100
|
*
|
|
@@ -73,35 +102,37 @@ export declare function registerCoreStep<R, C, CL, S>(ctx: BootContext<R, C, CL,
|
|
|
73
102
|
* `response` / `requestId` 不装:它们影响的是请求怎么被处理,而构建期一条请求都不会发。
|
|
74
103
|
* 守卫仍必须在——文档就是权限目录(§10-26)。
|
|
75
104
|
*/
|
|
76
|
-
export declare function registerDocumentCoreStep<
|
|
105
|
+
export declare function registerDocumentCoreStep<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>): Promise<void>;
|
|
77
106
|
/** `config(ctx)`:**仅运行期**(出 spec 路径不跑) */
|
|
78
|
-
export declare function buildConfig<
|
|
107
|
+
export declare function buildConfig<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, resources: ResourcesView<Resources>): Promise<void>;
|
|
79
108
|
/**
|
|
80
109
|
* `clients(ctx)`:**仅运行期**(需要 `config`,出 spec 路径不跑)。
|
|
81
110
|
*
|
|
82
111
|
* 外部客户端的地址 / 凭证来自业务配置,所以它必须排在 `config` 之后;
|
|
83
112
|
* 又因为多个 service 要共享同一份实例(而 `services` 之间禁止互依),所以它必须排在 `services` 之前。
|
|
113
|
+
*
|
|
114
|
+
* **槽位可选**:应用省略 `clients` 时,这里按「空客户端包」处理(`Clients` 退化为空)。
|
|
84
115
|
*/
|
|
85
|
-
export declare function buildClients<
|
|
116
|
+
export declare function buildClients<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, resources: ResourcesView<Resources>): Promise<ClientsView<Clients>>;
|
|
86
117
|
/** `services(ctx)`:**只构造**(`init()` 由调用方决定跑不跑) */
|
|
87
|
-
export declare function buildServices<
|
|
88
|
-
/** `middlewares(ctx)
|
|
89
|
-
export declare function registerMiddlewares<
|
|
118
|
+
export declare function buildServices<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, resources: ResourcesView<Resources>, clients: ClientsView<Clients>): Promise<Awaited<Services>>;
|
|
119
|
+
/** `middlewares(ctx)`:按**数组顺序**逐个 `register(app)`(两个入口都执行) */
|
|
120
|
+
export declare function registerMiddlewares<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, services: Awaited<Services>, clients: ClientsView<Clients>): Promise<void>;
|
|
90
121
|
/** 框架内置系统路由(`/` / `/health` / `/system/health` / `/system/openapi` / `/system/info`) */
|
|
91
|
-
export declare function registerSystemRoutesStep<
|
|
122
|
+
export declare function registerSystemRoutesStep<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, services: Awaited<Services>): void;
|
|
92
123
|
/** `routers(ctx)`:业务路由(文档与权限目录都从这里来) */
|
|
93
|
-
export declare function registerRouters<
|
|
124
|
+
export declare function registerRouters<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, services: Awaited<Services>, clients: ClientsView<Clients>): Promise<void>;
|
|
94
125
|
/** 系统路由(出文档路径:不构造 `services`) */
|
|
95
|
-
export declare function registerDocumentSystemRoutesStep<
|
|
126
|
+
export declare function registerDocumentSystemRoutesStep<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>): void;
|
|
96
127
|
/** 业务路由(出文档路径:不构造 `services` / `clients`;这是文档内容的**唯一**业务来源) */
|
|
97
|
-
export declare function registerDocumentRoutersStep<
|
|
128
|
+
export declare function registerDocumentRoutersStep<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>): Promise<void>;
|
|
98
129
|
/**
|
|
99
|
-
*
|
|
130
|
+
* 关闭流程(固定八步,顺序写在 `shutdown.ts` 里,不可覆盖)。
|
|
100
131
|
*
|
|
101
132
|
* 由 `onClose` 钩子触发;spec 入口在 `finally` 里 `close()` 同样会走它——
|
|
102
133
|
* 「构造了什么就释放什么」,构建脚本不再留下未释放的资源。
|
|
103
134
|
*/
|
|
104
|
-
export declare function buildShutdownFlow<
|
|
135
|
+
export declare function buildShutdownFlow<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, services: Awaited<Services>, resourceUnits?: readonly ResourceSetupUnit[]): () => Promise<void>;
|
|
105
136
|
/** 把关闭流程挂到 `onClose`(两个入口都挂:谁 `close()` 谁负责释放) */
|
|
106
|
-
export declare function installShutdownHook<
|
|
137
|
+
export declare function installShutdownHook<Resources, Config, Clients, Services, Env>(ctx: BootContext<Resources, Config, Clients, Services, Env>, services: Awaited<Services>, resourceUnits?: readonly ResourceSetupUnit[]): void;
|
|
107
138
|
//# sourceMappingURL=steps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../../source/core/boot/steps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../../source/core/boot/steps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,38 @@
|
|
|
1
1
|
import { Disposer } from '@taicode/common-base';
|
|
2
2
|
import { registerCore, registerDocumentCore, createDocumentFastify, createFastify } from '../assemble.js';
|
|
3
3
|
import { registerSystemRoutes } from '../modules/system-routes/index.js';
|
|
4
|
-
import { createClientsContext, createConfigAccess, createConfigContext, createMiddlewareContext, createResourcesContext, createRouterContext, createServiceContext,
|
|
5
|
-
import { CLIENTS_STAGE, CONFIG_STAGE, disposeServices, disposeStage,
|
|
4
|
+
import { createClientsContext, createConfigAccess, createConfigContext, createMiddlewareContext, createResourcesContext, createRouterContext, createServiceContext, liveResources, provideConfig, readConfigKey, splitResourceUnits, } from './context.js';
|
|
5
|
+
import { CLIENTS_STAGE, CONFIG_STAGE, disposeResourceUnits, disposeServices, disposeStage, MIDDLEWARES_STAGE, middlewareUnitName, RESOURCES_STAGE, ROUTERS_STAGE, SERVICES_STAGE, setupResourceUnits, } from './lifecycle.js';
|
|
6
6
|
import { runShutdown } from './shutdown.js';
|
|
7
|
+
/**
|
|
8
|
+
* 装配**步料**(不是装配流程)。
|
|
9
|
+
*
|
|
10
|
+
* `startServer` / `generateOpenApi`(`entries.ts`)各自写明自己的步序——
|
|
11
|
+
* 这里只放「一步」的实现,**不含任何 `phase` / `mode` / 跳过集**。哪个入口跑哪几步,
|
|
12
|
+
* 在入口函数里一眼可见,不需要在数据结构和分支之间来回对照。
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* 结算 `ctx.env`:**完全按内置逻辑加载**(dotenv 分层 + 进程注入已由 `loadEnvironment` /
|
|
16
|
+
* `prepareTestDatabase` 做完,结果在 `base` 与 `process.env` 里),这里只把两者合成最终视图。
|
|
17
|
+
*
|
|
18
|
+
* - `base`:框架固定约定值(`ENV` / `API_PORT` / `DATABASE_URL` / ...,已归一化);
|
|
19
|
+
* - 应用声明的扩展字段(`Env`,`defineServer` 的第一个泛型):运行期取**内置加载后的**
|
|
20
|
+
* `process.env[key]` 字符串直通——框架**不提供自定义加载**,也不对扩展字段做映射 / 校验 / 默认值。
|
|
21
|
+
*
|
|
22
|
+
* 返回「`process.env` 直通 + 固定约定覆盖」:固定键以归一化后的 `base` 为准(放最后覆盖),
|
|
23
|
+
* 其余键保持内置加载后的原始字符串。类型上窄化为 `AppEnv<Env>`(只暴露固定键 + 声明的扩展键)。
|
|
24
|
+
*
|
|
25
|
+
* `Env` 由调用方(`startServer` / `createTestServer`)显式传入其自身的 `Env` 泛型。
|
|
26
|
+
*/
|
|
27
|
+
export function resolveAppEnv(base) {
|
|
28
|
+
const passthrough = {};
|
|
29
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
30
|
+
if (typeof value === 'string') {
|
|
31
|
+
passthrough[key] = value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return { ...passthrough, ...base };
|
|
35
|
+
}
|
|
7
36
|
/** 建 app + 六个阶段 `Disposer`(两个入口的**第一步**,内容完全相同) */
|
|
8
37
|
export function createBootContext(server, env) {
|
|
9
38
|
return buildContext(server, env, createFastify(env));
|
|
@@ -71,7 +100,12 @@ function combineClassifiers(classifiers) {
|
|
|
71
100
|
return null;
|
|
72
101
|
};
|
|
73
102
|
}
|
|
74
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* `resources(ctx)`:**只构造、不建连**(仅运行期入口执行)。
|
|
105
|
+
*
|
|
106
|
+
* 只看两件事:调用工厂、拆解单元(视图 + 待执行的 `setup`)。**不执行** `setup` —— 那是下一个
|
|
107
|
+
* 单步 `setupResources`,两个入口各自显式写明(与整个 `steps.ts` 【步料不是流程】的定位一致)。
|
|
108
|
+
*/
|
|
75
109
|
export async function buildResources(ctx) {
|
|
76
110
|
const classifiers = [];
|
|
77
111
|
const built = (await ctx.server.resources(createResourcesContext({
|
|
@@ -82,11 +116,22 @@ export async function buildResources(ctx) {
|
|
|
82
116
|
classifiers.push(classifier);
|
|
83
117
|
},
|
|
84
118
|
})));
|
|
119
|
+
const { view, units } = splitResourceUnits(built);
|
|
85
120
|
return {
|
|
86
|
-
resources:
|
|
121
|
+
resources: view,
|
|
122
|
+
units,
|
|
87
123
|
classifyResourceError: combineClassifiers(classifiers),
|
|
88
124
|
};
|
|
89
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* 资源单元 `setup` 步:建连验证 / 同步库结构 / 预热,**在 `config` 之前**。
|
|
128
|
+
*
|
|
129
|
+
* 为什么必须是独立一步而不是 `buildResources` 内部:它是一道**有失败可能的装配动作**,
|
|
130
|
+
* 入口需要能把它跟「构造」分开看(失败时已构造的资源照样要释放)。
|
|
131
|
+
*/
|
|
132
|
+
export async function setupResources(ctx, units) {
|
|
133
|
+
await setupResourceUnits({ service: ctx.service, log: ctx.log, units });
|
|
134
|
+
}
|
|
90
135
|
/**
|
|
91
136
|
* core 装配:`identity` → `schema`(+ 路由契约守卫)→ `requestContext` → `cookie` →
|
|
92
137
|
* `response` → `openapi` → `requestId` 回吐。
|
|
@@ -121,16 +166,28 @@ export async function buildConfig(ctx, resources) {
|
|
|
121
166
|
*
|
|
122
167
|
* 外部客户端的地址 / 凭证来自业务配置,所以它必须排在 `config` 之后;
|
|
123
168
|
* 又因为多个 service 要共享同一份实例(而 `services` 之间禁止互依),所以它必须排在 `services` 之前。
|
|
169
|
+
*
|
|
170
|
+
* **槽位可选**:应用省略 `clients` 时,这里按「空客户端包」处理(`Clients` 退化为空)。
|
|
124
171
|
*/
|
|
125
172
|
export async function buildClients(ctx, resources) {
|
|
173
|
+
if (ctx.server.clients === undefined) {
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
126
176
|
const built = (await ctx.server.clients(createClientsContext({ log: ctx.log, disposer: ctx.disposers.clients, resources, configAccess: ctx.configAccess })));
|
|
127
|
-
return
|
|
177
|
+
return liveResources(built);
|
|
128
178
|
}
|
|
129
179
|
/** `services(ctx)`:**只构造**(`init()` 由调用方决定跑不跑) */
|
|
130
180
|
export async function buildServices(ctx, resources, clients) {
|
|
131
181
|
return ctx.server.services(createServiceContext({ log: ctx.log, disposer: ctx.disposers.services, resources, configAccess: ctx.configAccess, clients }));
|
|
132
182
|
}
|
|
133
|
-
/**
|
|
183
|
+
/** 归一化一个 `MiddlewareEntry`:裸函数补一个用于日志的占位名(`middleware[i]`) */
|
|
184
|
+
function normalizeMiddleware(entry, index) {
|
|
185
|
+
if (typeof entry === 'function') {
|
|
186
|
+
return { name: `middleware[${index}]`, register: entry };
|
|
187
|
+
}
|
|
188
|
+
return { name: entry.name, register: entry.register };
|
|
189
|
+
}
|
|
190
|
+
/** `middlewares(ctx)`:按**数组顺序**逐个 `register(app)`(两个入口都执行) */
|
|
134
191
|
export async function registerMiddlewares(ctx, services, clients) {
|
|
135
192
|
const middlewares = ctx.server.middlewares(createMiddlewareContext({
|
|
136
193
|
app: ctx.instance,
|
|
@@ -140,9 +197,10 @@ export async function registerMiddlewares(ctx, services, clients) {
|
|
|
140
197
|
configAccess: ctx.configAccess,
|
|
141
198
|
clients,
|
|
142
199
|
}));
|
|
143
|
-
for (const [
|
|
144
|
-
|
|
145
|
-
|
|
200
|
+
for (const [index, entry] of middlewares.entries()) {
|
|
201
|
+
const { name, register } = normalizeMiddleware(entry, index);
|
|
202
|
+
await register(ctx.instance);
|
|
203
|
+
ctx.log.debug({ service: ctx.service, unit: middlewareUnitName(ctx.service, name), stage: MIDDLEWARES_STAGE }, '[middleware] registered');
|
|
146
204
|
}
|
|
147
205
|
}
|
|
148
206
|
/** 框架内置系统路由(`/` / `/health` / `/system/health` / `/system/openapi` / `/system/info`) */
|
|
@@ -175,12 +233,12 @@ export async function registerDocumentRoutersStep(ctx) {
|
|
|
175
233
|
await registerRouters(ctx, DOCUMENT_UNITS, DOCUMENT_UNITS);
|
|
176
234
|
}
|
|
177
235
|
/**
|
|
178
|
-
*
|
|
236
|
+
* 关闭流程(固定八步,顺序写在 `shutdown.ts` 里,不可覆盖)。
|
|
179
237
|
*
|
|
180
238
|
* 由 `onClose` 钩子触发;spec 入口在 `finally` 里 `close()` 同样会走它——
|
|
181
239
|
* 「构造了什么就释放什么」,构建脚本不再留下未释放的资源。
|
|
182
240
|
*/
|
|
183
|
-
export function buildShutdownFlow(ctx, services) {
|
|
241
|
+
export function buildShutdownFlow(ctx, services, resourceUnits = []) {
|
|
184
242
|
return async () => {
|
|
185
243
|
await runShutdown({
|
|
186
244
|
flushRouters: () => disposeStage({ service: ctx.service, log: ctx.log, stage: ROUTERS_STAGE, disposer: ctx.disposers.routers }),
|
|
@@ -189,12 +247,13 @@ export function buildShutdownFlow(ctx, services) {
|
|
|
189
247
|
disposeServices: () => disposeServices({ service: ctx.service, log: ctx.log, services: services }),
|
|
190
248
|
flushClients: () => disposeStage({ service: ctx.service, log: ctx.log, stage: CLIENTS_STAGE, disposer: ctx.disposers.clients }),
|
|
191
249
|
flushConfig: () => disposeStage({ service: ctx.service, log: ctx.log, stage: CONFIG_STAGE, disposer: ctx.disposers.config }),
|
|
250
|
+
disposeResources: () => disposeResourceUnits({ service: ctx.service, log: ctx.log, units: resourceUnits }),
|
|
192
251
|
flushResources: () => disposeStage({ service: ctx.service, log: ctx.log, stage: RESOURCES_STAGE, disposer: ctx.disposers.resources }),
|
|
193
252
|
}, ctx.log);
|
|
194
253
|
};
|
|
195
254
|
}
|
|
196
255
|
/** 把关闭流程挂到 `onClose`(两个入口都挂:谁 `close()` 谁负责释放) */
|
|
197
|
-
export function installShutdownHook(ctx, services) {
|
|
198
|
-
const flow = buildShutdownFlow(ctx, services);
|
|
256
|
+
export function installShutdownHook(ctx, services, resourceUnits = []) {
|
|
257
|
+
const flow = buildShutdownFlow(ctx, services, resourceUnits);
|
|
199
258
|
ctx.app.addHook('onClose', () => flow());
|
|
200
259
|
}
|