@taicode/common-server 4.0.0-beta.5 → 4.0.0-beta.7
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/index.d.ts +3 -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 +51 -23
- package/output/core/entries.d.ts.map +1 -1
- package/output/core/entries.js +70 -31
- 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 +25 -6
- package/output/core/test-support/test-server.test.js +7 -5
- package/output/core/test-support/testdata.d.ts +171 -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 +240 -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 +3 -3
package/output/core/types.d.ts
CHANGED
|
@@ -6,18 +6,23 @@ import type { ConfigEnv } from './env.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* `defineServer` 槽位口径与接口类型(N1 / N2,**唯一规格来源**)。
|
|
8
8
|
*
|
|
9
|
-
* 顶层是「一个身份块 +
|
|
9
|
+
* 顶层是「一个身份块 + 工厂槽位」,**键顺序即分层**(`clients` 可选):
|
|
10
10
|
*
|
|
11
11
|
* ```
|
|
12
12
|
* app (身份块,无依赖)
|
|
13
13
|
* resources ← env 引导期:唯一读 env 的地方
|
|
14
14
|
* config ← env + resources 引导期:❗不给 services(结构性解耦)
|
|
15
|
-
* clients ← resources + config
|
|
15
|
+
* clients ← resources + config(可选) 外部服务客户端:❗不给 env / services / app
|
|
16
16
|
* services ← resources + config + clients 运行期开始:❗不给 env、不给 services
|
|
17
|
-
* middlewares ← app + services + config + clients (不给 resources / env
|
|
17
|
+
* middlewares ← app + services + config + clients (不给 resources / env);**有序数组**
|
|
18
18
|
* routers ← app + services + clients (不给 resources / env / config)
|
|
19
19
|
* ```
|
|
20
20
|
*
|
|
21
|
+
* 应用扩展 env(`Env`)**不是槽位**,而是 `defineServer` 的**第一个泛型**:可**单独手写**
|
|
22
|
+
* `defineServer<{ FOO?: string }>({...})`,也可不写、从 `resources` / `config` 的 `ctx` 类型标注
|
|
23
|
+
* 推导(`ResourcesContext<Env>`)。⚠️ 手写 `Env` 会让其余泛型停止推导(退化为 `object`,TS 限制),
|
|
24
|
+
* 详见 `defineServer` 文档。加载仍走内置逻辑,框架不提供自定义加载。
|
|
25
|
+
*
|
|
21
26
|
* 槽位只能引用**排在它前面**的产物(向下引用是编译错误);`env` 只属引导期
|
|
22
27
|
* (`resources` / `config` 可见,运行期三层一律看不到)。
|
|
23
28
|
*
|
|
@@ -36,13 +41,12 @@ import type { ConfigEnv } from './env.js';
|
|
|
36
41
|
*
|
|
37
42
|
* 类型安全机制(N2 §1):
|
|
38
43
|
* - **M1 分阶段 ctx**:每个工厂只拿到「该阶段该看的字段」(**外加每个阶段都有的 `disposer`**);
|
|
39
|
-
* - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) =>
|
|
40
|
-
* `Awaited<
|
|
44
|
+
* - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) => Resources`(不是 `Resources | Promise<Resources>`),下游一律
|
|
45
|
+
* `Awaited<Resources>` / `Awaited<Config>` / `Awaited<Clients>` / `Awaited<Services>`;
|
|
41
46
|
* - **M3 资源嵌套**:资源包挂在 `ctx.resources` 上(不再摊平到 ctx,键冲突从根上消失);
|
|
42
|
-
* - **M4 `Live<
|
|
43
|
-
* - **M5 强类型 `services`**:`middlewares` / `routers` 的 `ctx.services` 是 `
|
|
44
|
-
* - **M6
|
|
45
|
-
* - **M7 负向类型测试**:`define-server.negative-cases.ts`,由 `tsc --noEmit` 覆盖。
|
|
47
|
+
* - **M4 `Live<Value>` 静态开关**:资源包 / 客户端包里值为 `false` 的键在类型上剔除;
|
|
48
|
+
* - **M5 强类型 `services`**:`middlewares` / `routers` 的 `ctx.services` 是 `Services` 实体;
|
|
49
|
+
* - **M6 品牌**:`Server` 带 `__brand`(`SERVER_BRAND`),只能由 `defineServer` 产出,入口函数据此拒收裸对象。
|
|
46
50
|
*
|
|
47
51
|
* 命名全拼:`requestId` / `database` / `configuration` / `service`(不用 `db` / `cfg`)。
|
|
48
52
|
*/
|
|
@@ -67,8 +71,8 @@ export type AppInstance = FastifyInstance<RawServerDefault, IncomingMessage, Ser
|
|
|
67
71
|
*/
|
|
68
72
|
export interface AppIdentity {
|
|
69
73
|
/** 权限注册键 / cache 前缀 / 失效通道前缀 / 日志标签 */
|
|
70
|
-
readonly service: string;
|
|
71
74
|
readonly title: string;
|
|
75
|
+
readonly service: string;
|
|
72
76
|
/** openapi `info.version` + 权限操作自注册;建议从 package.json 读入 */
|
|
73
77
|
readonly version: string;
|
|
74
78
|
}
|
|
@@ -83,18 +87,143 @@ export interface RedisHandle {
|
|
|
83
87
|
dispose: () => void | Promise<void>;
|
|
84
88
|
}
|
|
85
89
|
/**
|
|
86
|
-
*
|
|
90
|
+
* 资源单元:**值 + 生命周期分离**。
|
|
91
|
+
*
|
|
92
|
+
* 为什么不把生命周期挂在值上(像 service 的 `init` / `dispose`):资源多是**第三方对象**
|
|
93
|
+
* (`PrismaClient`、Redis client、连接池),形状改不了 —— `prisma.setup = …` 是在改别人的类型。
|
|
94
|
+
* 单元是框架私有外壳,`value` 保持原样。
|
|
95
|
+
*
|
|
96
|
+
* ## 与 `ctx.disposer` 的分工
|
|
97
|
+
*
|
|
98
|
+
* 两者都能释放,但默认路径不同:
|
|
99
|
+
* - **资源自己的连接 / 句柄 → `dispose()`**:它和资源写在同一个对象里,一处表达完。
|
|
100
|
+
* - **`ctx.disposer` → 逃生口**:非常规清理(不是这个资源自身的句柄、需要跨资源协调、
|
|
101
|
+
* 临时产物),或资源值确实是裸对象、不想包单元时。
|
|
102
|
+
*
|
|
103
|
+
* 这正是「框架收起具体实现,`ctx` 留给特殊情况」的分工:正常情况不需要知道 `ctx.disposer`
|
|
104
|
+
* 存在。
|
|
105
|
+
*
|
|
106
|
+
* ## 两个生命周期,各管一段
|
|
107
|
+
*
|
|
108
|
+
* | 方法 | 谁调 | 何时 |
|
|
109
|
+
* | --- | --- | --- |
|
|
110
|
+
* | `setup()` | 框架(两个入口) | 装配期,`config` 之前 |
|
|
111
|
+
* | `dispose()` | 框架(关闭流程) | 进程关闭,最后一步 |
|
|
112
|
+
*
|
|
113
|
+
* 两者互不替代:`setup()` 是「连接前把地基铺好」(状态驱动的迁移 / 预热),`dispose()` 断开的是
|
|
114
|
+
* **连接**(一次性)。
|
|
115
|
+
*
|
|
116
|
+
* ## 用例间的样本数据不在资源上
|
|
117
|
+
*
|
|
118
|
+
* 「清表 + 重新插入样本数据」这伴事**不属于资源**:资源只知道有哪些表 / 哪些 key,不知道谁往里
|
|
119
|
+
* 写了什么。写在资源上就意味着新增一个 model 要回去改一次清库清单,漏改只会安静地变脏。
|
|
120
|
+
*
|
|
121
|
+
* 因此它单独做成一套机制:**`testdata/` 目录 + 每脚本自带 `load()` / `cleanup()`**,只在
|
|
122
|
+
* 可清理环境执行(见 `test-support/testdata.ts`)。
|
|
123
|
+
*
|
|
124
|
+
* ```ts
|
|
125
|
+
* resources: (ctx) => ({
|
|
126
|
+
* database: defineResource(createPrisma(ctx.env.DATABASE_URL), {
|
|
127
|
+
* // 自动执行:startServer / createTestServer 都会调,生产与测试同一份
|
|
128
|
+
* async setup() { await pushSchema(ctx.env.DATABASE_URL) },
|
|
129
|
+
* dispose: () => prisma.$disconnect(),
|
|
130
|
+
* }),
|
|
131
|
+
* cache: ctx.env.REDIS_URL ? defineResource(createRedis(ctx.env.REDIS_URL)) : false,
|
|
132
|
+
* })
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
export interface ResourceUnit<Value> {
|
|
136
|
+
/** 真正的资源对象(框架不碰) */
|
|
137
|
+
readonly value: Value;
|
|
138
|
+
/**
|
|
139
|
+
* 装配期准备(建连验证、同步库结构、预热……)。
|
|
140
|
+
*
|
|
141
|
+
* **何时跑**:`resources` 槽位构造完成后、`config` 槽位之前,按**对象声明序**逐项 `await`。
|
|
142
|
+
* 排在 `config` 之前是硬要求:`config` 常从数据库读(如 `SystemConfig` 表),库没准备好
|
|
143
|
+
* 它就连不上。
|
|
144
|
+
*
|
|
145
|
+
* **谁跑**:框架。两个入口都调,应用永不手调;`generateOpenApi` 不调(出 spec 不构造资源)。
|
|
146
|
+
*
|
|
147
|
+
* **不要在这里写 `if (ENV === 'test')`**:准备动作本身应当是状态驱动的(迁移工具会自己比对
|
|
148
|
+
* 现状、只补差量),生产与测试跑同一份代码。需要区分环境时,区分点应当来自 `ctx.env`,
|
|
149
|
+
* 而不是「跑不跑这个函数」。
|
|
150
|
+
*/
|
|
151
|
+
setup?(): void | Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* 关闭时释放本资源(断连、清池、flush……)。
|
|
154
|
+
*
|
|
155
|
+
* **何时跑**:关闭流程的最后一步(`resources` 阶段),按声明序**逆序**逐项 `await`——
|
|
156
|
+
* 后创建的先销毁;此时 `services` / `clients` / `config` 都已关完,不会再有使用方。
|
|
157
|
+
*
|
|
158
|
+
* **unit 级失败只记日志、不阻断其他资源**(关闭不能半途中断)。
|
|
159
|
+
*/
|
|
160
|
+
dispose?(): void | Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
/** 资源单元的生命周期(`defineResource` 的第二个参数) */
|
|
163
|
+
export interface ResourceLifecycle {
|
|
164
|
+
setup?(): void | Promise<void>;
|
|
165
|
+
dispose?(): void | Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 构造资源单元(唯一的正当方式)。
|
|
87
169
|
*
|
|
88
|
-
*
|
|
89
|
-
* 而 `cache: Cache | undefined`(显式可选)表示**运行时缺省**:键存在,类型为 `Cache | undefined`。
|
|
170
|
+
* 实现见 `core/resource.ts`(`types.ts` 只放类型,不放运行时值)。
|
|
90
171
|
*/
|
|
91
|
-
export type
|
|
92
|
-
|
|
172
|
+
export type DefineResource = <Value>(value: Value, lifecycle?: ResourceLifecycle) => ResourceUnit<Value>;
|
|
173
|
+
/** 单元 → 值;非单元原样通过(`defineResource` 是可选的,裸对象照旧可用) */
|
|
174
|
+
type UnwrapResource<Value> = Value extends ResourceUnit<infer Inner> ? Inner : Value;
|
|
175
|
+
/**
|
|
176
|
+
* 剔掉「静态关闭」的键,并从保留键的类型里抹掉 `false` 分支(编译期静态开关)。
|
|
177
|
+
*
|
|
178
|
+
* 三种写法,三种语义,都被精确表达:
|
|
179
|
+
* - `cache: false`(**纯 `false`**)→ **静态关闭**:`Live<Value>` 里该键**不存在**,访问直接编译失败;
|
|
180
|
+
* - `cache: Cache | false`(**条件构造**)→ 键**保留**,但类型收窄为 `Cache`(`false` 分支被
|
|
181
|
+
* `Exclude` 抹掉)——运行期 `liveResources` 会剔掉值为 `false` 的那一项,所以类型与运行时一致;
|
|
182
|
+
* - `cache: Cache | undefined`(**显式可选**)→ 键存在、类型保持 `Cache | undefined`(`undefined`
|
|
183
|
+
* 表示「运行时缺省」,与「静态关闭」语义不同,不受影响)。
|
|
184
|
+
*
|
|
185
|
+
* 旧版 `Value[K] extends false ? never : K` 只能处理「纯 `false`」,遇到 `Cache | false` 会把
|
|
186
|
+
* 整个键保留且类型仍含 `false`(与运行时剔除行为不符)。现版用 `Exclude<Value[K], false>` 对齐。
|
|
187
|
+
*/
|
|
188
|
+
export type Live<Value> = {
|
|
189
|
+
[K in keyof Value as [Exclude<Value[K], false>] extends [never] ? never : K]: Exclude<Value[K], false>;
|
|
93
190
|
};
|
|
94
|
-
/**
|
|
95
|
-
|
|
191
|
+
/**
|
|
192
|
+
* 框架在 ctx 上暴露的资源视图(`Awaited<>` 归一 + `false` 键剔除 + **单元解包**)。
|
|
193
|
+
*
|
|
194
|
+
* 解包是必须的:`ctx.resources.database` 必须是那个客户端本身,不能是
|
|
195
|
+
* `ResourceUnit<PrismaClient>` —— 否则每个消费方都要多写一层 `.value`,而单元外壳
|
|
196
|
+
* 是框架用来挂 `setup` 的实现细节。
|
|
197
|
+
*/
|
|
198
|
+
export type ResourcesView<Resources> = Live<{
|
|
199
|
+
[K in keyof Awaited<Resources>]: UnwrapResource<Awaited<Resources>[K]>;
|
|
200
|
+
}>;
|
|
96
201
|
/** 框架在 ctx 上暴露的客户端视图(口径同 `ResourcesView`) */
|
|
97
|
-
export type ClientsView<
|
|
202
|
+
export type ClientsView<Clients> = Live<Awaited<Clients>>;
|
|
203
|
+
/**
|
|
204
|
+
* 应用扩展后的 env 类型(固定 env + 应用声明的扩展字段)。
|
|
205
|
+
*
|
|
206
|
+
* `ConfigEnv` 是框架**固定约定**的最小集合(`ENV` / `API_PORT` / ...),不接受业务字段;
|
|
207
|
+
* 业务字段(如 `REDIS_KEY_PREFIX`、第三方 token)由应用声明:`Env` 是 `defineServer` 的**第一个
|
|
208
|
+
* 泛型**,可**手写** `defineServer<{ FOO?: string }>({...})`,也可从 `resources` / `config` 工厂的
|
|
209
|
+
* `ctx` 类型标注(`ResourcesContext<Env>` / `ConfigContext<Env>`)推导;两种方式都让扩展字段并进
|
|
210
|
+
* `ctx.env`、有类型地访问。(手写 `Env` 的代价见 `defineServer` 文档:其余泛型会退化为 `object`。)
|
|
211
|
+
*
|
|
212
|
+
* ⚠️ **只是类型声明,不影响加载**:env 的加载完全走框架内置逻辑(dotenv 分层 + 进程注入,
|
|
213
|
+
* 见 `env.ts`),框架**不提供自定义加载**。运行期扩展字段的值就是内置加载后的 `process.env[key]`
|
|
214
|
+
* (字符串直通,不映射 / 不校验 / 不给默认值——要默认值 / 归一化在 `resources` 里自己做)。
|
|
215
|
+
*
|
|
216
|
+
* 这样各仓不必再往 `ctx.env` 上开「`process.env.XXX` 无类型后门」——用泛型或 ctx 标注声明即可。
|
|
217
|
+
*
|
|
218
|
+
* ```ts
|
|
219
|
+
* // 命名工厂:参数标注扩展 env,`defineServer` 从中推导 Env(point-free 最简)
|
|
220
|
+
* function createResources(ctx: ResourcesContext<{ REDIS_KEY_PREFIX?: string }>) {
|
|
221
|
+
* return { cache: createCache({ prefix: ctx.env.REDIS_KEY_PREFIX ?? 'account' }) }
|
|
222
|
+
* }
|
|
223
|
+
* const server = defineServer({ app, resources: createResources, config, services, middlewares, routers })
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
export type AppEnv<Env> = ConfigEnv & Env;
|
|
98
227
|
/**
|
|
99
228
|
* 资源错误分类器(响应层用)。
|
|
100
229
|
*
|
|
@@ -122,8 +251,8 @@ export type ErrorClassifier = (error: unknown) => SystemError | null;
|
|
|
122
251
|
* }
|
|
123
252
|
* ```
|
|
124
253
|
*/
|
|
125
|
-
export interface ResourcesContext {
|
|
126
|
-
readonly env:
|
|
254
|
+
export interface ResourcesContext<Env = object> {
|
|
255
|
+
readonly env: AppEnv<Env>;
|
|
127
256
|
readonly log: LoggerLike;
|
|
128
257
|
/**
|
|
129
258
|
* 本阶段的释放器:**在创建资源的地方当场注册释放**。
|
|
@@ -140,7 +269,7 @@ export interface ResourcesContext {
|
|
|
140
269
|
*/
|
|
141
270
|
registerClassifyError(classifier: ErrorClassifier): void;
|
|
142
271
|
}
|
|
143
|
-
export type ResourcesFactory<
|
|
272
|
+
export type ResourcesFactory<Resources, Env = object> = (ctx: ResourcesContext<Env>) => Resources;
|
|
144
273
|
/**
|
|
145
274
|
* `config(ctx)` 的入参:`env` + `log` + `disposer` + 资源视图。
|
|
146
275
|
*
|
|
@@ -155,14 +284,14 @@ export type ResourcesFactory<R> = (ctx: ResourcesContext) => R;
|
|
|
155
284
|
*
|
|
156
285
|
* 返回值是**启动期快照**:进程内不再变,没有热更新通道。要能随时改的开关放数据库、请求期现读。
|
|
157
286
|
*/
|
|
158
|
-
export interface ConfigContext<
|
|
159
|
-
readonly env:
|
|
287
|
+
export interface ConfigContext<Resources, Env = object> {
|
|
288
|
+
readonly env: AppEnv<Env>;
|
|
160
289
|
readonly log: LoggerLike;
|
|
161
290
|
/** 本阶段的释放器(关闭时在 services 之后、resources 之前 flush) */
|
|
162
291
|
readonly disposer: Disposer;
|
|
163
|
-
readonly resources: ResourcesView<
|
|
292
|
+
readonly resources: ResourcesView<Resources>;
|
|
164
293
|
}
|
|
165
|
-
export type ConfigFactory<
|
|
294
|
+
export type ConfigFactory<Resources, Config, Env = object> = (ctx: ConfigContext<Resources, Env>) => Config;
|
|
166
295
|
/**
|
|
167
296
|
* `clients(ctx)` 的入参:`log` + `disposer` + `resources` + `config`。
|
|
168
297
|
*
|
|
@@ -183,16 +312,17 @@ export type ConfigFactory<R, C> = (ctx: ConfigContext<R>) => C;
|
|
|
183
312
|
* }
|
|
184
313
|
* ```
|
|
185
314
|
*
|
|
186
|
-
*
|
|
315
|
+
* 没有外部客户端时**省略本槽位**(框架默认空客户端包)——独立成层只为「分层依赖」
|
|
316
|
+
* (排在 `config` 之后、`services` 之前),不要求「必须出现」。
|
|
187
317
|
*/
|
|
188
|
-
export interface ClientsContext<
|
|
318
|
+
export interface ClientsContext<Resources, Config> {
|
|
189
319
|
readonly log: LoggerLike;
|
|
190
320
|
/** 本阶段的释放器(关闭时在 services 之后、config 之前 flush) */
|
|
191
321
|
readonly disposer: Disposer;
|
|
192
|
-
readonly resources: ResourcesView<
|
|
193
|
-
readonly config: Awaited<
|
|
322
|
+
readonly resources: ResourcesView<Resources>;
|
|
323
|
+
readonly config: Awaited<Config>;
|
|
194
324
|
}
|
|
195
|
-
export type ClientsFactory<
|
|
325
|
+
export type ClientsFactory<Resources, Config, Clients> = (ctx: ClientsContext<NoInfer<Resources>, NoInfer<Config>>) => Clients;
|
|
196
326
|
/**
|
|
197
327
|
* `services(ctx)` 的入参:`log` + `disposer` + `resources` + `config` + `clients`。
|
|
198
328
|
*
|
|
@@ -212,54 +342,73 @@ export type ClientsFactory<R, C, CL> = (ctx: ClientsContext<NoInfer<R>, NoInfer<
|
|
|
212
342
|
* - **service 之间不得互依**:要共享的东西放 `clients`;需要常驻资源的单元自己实现
|
|
213
343
|
* `init()` / `dispose()`(框架会在 `middlewares` 之前 `init`、关闭时逆序 `dispose`)。
|
|
214
344
|
*/
|
|
215
|
-
export interface ServiceContext<
|
|
345
|
+
export interface ServiceContext<Resources, Config, Clients = unknown> {
|
|
216
346
|
readonly log: LoggerLike;
|
|
217
347
|
/** 本阶段的释放器(关闭时**先** flush 它,再逐单元逆序 `dispose`) */
|
|
218
348
|
readonly disposer: Disposer;
|
|
219
|
-
readonly resources: ResourcesView<
|
|
220
|
-
readonly config: Awaited<
|
|
221
|
-
readonly clients: ClientsView<
|
|
349
|
+
readonly resources: ResourcesView<Resources>;
|
|
350
|
+
readonly config: Awaited<Config>;
|
|
351
|
+
readonly clients: ClientsView<Clients>;
|
|
222
352
|
}
|
|
223
353
|
/**
|
|
224
|
-
*
|
|
354
|
+
* 一个已构造好的中间件注册函数:接收 app,挂钩子 / 注册插件。
|
|
225
355
|
*
|
|
226
356
|
* 中间件**不参与生命周期**(要常驻资源就放 `services`);注册后不再暴露内部钩子名。
|
|
227
357
|
*/
|
|
228
358
|
export type Middleware = (app: AppInstance) => void | Promise<void>;
|
|
359
|
+
/**
|
|
360
|
+
* 具名中间件单元:`register` + 一个仅用于日志 / 报错定位的 `name`。
|
|
361
|
+
*
|
|
362
|
+
* 标准中间件工厂(`createCorsMiddleware` / `createAuthMiddleware` / `createRateLimitMiddleware`)
|
|
363
|
+
* 直接返回带默认名字的单元,调用方把它们按顺序摆进数组即可,不必自己起名。
|
|
364
|
+
*/
|
|
365
|
+
export interface MiddlewareUnit {
|
|
366
|
+
/** 名字:仅用于注册期日志 / 报错定位,**不参与生命周期、不影响注册序**(顺序由数组位置决定) */
|
|
367
|
+
readonly name: string;
|
|
368
|
+
/** 注册函数 */
|
|
369
|
+
readonly register: Middleware;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* `middlewares` 数组里的一项:**具名单元**或**裸注册函数**。
|
|
373
|
+
*
|
|
374
|
+
* 裸函数会被框架补一个占位名(`middleware[i]`,`i` 为数组下标)用于日志;要可读的日志名
|
|
375
|
+
* 就返回 `MiddlewareUnit`(标准工厂已经这么做了)。
|
|
376
|
+
*/
|
|
377
|
+
export type MiddlewareEntry = MiddlewareUnit | Middleware;
|
|
229
378
|
/**
|
|
230
379
|
* `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config` + `clients`。
|
|
231
380
|
*
|
|
232
381
|
* ❗**故意没有 `resources` / `env`** —— 中间件要访问数据层必须穿过 service。
|
|
233
382
|
*
|
|
234
383
|
* ```ts
|
|
235
|
-
* middlewares: (ctx) =>
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
384
|
+
* middlewares: (ctx) => [
|
|
385
|
+
* createCorsMiddleware(ctx),
|
|
386
|
+
* createAuthMiddleware(ctx, { client: createAuthClient(ctx.services) }),
|
|
387
|
+
* createRateLimitMiddleware(ctx, { store: ctx.services.rateLimitStore }),
|
|
388
|
+
* ]
|
|
240
389
|
* ```
|
|
241
|
-
*
|
|
242
|
-
* 返回对象是**键即名字、键序即注册序**(见下方 `MiddlewaresFactory` 的数字键陷阱)。
|
|
243
390
|
*/
|
|
244
|
-
export interface MiddlewareContext<
|
|
391
|
+
export interface MiddlewareContext<Config, Services, Clients = unknown> {
|
|
245
392
|
readonly app: AppInstance;
|
|
246
393
|
readonly log: LoggerLike;
|
|
247
394
|
/** 本阶段的释放器(关闭时最早 flush:在 services / clients / config / resources 之前) */
|
|
248
395
|
readonly disposer: Disposer;
|
|
249
|
-
readonly services: Awaited<
|
|
250
|
-
readonly config: Awaited<
|
|
251
|
-
readonly clients: ClientsView<
|
|
396
|
+
readonly services: Awaited<Services>;
|
|
397
|
+
readonly config: Awaited<Config>;
|
|
398
|
+
readonly clients: ClientsView<Clients>;
|
|
252
399
|
}
|
|
253
400
|
/**
|
|
254
|
-
* `middlewares`
|
|
401
|
+
* `middlewares` 返回一个**有序数组** —— **数组位置即注册序**,再无歧义。
|
|
255
402
|
*
|
|
256
|
-
*
|
|
403
|
+
* 为什么是数组而不是 `Record<string, Middleware>`:注册序是中间件的核心语义(cors 必须在
|
|
404
|
+
* auth 之前、auth 必须在限流之前),而对象键的枚举序由 JS 引擎决定(纯数字键还会被重排到
|
|
405
|
+
* 最前)——用「顺序由引擎决定、且有已知陷阱」的结构承载「必须严格有序」的东西本身自相矛盾。
|
|
406
|
+
* 数组把顺序交还给书写顺序,既直观又没有数字键坑。
|
|
257
407
|
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
* 这类标识符即可)。除此外,注册序 = 对象键顺序。
|
|
408
|
+
* 每一项可以是标准工厂返回的具名单元(`MiddlewareUnit`),也可以是一个裸注册函数
|
|
409
|
+
* (`Middleware`,框架会补占位名用于日志)。
|
|
261
410
|
*/
|
|
262
|
-
export type MiddlewaresFactory<
|
|
411
|
+
export type MiddlewaresFactory<Config, Services, Clients = unknown> = (ctx: MiddlewareContext<Config, Services, Clients>) => MiddlewareEntry[];
|
|
263
412
|
/**
|
|
264
413
|
* `routers(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `clients`。
|
|
265
414
|
*
|
|
@@ -275,48 +424,62 @@ export type MiddlewaresFactory<C, S, CL = unknown> = (ctx: MiddlewareContext<C,
|
|
|
275
424
|
* `generateOpenApi` 走这条路径时不构造 `services`,注册期就读它会让出 spec 直接失败。
|
|
276
425
|
* 没业务路由也要写 `routers: () => {}`。
|
|
277
426
|
*/
|
|
278
|
-
export interface RouterContext<
|
|
427
|
+
export interface RouterContext<Services, Clients = unknown> {
|
|
279
428
|
readonly app: AppInstance;
|
|
280
429
|
readonly log: LoggerLike;
|
|
281
430
|
/** 本阶段的释放器(关闭时**最先** flush) */
|
|
282
431
|
readonly disposer: Disposer;
|
|
283
|
-
readonly services: Awaited<
|
|
284
|
-
readonly clients: ClientsView<
|
|
432
|
+
readonly services: Awaited<Services>;
|
|
433
|
+
readonly clients: ClientsView<Clients>;
|
|
285
434
|
}
|
|
286
|
-
export type RoutersFactory<
|
|
435
|
+
export type RoutersFactory<Services, Clients = unknown> = (ctx: RouterContext<Services, Clients>) => void | Promise<void>;
|
|
287
436
|
/**
|
|
288
437
|
* `services` 返回值形状闸门:
|
|
289
438
|
* - 原始类型值(`string` / `number` / `boolean`)**拒绝**(防把配置塞进 services)。
|
|
290
439
|
*/
|
|
291
|
-
export type ServicesShape<
|
|
292
|
-
[K in keyof
|
|
440
|
+
export type ServicesShape<Services> = {
|
|
441
|
+
[K in keyof Services]: Services[K] extends string | number | boolean ? never : Services[K];
|
|
293
442
|
};
|
|
294
443
|
/**
|
|
295
|
-
* `defineServer()` 入参:一个身份块 +
|
|
444
|
+
* `defineServer()` 入参:一个身份块 + 工厂槽位(键顺序=分层)。
|
|
296
445
|
*
|
|
297
446
|
* ```
|
|
298
|
-
* app → resources(env) → config(env+resources) → clients(resources+config)
|
|
447
|
+
* app → resources(env) → config(env+resources) → (clients?)(resources+config)
|
|
299
448
|
* → services(resources+config+clients) → middlewares(app+services+config+clients)
|
|
300
449
|
* → routers(app+services+clients)
|
|
301
450
|
* ```
|
|
302
451
|
*
|
|
303
|
-
* `
|
|
304
|
-
*
|
|
452
|
+
* `clients` 槽位**可选**(省略即空客户端包、`Clients` 退化为空)。
|
|
453
|
+
*
|
|
454
|
+
* **应用扩展 env(`Env`)不是槽位**,而是 `defineServer` 的**第一个泛型**(本接口里排最后一位,
|
|
455
|
+
* 但 `defineServer` 把它提到首位以便单独手写):手写 `defineServer<{ FOO?: string }>({...})`,或
|
|
456
|
+
* 不写、从 `resources` / `config` 的 `ctx` 类型标注推导。不声明即无扩展(`Env = object`)。
|
|
457
|
+
* 手写 `Env` 会让其余泛型退化为 `object`(TS 限制,详见 `defineServer`)。加载走内置逻辑。
|
|
458
|
+
*
|
|
459
|
+
* `Services` 上的两道闸门(`ServicesShape<Services>`,N2 §2.5)写在 `services` 的返回类型上:
|
|
460
|
+
* `Services & ServicesShape<Services>` —— 形状合法时恒等,非法时该值塌成 `never` → 编译失败。
|
|
305
461
|
*/
|
|
306
|
-
export interface ServerConfig<
|
|
462
|
+
export interface ServerConfig<Resources, Config, Clients = object, Services = object, Env = object> {
|
|
307
463
|
app: AppIdentity;
|
|
308
|
-
|
|
309
|
-
|
|
464
|
+
/**
|
|
465
|
+
* 资源工厂。`ctx` 类型可写 `ResourcesContext<Env>` 声明应用扩展 env —— `Env` 从这里推导、
|
|
466
|
+
* 并进 `ctx.env`(见 `AppEnv`)。不声明即 `Env = object`(无扩展)。
|
|
467
|
+
*/
|
|
468
|
+
resources: ResourcesFactory<Resources, Env>;
|
|
469
|
+
config: ConfigFactory<NoInfer<Resources>, Config, Env>;
|
|
310
470
|
/**
|
|
311
471
|
* 外部服务客户端(**我们调用的、地址/凭证来自业务配置的外部服务**)。
|
|
312
472
|
*
|
|
313
473
|
* 存在的唯一理由是「`services` 之间禁止互依」与「客户端需要 `config`」这两条约束
|
|
314
474
|
* 的交集:多个 service / middleware 必须共享同一份客户端,而它们之间没有共享通道。
|
|
315
|
-
*
|
|
475
|
+
*
|
|
476
|
+
* **可选**:没有外部客户端就**省略**本槽位(框架默认 `() => ({})`,`Clients` 退化为空)——
|
|
477
|
+
* 不再要求写一行仪式性的 `clients: () => ({})`。独立成层的意义只在于「分层依赖」
|
|
478
|
+
* (客户端排在 `config` 之后、`services` 之前),不在于「必须出现」。
|
|
316
479
|
*/
|
|
317
|
-
clients
|
|
318
|
-
services: (ctx: ServiceContext<NoInfer<
|
|
319
|
-
middlewares: MiddlewaresFactory<NoInfer<
|
|
480
|
+
clients?: ClientsFactory<NoInfer<Resources>, NoInfer<Config>, Clients>;
|
|
481
|
+
services: (ctx: ServiceContext<NoInfer<Resources>, NoInfer<Config>, NoInfer<Clients>>) => Services & ServicesShape<Services>;
|
|
482
|
+
middlewares: MiddlewaresFactory<NoInfer<Config>, NoInfer<Services>, NoInfer<Clients>>;
|
|
320
483
|
/**
|
|
321
484
|
* 业务路由注册(**必填**)。
|
|
322
485
|
*
|
|
@@ -325,14 +488,15 @@ export interface ServerConfig<R, C, CL, S> {
|
|
|
325
488
|
* 本槽位只用于注册**业务**路由。没有业务路由的应用也必须显式传入(写 `() => {}`),
|
|
326
489
|
* 以保持槽位结构恒定、无「可省略」分支。
|
|
327
490
|
*/
|
|
328
|
-
routers: RoutersFactory<NoInfer<
|
|
491
|
+
routers: RoutersFactory<NoInfer<Services>, NoInfer<Clients>>;
|
|
329
492
|
}
|
|
330
493
|
/** 品牌字段值(`Server` 的 `__brand`) */
|
|
331
494
|
export declare const SERVER_BRAND = "taicode.server";
|
|
332
495
|
/** 纯定义产物:同一份定义可同时喂给「启动」与「出 spec」 */
|
|
333
|
-
export type Server<
|
|
496
|
+
export type Server<Resources, Config, Clients = object, Services = object, Env = object> = ServerConfig<Resources, Config, Clients, Services, Env> & {
|
|
334
497
|
readonly __brand: typeof SERVER_BRAND;
|
|
335
498
|
};
|
|
336
499
|
/** `generateOpenApi()` 的返回(OpenAPI 文档对象) */
|
|
337
500
|
export type OpenApiDocument = Record<string, unknown>;
|
|
501
|
+
export {};
|
|
338
502
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/core/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACjE,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;AAEzC
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/core/types.ts"],"names":[],"mappings":"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"}
|
package/output/cors/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare function createCorsMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>):
|
|
1
|
+
import type { MiddlewareContext, MiddlewareUnit } from '../core/types.js';
|
|
2
|
+
export declare function createCorsMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>): MiddlewareUnit;
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/cors/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/cors/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAc,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAkBrF,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,cAAc,CA4B3G"}
|
package/output/cors/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import cors from '@fastify/cors';
|
|
2
2
|
import { readConfigKey } from '../core/boot/context.js';
|
|
3
3
|
export function createCorsMiddleware(ctx) {
|
|
4
|
-
|
|
4
|
+
const register = async (app) => {
|
|
5
5
|
let warned = false;
|
|
6
6
|
// 请求期现读:`cors` 配置变化立即生效;构造期不读
|
|
7
7
|
const resolveOrigins = () => {
|
|
@@ -24,4 +24,5 @@ export function createCorsMiddleware(ctx) {
|
|
|
24
24
|
credentials: true,
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
|
+
return { name: 'cors', register };
|
|
27
28
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MiddlewareContext, MiddlewareUnit } from '../core/types.js';
|
|
2
2
|
import type { RateLimitStore } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* 标准中间件单元 `rateLimit`(可替换;**不在默认预设内**)。
|
|
@@ -14,7 +14,7 @@ export interface RateLimitMiddlewareOptions {
|
|
|
14
14
|
/** 存储后端(缺省进程内 memory) */
|
|
15
15
|
store?: RateLimitStore;
|
|
16
16
|
}
|
|
17
|
-
export declare function createRateLimitMiddleware<C = unknown, S = unknown>(_ctx: MiddlewareContext<C, S>, options?: RateLimitMiddlewareOptions):
|
|
17
|
+
export declare function createRateLimitMiddleware<C = unknown, S = unknown>(_ctx: MiddlewareContext<C, S>, options?: RateLimitMiddlewareOptions): MiddlewareUnit;
|
|
18
18
|
export { RateLimiter, createRateLimitKey } from './limiter.js';
|
|
19
19
|
export { MemoryRateLimitStore } from './memory-store.js';
|
|
20
20
|
export { RedisRateLimitStore } from './redis-store.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/rate-limit/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/rate-limit/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAc,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD;;;;;;;;;GASG;AAEH,MAAM,WAAW,0BAA0B;IACzC,yBAAyB;IACzB,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB;AAqBD,wBAAgB,yBAAyB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,cAAc,CAoB3J;AAGD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AAClE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
|
|
@@ -20,7 +20,7 @@ function buildKeys(request, keys) {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
export function createRateLimitMiddleware(_ctx, options = {}) {
|
|
23
|
-
|
|
23
|
+
const register = (app) => {
|
|
24
24
|
const limiter = new RateLimiter({ store: options.store ?? new MemoryRateLimitStore() });
|
|
25
25
|
const prefix = `${readServerIdentity(app)?.service ?? ''}:`;
|
|
26
26
|
app.addHook('preValidation', async (request) => {
|
|
@@ -35,6 +35,7 @@ export function createRateLimitMiddleware(_ctx, options = {}) {
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
};
|
|
38
|
+
return { name: 'rateLimit', register };
|
|
38
39
|
}
|
|
39
40
|
// ── 限流实现(与中间件同一模块)─────────────────────────────────
|
|
40
41
|
export { RateLimiter, createRateLimitKey } from './limiter.js';
|
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.7",
|
|
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.7"
|
|
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.7"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"test": "vitest run",
|