@taicode/common-server 3.8.0 → 4.0.0-beta.10
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 +18 -15
- package/output/app/index.d.ts.map +1 -1
- package/output/app/index.js +330 -9
- package/output/auth/index.d.ts +73 -0
- package/output/auth/index.d.ts.map +1 -0
- package/output/auth/index.js +196 -0
- package/output/core/assemble.d.ts +64 -0
- package/output/core/assemble.d.ts.map +1 -0
- package/output/core/assemble.js +88 -0
- package/output/core/boot/context.d.ts +109 -0
- package/output/core/boot/context.d.ts.map +1 -0
- package/output/core/boot/context.js +145 -0
- package/output/core/boot/lifecycle.d.ts +206 -0
- package/output/core/boot/lifecycle.d.ts.map +1 -0
- package/output/core/boot/lifecycle.js +255 -0
- package/output/core/boot/shutdown.d.ts +34 -0
- package/output/core/boot/shutdown.d.ts.map +1 -0
- package/output/core/boot/shutdown.js +50 -0
- package/output/core/boot/signal.d.ts +13 -0
- package/output/core/boot/signal.d.ts.map +1 -0
- package/output/{app → core/boot}/signal.js +5 -4
- package/output/core/boot/steps.d.ts +138 -0
- package/output/core/boot/steps.d.ts.map +1 -0
- package/output/core/boot/steps.js +259 -0
- package/output/core/entries.d.ts +151 -0
- package/output/core/entries.d.ts.map +1 -0
- package/output/core/entries.js +242 -0
- package/output/core/entries.test.d.ts +2 -0
- package/output/core/entries.test.d.ts.map +1 -0
- package/output/core/entries.test.js +458 -0
- package/output/core/env.d.ts +83 -0
- package/output/core/env.d.ts.map +1 -0
- package/output/core/env.js +124 -0
- package/output/{app → core}/env.test.d.ts.map +1 -1
- package/output/core/env.test.js +105 -0
- package/output/core/modules/auth-provider/index.d.ts +10 -0
- package/output/core/modules/auth-provider/index.d.ts.map +1 -0
- package/output/core/modules/auth-provider/index.js +9 -0
- package/output/core/modules/auth-provider/types.d.ts +71 -0
- package/output/core/modules/auth-provider/types.d.ts.map +1 -0
- package/output/core/modules/context/index.d.ts +18 -0
- package/output/core/modules/context/index.d.ts.map +1 -0
- package/output/core/modules/context/index.js +23 -0
- package/output/core/modules/context/index.test.d.ts.map +1 -0
- package/output/core/modules/cookie/index.d.ts +21 -0
- package/output/core/modules/cookie/index.d.ts.map +1 -0
- package/output/core/modules/cookie/index.js +17 -0
- package/output/core/modules/identity/index.d.ts +4 -0
- package/output/core/modules/identity/index.d.ts.map +1 -0
- package/output/core/modules/identity/index.js +14 -0
- package/output/core/modules/logging/index.d.ts +5 -0
- package/output/core/modules/logging/index.d.ts.map +1 -0
- package/output/core/modules/logging/index.js +29 -0
- package/output/core/modules/openapi/index.d.ts +7 -0
- package/output/core/modules/openapi/index.d.ts.map +1 -0
- package/output/core/modules/openapi/index.js +41 -0
- package/output/core/modules/permission-operations/index.d.ts +26 -0
- package/output/core/modules/permission-operations/index.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.js +36 -0
- package/output/core/modules/permission-operations/index.test.d.ts +2 -0
- package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
- package/output/core/modules/permission-operations/index.test.js +45 -0
- package/output/core/modules/request-id/index.d.ts +33 -0
- package/output/core/modules/request-id/index.d.ts.map +1 -0
- package/output/core/modules/request-id/index.js +66 -0
- package/output/core/modules/response/index.d.ts +50 -0
- package/output/core/modules/response/index.d.ts.map +1 -0
- package/output/core/modules/response/index.js +102 -0
- package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
- package/output/core/modules/schema/builders.d.ts.map +1 -0
- package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
- package/output/core/modules/schema/index.d.ts +34 -0
- package/output/core/modules/schema/index.d.ts.map +1 -0
- package/output/core/modules/schema/index.js +62 -0
- package/output/core/modules/schema/index.test.d.ts +2 -0
- package/output/core/modules/schema/index.test.d.ts.map +1 -0
- package/output/core/modules/schema/index.test.js +55 -0
- package/output/core/modules/system-routes/index.d.ts +15 -0
- package/output/core/modules/system-routes/index.d.ts.map +1 -0
- package/output/core/modules/system-routes/index.js +205 -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 +89 -0
- package/output/core/test-support/database.d.ts.map +1 -0
- package/output/core/test-support/database.js +131 -0
- package/output/core/test-support/database.test.d.ts.map +1 -0
- package/output/core/test-support/database.test.js +111 -0
- package/output/core/test-support/environment.d.ts +38 -0
- package/output/core/test-support/environment.d.ts.map +1 -0
- package/output/core/test-support/environment.js +45 -0
- package/output/core/test-support/service-context.d.ts +64 -0
- package/output/core/test-support/service-context.d.ts.map +1 -0
- package/output/core/test-support/service-context.js +63 -0
- package/output/core/test-support/service-context.test.d.ts +2 -0
- package/output/core/test-support/service-context.test.d.ts.map +1 -0
- package/output/core/test-support/service-context.test.js +55 -0
- package/output/core/test-support/test-server.d.ts +154 -0
- package/output/core/test-support/test-server.d.ts.map +1 -0
- package/output/core/test-support/test-server.js +114 -0
- package/output/core/test-support/test-server.test.d.ts +2 -0
- package/output/core/test-support/test-server.test.d.ts.map +1 -0
- package/output/core/test-support/test-server.test.js +296 -0
- package/output/core/test-support/testdata.d.ts +255 -0
- package/output/core/test-support/testdata.d.ts.map +1 -0
- package/output/core/test-support/testdata.js +271 -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 +334 -0
- package/output/core/type-augment.d.ts +34 -0
- package/output/core/type-augment.d.ts.map +1 -0
- package/output/core/type-augment.js +7 -0
- package/output/core/types.d.ts +582 -0
- package/output/core/types.d.ts.map +1 -0
- package/output/core/types.js +2 -0
- package/output/cors/index.d.ts +3 -0
- package/output/cors/index.d.ts.map +1 -0
- package/output/cors/index.js +28 -0
- package/output/index.d.ts +10 -4
- package/output/index.d.ts.map +1 -1
- package/output/index.js +25 -5
- package/output/rate-limit/index.d.ts +23 -0
- package/output/rate-limit/index.d.ts.map +1 -0
- package/output/rate-limit/index.js +43 -0
- package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
- package/output/rate-limit/limiter.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
- package/output/rate-limit/memory-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
- package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
- package/output/rate-limit/redis-store.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
- package/output/rate-limit/redis-store.test.d.ts.map +1 -0
- package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
- package/output/rate-limit/types.d.ts +45 -0
- package/output/rate-limit/types.d.ts.map +1 -0
- package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
- package/output/redis/cache.d.ts.map +1 -0
- package/output/redis/cache.test.d.ts +2 -0
- package/output/redis/cache.test.d.ts.map +1 -0
- package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
- package/output/redis/index.d.ts +50 -0
- package/output/redis/index.d.ts.map +1 -0
- package/output/redis/index.js +71 -0
- package/output/redis/index.test.d.ts +2 -0
- package/output/{context → redis}/index.test.d.ts.map +1 -1
- package/output/redis/index.test.js +55 -0
- package/output/redis-queue/index.js +1 -1
- package/output/redis-queue/test-helpers.d.ts +1 -1
- package/output/redis-queue/test-helpers.js +1 -1
- package/output/redis-queue/types.d.ts +3 -12
- package/output/redis-queue/types.d.ts.map +1 -1
- package/package.json +11 -7
- package/output/app/assemble.d.ts +0 -16
- package/output/app/assemble.d.ts.map +0 -1
- package/output/app/assemble.js +0 -124
- package/output/app/database.d.ts +0 -36
- package/output/app/database.d.ts.map +0 -1
- package/output/app/database.js +0 -40
- package/output/app/database.test.d.ts.map +0 -1
- package/output/app/database.test.js +0 -66
- package/output/app/default-modules.d.ts +0 -23
- package/output/app/default-modules.d.ts.map +0 -1
- package/output/app/default-modules.js +0 -40
- package/output/app/define-server.d.ts +0 -12
- package/output/app/define-server.d.ts.map +0 -1
- package/output/app/define-server.js +0 -289
- package/output/app/define-server.test.d.ts +0 -2
- package/output/app/define-server.test.d.ts.map +0 -1
- package/output/app/define-server.test.js +0 -299
- package/output/app/env.d.ts +0 -24
- package/output/app/env.d.ts.map +0 -1
- package/output/app/env.js +0 -38
- package/output/app/env.test.js +0 -78
- package/output/app/module.d.ts +0 -88
- package/output/app/module.d.ts.map +0 -1
- package/output/app/module.js +0 -34
- package/output/app/shutdown.d.ts +0 -9
- package/output/app/shutdown.d.ts.map +0 -1
- package/output/app/shutdown.js +0 -15
- package/output/app/shutdown.test.d.ts +0 -2
- package/output/app/shutdown.test.d.ts.map +0 -1
- package/output/app/shutdown.test.js +0 -149
- package/output/app/signal.d.ts +0 -12
- package/output/app/signal.d.ts.map +0 -1
- package/output/app/steps.d.ts +0 -17
- package/output/app/steps.d.ts.map +0 -1
- package/output/app/steps.js +0 -41
- package/output/app/types.d.ts +0 -149
- package/output/app/types.d.ts.map +0 -1
- package/output/context/index.d.ts +0 -6
- package/output/context/index.d.ts.map +0 -1
- package/output/context/index.js +0 -14
- package/output/rate-limiter/index.d.ts +0 -5
- package/output/rate-limiter/index.d.ts.map +0 -1
- package/output/rate-limiter/index.js +0 -4
- package/output/rate-limiter/memory-store.d.ts.map +0 -1
- package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.d.ts.map +0 -1
- package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
- package/output/rate-limiter/types.d.ts +0 -27
- package/output/rate-limiter/types.d.ts.map +0 -1
- package/output/redis-cache/index.d.ts +0 -4
- package/output/redis-cache/index.d.ts.map +0 -1
- package/output/redis-cache/index.js +0 -1
- package/output/redis-cache/redis-cache.d.ts.map +0 -1
- package/output/redis-cache/redis-cache.test.d.ts +0 -2
- package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
- package/output/schema/index.d.ts.map +0 -1
- /package/output/{app → core}/env.test.d.ts +0 -0
- /package/output/{app → core/modules/auth-provider}/types.js +0 -0
- /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
- /package/output/{context → core/modules/context}/index.test.js +0 -0
- /package/output/{app → core/test-support}/database.test.d.ts +0 -0
- /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
- /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
- /package/output/{rate-limiter → rate-limit}/types.js +0 -0
- /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { initServiceUnits } from './boot/lifecycle.js';
|
|
2
|
+
import { attachSignalHandlers } from './boot/signal.js';
|
|
3
|
+
import { buildClients, buildConfig, buildResources, buildServices, createBootContext, createDocumentContext, installShutdownHook, registerCoreStep, registerDocumentCoreStep, registerDocumentRoutersStep, registerDocumentSystemRoutesStep, registerMiddlewares, registerRouters, registerSystemRoutesStep, resolveAppEnv, setupResources, } from './boot/steps.js';
|
|
4
|
+
import { loadEnvironment, resolveConfigEnv, CLEANABLE_ENVS } from './env.js';
|
|
5
|
+
import { SERVER_BRAND } from './types.js';
|
|
6
|
+
import { discoverTestDataModules, loadTestData } from './test-support/testdata.js';
|
|
7
|
+
/** 默认监听地址 */
|
|
8
|
+
const DEFAULT_HOST = '0.0.0.0';
|
|
9
|
+
function resolvePort(env) {
|
|
10
|
+
const parsed = Number(env.API_PORT);
|
|
11
|
+
// `0` 是合法值:交给内核分配空闲端口(测试与多实例本地调试都靠它,而不是靠「不监听」开关)
|
|
12
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
13
|
+
throw new Error(`[server] API_PORT is required to listen (got "${env.API_PORT}")`);
|
|
14
|
+
}
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* `defineServer()`(N1 §1)——**纯定义**:产出一个 `Server` 对象,不建连、不起服务。
|
|
19
|
+
*
|
|
20
|
+
* 启动是另一个函数(`startServer`)的事——同一份定义可同时喂给「启动」与「出 spec」
|
|
21
|
+
* (`generateOpenApi`)。
|
|
22
|
+
*
|
|
23
|
+
* ## 怎么用
|
|
24
|
+
*
|
|
25
|
+
* 一个服务**只在 `source/server.ts` 里写一次**,然后在三个地方消费它:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* // source/server.ts
|
|
29
|
+
* export const server = defineServer({ app, resources, config, clients, services, middlewares, routers })
|
|
30
|
+
* ```
|
|
31
|
+
* ```ts
|
|
32
|
+
* await startServer(server) // source/index.ts —— 启动
|
|
33
|
+
* await generateOpenApi(server) // build/openapi-gen.ts —— 出 spec
|
|
34
|
+
* await createTestServer(server) // *.test.ts —— 测试
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ## 两条容易踩的规矩
|
|
38
|
+
*
|
|
39
|
+
* - **本函数不读 env、不建连、不读库**。放在模块顶层是安全的:`import { server }` 不会有副作用。
|
|
40
|
+
* (需要 env 的工具进程自己调 `loadRuntimeEnvironment()`。)
|
|
41
|
+
* - **`clients` 可省略**,其余槽位恒定:没有业务路由也要写 `routers: () => {}`。省略 `clients`
|
|
42
|
+
* → 空客户端包。其余槽位不留「可省略」分支,省掉一个就会让「这份定义到底声明了什么」需要靠猜。
|
|
43
|
+
*
|
|
44
|
+
* ## 扩展 env(`Env`):`Env` 是**第一个**泛型,类型声明,不影响加载
|
|
45
|
+
*
|
|
46
|
+
* `Env` 排在泛型首位,就是为了能**单独手写**:`defineServer<{ FOO?: string }>({ ... })` 把扩展字段
|
|
47
|
+
* 的类型并进 `ctx.env`(`resources` / `config` 的 `ctx.env` 上可读、有类型)。不写即 `Env = object`
|
|
48
|
+
* (无扩展,`ctx.env` 就是 `ConfigEnv`)。
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ **TypeScript 限制(务必知道)**:只要**显式写了 `Env`**,其余四个泛型
|
|
51
|
+
* (`Resources` / `Config` / `Clients` / `Services`)就**不再从工厂推导、一律退化为 `object`**
|
|
52
|
+
* —— 这是 TS「类型参数全有或全无」的规则,不是本函数能绕过的。后果:`defineServer<MyEnv>({...})`
|
|
53
|
+
* 产出的 `server` 上,`services` 等类型会塌成 `object`,`testServer.services.xxx` 之类会丢类型。
|
|
54
|
+
*
|
|
55
|
+
* 因此两种写法各有适用:
|
|
56
|
+
* - **要完整推导**(典型业务服务,依赖 `ctx.services` / `testServer.services` 的类型):**不写泛型**,
|
|
57
|
+
* 把扩展 env 的类型写在**工厂参数标注**上——`resources: (ctx: ResourcesContext<{ FOO?: string }>) => ...`
|
|
58
|
+
* 或 point-free 传入参数已标注的命名工厂。这样 `Env` 与其余泛型**一起被推导**,不丢类型。
|
|
59
|
+
* - **只在意 `Env`、不依赖其余被推导类型**(简单服务 / 测试):直接 `defineServer<MyEnv>({...})`。
|
|
60
|
+
*
|
|
61
|
+
* ⚠️ **只声明类型,不参与加载**:env 的加载完全走框架内置逻辑(dotenv 分层 + 进程注入),框架
|
|
62
|
+
* **不提供自定义加载**;运行期扩展字段的值就是内置加载后的 `process.env[key]`(字符串直通)。
|
|
63
|
+
*
|
|
64
|
+
* ```ts
|
|
65
|
+
* // 写法一(推荐业务服务用):不写泛型,Env 从工厂参数标注推导,其余泛型一起推导
|
|
66
|
+
* function createResources(ctx: ResourcesContext<{ REDIS_KEY_PREFIX?: string }>) { ... }
|
|
67
|
+
* export const server = defineServer({ app, resources: createResources, config, services, middlewares, routers })
|
|
68
|
+
*
|
|
69
|
+
* // 写法二:显式手写 Env(首位)——简单;但其余泛型退化为 object
|
|
70
|
+
* export const server = defineServer<{ REDIS_KEY_PREFIX?: string }>({ app, resources, config, services, middlewares, routers })
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export function defineServer(config) {
|
|
74
|
+
const server = {
|
|
75
|
+
...config,
|
|
76
|
+
__brand: SERVER_BRAND,
|
|
77
|
+
};
|
|
78
|
+
return server;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* `startServer()`——真正跑起来:构造资源、加载配置、建客户端、建服务、装中间件、注册路由、listen、装信号。
|
|
82
|
+
*
|
|
83
|
+
* 步序**就写在本函数里**,不抽到共享的「按模式分支」的装配器:
|
|
84
|
+
* ```
|
|
85
|
+
* env → resources → core → config → clients → services → services.init() → middlewares
|
|
86
|
+
* → systemRoutes → routers → onClose → ready → testdata → listen/signal
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* - 最早阶段**固定加载 env**(`.env.dev < .env < .env.local`;`ENV=test` 追加 `.env.test.local`)。
|
|
90
|
+
* - 返回**原生 `FastifyInstance`**:不包 `fastify-plugin`、不返回包装对象。
|
|
91
|
+
* - **一定 listen**:监听端口 + 装信号处理器是这个入口的定义,没有开关。不监听端口的是
|
|
92
|
+
* `createTestServer()`(`inject`),那是另一个入口——两个入口各自固定自己的行为,
|
|
93
|
+
* 而不是同一个入口按环境变量 / 选项改行为。
|
|
94
|
+
* - 硬性要求 `Server`(品牌)产物:传裸 `ServerConfig` 是编译错误。
|
|
95
|
+
*
|
|
96
|
+
* ## `development` / `test` 下会自动装载 `testdata/`
|
|
97
|
+
*
|
|
98
|
+
* 真启动(本地 `pnpm dev`)在 `ready()` 之后、**listen 之前**装载 `<cwd>/testdata` 下的脚本并整体
|
|
99
|
+
* `load()`(见 `test-support/testdata.ts`):端口一旦对外,数据必须已经在位。脚本拿到的是装配好的
|
|
100
|
+
* ctx(`env` + `resources` + `log`),所以它们写数据用的就是被测服务读数据的那份连接。
|
|
101
|
+
* `ENV` 不在 `CLEANABLE_ENVS` 里(即生产)时**整段跳过**——这是 `startServer` 的唯一一处环境
|
|
102
|
+
* 分支,且是安静的:生产不需要测试数据,也没有什么东西需要失败。
|
|
103
|
+
*
|
|
104
|
+
* `cleanup()` 在 `startServer` 路径上不会被调用:真启动要的就是「数据在」,不是「数据不在」。
|
|
105
|
+
*
|
|
106
|
+
* ## 怎么用
|
|
107
|
+
*
|
|
108
|
+
* ```ts
|
|
109
|
+
* // source/index.ts —— 整个进程的启动点,只做这一件事
|
|
110
|
+
* import { startServer } from '@taicode/common-server/app'
|
|
111
|
+
* import { server } from './server'
|
|
112
|
+
*
|
|
113
|
+
* const app = await startServer(server)
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* 你**不需要**写:加载 `.env`、创建 Fastify、注册响应信封 / cookie / OpenAPI / 系统路由、
|
|
117
|
+
* 装关闭钩子、装 `SIGTERM` / `SIGINT`。这些都由本函数固定完成,顺序写在下面的步序里。
|
|
118
|
+
*
|
|
119
|
+
* 你**需要**知道的两件事:
|
|
120
|
+
* - 端口只读 env 的 `API_PORT`,**不回退 `PORT`**(`PORT` 属 nginx)。`API_PORT=0` 让内核
|
|
121
|
+
* 分配空闲端口,多实例本地调试靠它。
|
|
122
|
+
* - 失败时已构造的资源会被按固定顺序释放(关闭流程八步),不会漏掉连接池;错误原样抛给
|
|
123
|
+
* 调用方——所以端口占用会让 `startServer` 直接 reject,需要在入口处接住。
|
|
124
|
+
*/
|
|
125
|
+
export async function startServer(server) {
|
|
126
|
+
const env = resolveAppEnv(loadEnvironment());
|
|
127
|
+
const ctx = createBootContext(server, env);
|
|
128
|
+
const { resources, units: resourceUnits, classifyResourceError } = await buildResources(ctx);
|
|
129
|
+
await setupResources(ctx, resourceUnits);
|
|
130
|
+
await registerCoreStep(ctx, classifyResourceError);
|
|
131
|
+
await buildConfig(ctx, resources);
|
|
132
|
+
const clients = await buildClients(ctx, resources);
|
|
133
|
+
const services = await buildServices(ctx, resources, clients);
|
|
134
|
+
await initServiceUnits({ service: ctx.service, log: ctx.log, services: services });
|
|
135
|
+
await registerMiddlewares(ctx, services, clients);
|
|
136
|
+
registerSystemRoutesStep(ctx, services);
|
|
137
|
+
await registerRouters(ctx, services, clients);
|
|
138
|
+
installShutdownHook(ctx, services, resourceUnits);
|
|
139
|
+
// 信号处理器「随 listen 装、随 close 卸」。但 Fastify 一旦 listening 就拒绝 `addHook`,
|
|
140
|
+
// 所以 onClose 里的 detach 必须先挂上,处理器本身等 listen 成功后再装。
|
|
141
|
+
let detachSignalHandlers = () => { };
|
|
142
|
+
ctx.app.addHook('onClose', () => {
|
|
143
|
+
detachSignalHandlers();
|
|
144
|
+
});
|
|
145
|
+
await ctx.app.ready();
|
|
146
|
+
// 服务已经完全就绪(路由 / 中间件 / 关闭钩子都装好),现在把开发用样本数据写进去,然后才开门。
|
|
147
|
+
// 顺序是**装配 → 数据 → listen**:端口一旦对外,第一条请求就必须能看到完整数据;反过来
|
|
148
|
+
// (先 listen 再 load)会留下一段「端口在响应、数据还没就位」的窗口——本地前端 / 集成脚本
|
|
149
|
+
// 恰好在这时请求,就是偶发的 404 / 空列表。
|
|
150
|
+
//
|
|
151
|
+
// 失败直接冒泡给调用方:`testdata/` 的脚本是服务能正常提供数据的前提,它失败时**不该**把端口
|
|
152
|
+
// 占着等人发现。非可清理环境(生产)整段跳过——这是本入口唯一的环境分支。
|
|
153
|
+
if (CLEANABLE_ENVS.includes(env.ENV)) {
|
|
154
|
+
const units = await discoverTestDataModules();
|
|
155
|
+
if (units.length > 0) {
|
|
156
|
+
// testdata 跑在 `ready()` 之后:`config` / `clients` / `services` 都已就绪,一并注入。
|
|
157
|
+
// `app` **不注入**(拿到它就能 `inject()`,等于用业务链路造数据,见 `TestDataContext`)。
|
|
158
|
+
const testDataCtx = {
|
|
159
|
+
env,
|
|
160
|
+
resources,
|
|
161
|
+
config: ctx.configAccess.read(),
|
|
162
|
+
clients,
|
|
163
|
+
services,
|
|
164
|
+
log: ctx.log,
|
|
165
|
+
};
|
|
166
|
+
await loadTestData({ ctx: testDataCtx, units, service: ctx.service });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
try {
|
|
170
|
+
await ctx.app.listen({ port: resolvePort(env), host: DEFAULT_HOST });
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
// 端口占用 / 地址不可用:把已构造的资源按固定八步释放掉,再把错误抛给调用方
|
|
174
|
+
await ctx.app.close();
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
detachSignalHandlers = attachSignalHandlers(ctx.app, ctx.log);
|
|
178
|
+
return ctx.app;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* `generateOpenApi()`——只出 spec。
|
|
182
|
+
*
|
|
183
|
+
* **文档只由「路由声明 + zod → JSON Schema 变换」决定**,因此这里只跑那两件事:
|
|
184
|
+
* ```
|
|
185
|
+
* 最小 Fastify(无 logger / 无 requestId)
|
|
186
|
+
* → schema 契约(zod compiler + 路由守卫)+ swagger
|
|
187
|
+
* → 5 条系统路由 → routers
|
|
188
|
+
* → ready → swagger() → close()
|
|
189
|
+
* ```
|
|
190
|
+
*
|
|
191
|
+
* **不构造 `resources` / `config` / `clients` / `services`**:它们存在的意义是「让服务跑起来」
|
|
192
|
+
* (连接池、配置、外部客户端、业务单元及其 `init()`),而构建期不跑服务。
|
|
193
|
+
*
|
|
194
|
+
* **文档来源只有两处**:`routers` 与系统路由。其他槽位结构上就拿不到 `app`
|
|
195
|
+
* (`resources` / `config` / `clients` / `services` 的 ctx 里**没有 `app`**),因此根本无法
|
|
196
|
+
* 注册路由;于是也不需要执行它们去「发现」什么——那本来就不可能发现出接口。
|
|
197
|
+
*
|
|
198
|
+
* 代价是一条必须遵守的契约:`routers` / 系统路由在**注册期不得读取 `services` / `clients`**
|
|
199
|
+
* (只能捕获进 handler、请求期使用)。这与既有规则 B 同属一类——构造期本来就不允许
|
|
200
|
+
* 查库 / 建连 / 发请求。
|
|
201
|
+
*
|
|
202
|
+
* **红线**:全程**不得要求配置有效** —— 不跑 `config` / `clients` 槽位、不要求 `DATABASE_URL` /
|
|
203
|
+
* `REDIS_URL` 有效;`ctx.config` / `ctx.clients` 读到即抛 `CONFIG_UNAVAILABLE`。
|
|
204
|
+
*
|
|
205
|
+
* **没有关闭流程**:`init()` 与建连都没发生,无物可 flush;`close()` 只为释放 Fastify
|
|
206
|
+
* 自身句柄。
|
|
207
|
+
*
|
|
208
|
+
* ## 怎么用
|
|
209
|
+
*
|
|
210
|
+
* ```ts
|
|
211
|
+
* // build/openapi-gen.ts
|
|
212
|
+
* import { writeFile } from 'node:fs/promises'
|
|
213
|
+
* import { generateOpenApi } from '@taicode/common-server/app'
|
|
214
|
+
* import { server } from '../source/server'
|
|
215
|
+
*
|
|
216
|
+
* const document = await generateOpenApi(server)
|
|
217
|
+
* await writeFile('openapi.json', JSON.stringify(document, null, 2))
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* 这一段在**构建期**跑,因此必须能离线成功:不需要数据库、不需要 Redis、不需要任何 env。
|
|
221
|
+
* 如果你的 `routers` 里写了 `services.xxx`(而不是只把它捕获进 handler),这条路会抛
|
|
222
|
+
* `CONFIG_UNAVAILABLE`——那不是 bug,是在提醒「路由注册期不该依赖运行期产物」。
|
|
223
|
+
*/
|
|
224
|
+
export async function generateOpenApi(server) {
|
|
225
|
+
// 不加载 .env 文件、不读配置、不跑 `resources` / `config` 槽位(出 spec 不要求 env 有效,也不构造 resources / config)
|
|
226
|
+
const env = resolveConfigEnv();
|
|
227
|
+
const ctx = createDocumentContext(server, env);
|
|
228
|
+
try {
|
|
229
|
+
await registerDocumentCoreStep(ctx);
|
|
230
|
+
registerDocumentSystemRoutesStep(ctx);
|
|
231
|
+
await registerDocumentRoutersStep(ctx);
|
|
232
|
+
await ctx.app.ready();
|
|
233
|
+
const swagger = ctx.app.swagger;
|
|
234
|
+
if (typeof swagger !== 'function') {
|
|
235
|
+
throw new Error('[generateOpenApi] @fastify/swagger is not registered');
|
|
236
|
+
}
|
|
237
|
+
return swagger.call(ctx.app);
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
await ctx.app.close();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entries.test.d.ts","sourceRoot":"","sources":["../../source/core/entries.test.ts"],"names":[],"mappings":""}
|