@taicode/common-server 4.0.0-beta.1 → 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.
Files changed (292) hide show
  1. package/output/app/index.d.ts +18 -15
  2. package/output/app/index.d.ts.map +1 -1
  3. package/output/app/index.js +330 -18
  4. package/output/auth/index.d.ts +73 -0
  5. package/output/auth/index.d.ts.map +1 -0
  6. package/output/auth/index.js +196 -0
  7. package/output/core/assemble.d.ts +64 -0
  8. package/output/core/assemble.d.ts.map +1 -0
  9. package/output/core/assemble.js +88 -0
  10. package/output/core/boot/context.d.ts +109 -0
  11. package/output/core/boot/context.d.ts.map +1 -0
  12. package/output/core/boot/context.js +145 -0
  13. package/output/core/boot/lifecycle.d.ts +206 -0
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/core/boot/lifecycle.js +255 -0
  16. package/output/core/boot/shutdown.d.ts +34 -0
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/core/boot/shutdown.js +50 -0
  19. package/output/core/boot/signal.d.ts +13 -0
  20. package/output/core/boot/signal.d.ts.map +1 -0
  21. package/output/{app → core/boot}/signal.js +3 -2
  22. package/output/core/boot/steps.d.ts +138 -0
  23. package/output/core/boot/steps.d.ts.map +1 -0
  24. package/output/core/boot/steps.js +259 -0
  25. package/output/core/entries.d.ts +151 -0
  26. package/output/core/entries.d.ts.map +1 -0
  27. package/output/core/entries.js +242 -0
  28. package/output/core/entries.test.d.ts +2 -0
  29. package/output/core/entries.test.d.ts.map +1 -0
  30. package/output/core/entries.test.js +458 -0
  31. package/output/core/env.d.ts +83 -0
  32. package/output/core/env.d.ts.map +1 -0
  33. package/output/core/env.js +124 -0
  34. package/output/{app → core}/env.test.d.ts.map +1 -1
  35. package/output/core/env.test.js +105 -0
  36. package/output/core/modules/auth-provider/index.d.ts +10 -0
  37. package/output/core/modules/auth-provider/index.d.ts.map +1 -0
  38. package/output/core/modules/auth-provider/index.js +9 -0
  39. package/output/core/modules/auth-provider/types.d.ts +71 -0
  40. package/output/core/modules/auth-provider/types.d.ts.map +1 -0
  41. package/output/core/modules/context/index.d.ts +18 -0
  42. package/output/core/modules/context/index.d.ts.map +1 -0
  43. package/output/core/modules/context/index.js +23 -0
  44. package/output/core/modules/context/index.test.d.ts.map +1 -0
  45. package/output/core/modules/cookie/index.d.ts +21 -0
  46. package/output/core/modules/cookie/index.d.ts.map +1 -0
  47. package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
  48. package/output/core/modules/identity/index.d.ts +4 -0
  49. package/output/core/modules/identity/index.d.ts.map +1 -0
  50. package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
  51. package/output/core/modules/logging/index.d.ts +5 -0
  52. package/output/core/modules/logging/index.d.ts.map +1 -0
  53. package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
  54. package/output/core/modules/openapi/index.d.ts +7 -0
  55. package/output/core/modules/openapi/index.d.ts.map +1 -0
  56. package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
  57. package/output/core/modules/permission-operations/index.d.ts +26 -0
  58. package/output/core/modules/permission-operations/index.d.ts.map +1 -0
  59. package/output/core/modules/permission-operations/index.js +36 -0
  60. package/output/core/modules/permission-operations/index.test.d.ts +2 -0
  61. package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
  62. package/output/core/modules/permission-operations/index.test.js +45 -0
  63. package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
  64. package/output/core/modules/request-id/index.d.ts.map +1 -0
  65. package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
  66. package/output/core/modules/response/index.d.ts +50 -0
  67. package/output/core/modules/response/index.d.ts.map +1 -0
  68. package/output/{app/core/response.js → core/modules/response/index.js} +31 -18
  69. package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
  70. package/output/core/modules/schema/builders.d.ts.map +1 -0
  71. package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
  72. package/output/core/modules/schema/index.d.ts +34 -0
  73. package/output/core/modules/schema/index.d.ts.map +1 -0
  74. package/output/core/modules/schema/index.js +62 -0
  75. package/output/core/modules/schema/index.test.d.ts +2 -0
  76. package/output/core/modules/schema/index.test.d.ts.map +1 -0
  77. package/output/core/modules/schema/index.test.js +55 -0
  78. package/output/core/modules/system-routes/index.d.ts +15 -0
  79. package/output/core/modules/system-routes/index.d.ts.map +1 -0
  80. package/output/core/modules/system-routes/index.js +205 -0
  81. package/output/core/resource.d.ts +56 -0
  82. package/output/core/resource.d.ts.map +1 -0
  83. package/output/core/resource.js +56 -0
  84. package/output/core/test-support/database.d.ts +89 -0
  85. package/output/core/test-support/database.d.ts.map +1 -0
  86. package/output/core/test-support/database.js +131 -0
  87. package/output/core/test-support/database.test.d.ts +2 -0
  88. package/output/core/test-support/database.test.d.ts.map +1 -0
  89. package/output/core/test-support/database.test.js +111 -0
  90. package/output/core/test-support/environment.d.ts +38 -0
  91. package/output/core/test-support/environment.d.ts.map +1 -0
  92. package/output/core/test-support/environment.js +45 -0
  93. package/output/core/test-support/service-context.d.ts +64 -0
  94. package/output/core/test-support/service-context.d.ts.map +1 -0
  95. package/output/core/test-support/service-context.js +63 -0
  96. package/output/core/test-support/service-context.test.d.ts +2 -0
  97. package/output/core/test-support/service-context.test.d.ts.map +1 -0
  98. package/output/core/test-support/service-context.test.js +55 -0
  99. package/output/core/test-support/test-server.d.ts +154 -0
  100. package/output/core/test-support/test-server.d.ts.map +1 -0
  101. package/output/core/test-support/test-server.js +114 -0
  102. package/output/core/test-support/test-server.test.d.ts +2 -0
  103. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  104. package/output/core/test-support/test-server.test.js +296 -0
  105. package/output/core/test-support/testdata.d.ts +255 -0
  106. package/output/core/test-support/testdata.d.ts.map +1 -0
  107. package/output/core/test-support/testdata.js +271 -0
  108. package/output/core/test-support/testdata.test.d.ts +2 -0
  109. package/output/core/test-support/testdata.test.d.ts.map +1 -0
  110. package/output/core/test-support/testdata.test.js +334 -0
  111. package/output/{app → core}/type-augment.d.ts +2 -2
  112. package/output/core/type-augment.d.ts.map +1 -0
  113. package/output/core/types.d.ts +582 -0
  114. package/output/core/types.d.ts.map +1 -0
  115. package/output/cors/index.d.ts +3 -0
  116. package/output/cors/index.d.ts.map +1 -0
  117. package/output/cors/index.js +28 -0
  118. package/output/index.d.ts +10 -4
  119. package/output/index.d.ts.map +1 -1
  120. package/output/index.js +25 -5
  121. package/output/rate-limit/index.d.ts +23 -0
  122. package/output/rate-limit/index.d.ts.map +1 -0
  123. package/output/rate-limit/index.js +43 -0
  124. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  125. package/output/rate-limit/limiter.d.ts.map +1 -0
  126. package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
  127. package/output/rate-limit/memory-store.d.ts.map +1 -0
  128. package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
  129. package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
  130. package/output/rate-limit/redis-store.d.ts.map +1 -0
  131. package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
  132. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  133. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  134. package/output/rate-limit/types.d.ts +45 -0
  135. package/output/rate-limit/types.d.ts.map +1 -0
  136. package/output/rate-limit/types.js +1 -0
  137. package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
  138. package/output/redis/cache.d.ts.map +1 -0
  139. package/output/redis/cache.test.d.ts +2 -0
  140. package/output/redis/cache.test.d.ts.map +1 -0
  141. package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
  142. package/output/{app/redis.d.ts → redis/index.d.ts} +12 -7
  143. package/output/redis/index.d.ts.map +1 -0
  144. package/output/{app/redis.js → redis/index.js} +15 -5
  145. package/output/redis/index.test.d.ts +2 -0
  146. package/output/{context → redis}/index.test.d.ts.map +1 -1
  147. package/output/redis/index.test.js +55 -0
  148. package/output/redis-queue/index.js +1 -1
  149. package/output/redis-queue/test-helpers.d.ts +1 -1
  150. package/output/redis-queue/test-helpers.js +1 -1
  151. package/output/redis-queue/types.d.ts +3 -12
  152. package/output/redis-queue/types.d.ts.map +1 -1
  153. package/package.json +5 -5
  154. package/output/app/auth.test.d.ts +0 -2
  155. package/output/app/auth.test.d.ts.map +0 -1
  156. package/output/app/auth.test.js +0 -148
  157. package/output/app/boot.d.ts +0 -48
  158. package/output/app/boot.d.ts.map +0 -1
  159. package/output/app/boot.js +0 -123
  160. package/output/app/config.test.d.ts +0 -2
  161. package/output/app/config.test.d.ts.map +0 -1
  162. package/output/app/config.test.js +0 -132
  163. package/output/app/context.d.ts +0 -68
  164. package/output/app/context.d.ts.map +0 -1
  165. package/output/app/context.js +0 -73
  166. package/output/app/core/cookie.d.ts +0 -25
  167. package/output/app/core/cookie.d.ts.map +0 -1
  168. package/output/app/core/identity.d.ts +0 -12
  169. package/output/app/core/identity.d.ts.map +0 -1
  170. package/output/app/core/index.d.ts +0 -44
  171. package/output/app/core/index.d.ts.map +0 -1
  172. package/output/app/core/index.js +0 -57
  173. package/output/app/core/logging.d.ts +0 -13
  174. package/output/app/core/logging.d.ts.map +0 -1
  175. package/output/app/core/openapi.d.ts +0 -17
  176. package/output/app/core/openapi.d.ts.map +0 -1
  177. package/output/app/core/request-id.d.ts.map +0 -1
  178. package/output/app/core/response.d.ts +0 -41
  179. package/output/app/core/response.d.ts.map +0 -1
  180. package/output/app/define-server.negative-cases.d.ts +0 -32
  181. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  182. package/output/app/define-server.negative-cases.js +0 -209
  183. package/output/app/define-server.test.d.ts +0 -2
  184. package/output/app/define-server.test.d.ts.map +0 -1
  185. package/output/app/define-server.test.js +0 -97
  186. package/output/app/entries.d.ts +0 -29
  187. package/output/app/entries.d.ts.map +0 -1
  188. package/output/app/entries.js +0 -64
  189. package/output/app/env.d.ts +0 -59
  190. package/output/app/env.d.ts.map +0 -1
  191. package/output/app/env.js +0 -66
  192. package/output/app/env.test.js +0 -124
  193. package/output/app/error-contract.test.d.ts +0 -2
  194. package/output/app/error-contract.test.d.ts.map +0 -1
  195. package/output/app/error-contract.test.js +0 -125
  196. package/output/app/errors.d.ts +0 -20
  197. package/output/app/errors.d.ts.map +0 -1
  198. package/output/app/errors.js +0 -51
  199. package/output/app/generate-openapi.test.d.ts +0 -2
  200. package/output/app/generate-openapi.test.d.ts.map +0 -1
  201. package/output/app/generate-openapi.test.js +0 -123
  202. package/output/app/lifecycle-logging.test.d.ts +0 -2
  203. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  204. package/output/app/lifecycle-logging.test.js +0 -155
  205. package/output/app/lifecycle.d.ts +0 -70
  206. package/output/app/lifecycle.d.ts.map +0 -1
  207. package/output/app/lifecycle.js +0 -99
  208. package/output/app/lifecycle.test.d.ts +0 -2
  209. package/output/app/lifecycle.test.d.ts.map +0 -1
  210. package/output/app/lifecycle.test.js +0 -152
  211. package/output/app/middleware/auth.d.ts +0 -71
  212. package/output/app/middleware/auth.d.ts.map +0 -1
  213. package/output/app/middleware/auth.js +0 -190
  214. package/output/app/middleware/context.d.ts +0 -13
  215. package/output/app/middleware/context.d.ts.map +0 -1
  216. package/output/app/middleware/context.js +0 -15
  217. package/output/app/middleware/cors.d.ts +0 -14
  218. package/output/app/middleware/cors.d.ts.map +0 -1
  219. package/output/app/middleware/cors.js +0 -30
  220. package/output/app/middleware/index.d.ts +0 -17
  221. package/output/app/middleware/index.d.ts.map +0 -1
  222. package/output/app/middleware/index.js +0 -16
  223. package/output/app/middleware/rate-limit.d.ts +0 -24
  224. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  225. package/output/app/middleware/rate-limit.js +0 -46
  226. package/output/app/middleware-order.test.d.ts +0 -2
  227. package/output/app/middleware-order.test.d.ts.map +0 -1
  228. package/output/app/middleware-order.test.js +0 -148
  229. package/output/app/permission-operations.d.ts +0 -48
  230. package/output/app/permission-operations.d.ts.map +0 -1
  231. package/output/app/permission-operations.js +0 -77
  232. package/output/app/permission-operations.test.d.ts +0 -2
  233. package/output/app/permission-operations.test.d.ts.map +0 -1
  234. package/output/app/permission-operations.test.js +0 -72
  235. package/output/app/permission-registration.test.d.ts +0 -2
  236. package/output/app/permission-registration.test.d.ts.map +0 -1
  237. package/output/app/permission-registration.test.js +0 -138
  238. package/output/app/redis.d.ts.map +0 -1
  239. package/output/app/registered-operations.test.d.ts +0 -2
  240. package/output/app/registered-operations.test.d.ts.map +0 -1
  241. package/output/app/registered-operations.test.js +0 -57
  242. package/output/app/request-id.test.d.ts +0 -2
  243. package/output/app/request-id.test.d.ts.map +0 -1
  244. package/output/app/request-id.test.js +0 -130
  245. package/output/app/resources.test.d.ts +0 -2
  246. package/output/app/resources.test.d.ts.map +0 -1
  247. package/output/app/resources.test.js +0 -197
  248. package/output/app/shutdown.d.ts +0 -22
  249. package/output/app/shutdown.d.ts.map +0 -1
  250. package/output/app/shutdown.js +0 -30
  251. package/output/app/shutdown.test.d.ts +0 -2
  252. package/output/app/shutdown.test.d.ts.map +0 -1
  253. package/output/app/shutdown.test.js +0 -103
  254. package/output/app/signal.d.ts +0 -12
  255. package/output/app/signal.d.ts.map +0 -1
  256. package/output/app/smoke.test.d.ts +0 -2
  257. package/output/app/smoke.test.d.ts.map +0 -1
  258. package/output/app/smoke.test.js +0 -210
  259. package/output/app/test-support.d.ts +0 -21
  260. package/output/app/test-support.d.ts.map +0 -1
  261. package/output/app/test-support.js +0 -24
  262. package/output/app/type-augment.d.ts.map +0 -1
  263. package/output/app/types.d.ts +0 -211
  264. package/output/app/types.d.ts.map +0 -1
  265. package/output/context/index.d.ts +0 -6
  266. package/output/context/index.d.ts.map +0 -1
  267. package/output/context/index.js +0 -14
  268. package/output/rate-limiter/index.d.ts +0 -5
  269. package/output/rate-limiter/index.d.ts.map +0 -1
  270. package/output/rate-limiter/index.js +0 -4
  271. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  272. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  273. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  274. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  275. package/output/rate-limiter/types.d.ts +0 -27
  276. package/output/rate-limiter/types.d.ts.map +0 -1
  277. package/output/redis-cache/index.d.ts +0 -4
  278. package/output/redis-cache/index.d.ts.map +0 -1
  279. package/output/redis-cache/index.js +0 -1
  280. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  281. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  282. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  283. package/output/schema/index.d.ts.map +0 -1
  284. /package/output/{app → core}/env.test.d.ts +0 -0
  285. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  286. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  287. /package/output/{context → core/modules/context}/index.test.js +0 -0
  288. /package/output/{app → core}/type-augment.js +0 -0
  289. /package/output/{app → core}/types.js +0 -0
  290. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  291. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  292. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -0,0 +1,582 @@
1
+ import type { Disposer, SystemError } from '@taicode/common-base';
2
+ import type { FastifyBaseLogger, FastifyInstance, RawServerDefault } from 'fastify';
3
+ import type { ZodTypeProvider } from 'fastify-type-provider-zod';
4
+ import type { IncomingMessage, ServerResponse } from 'node:http';
5
+ import type { ConfigEnv } from './env.js';
6
+ /**
7
+ * `defineServer` 槽位口径与接口类型(N1 / N2,**唯一规格来源**)。
8
+ *
9
+ * 顶层是「一个身份块 + 工厂槽位」,**键顺序即分层**(`clients` 可选):
10
+ *
11
+ * ```
12
+ * app (身份块,无依赖)
13
+ * resources ← env 引导期:唯一读 env 的地方
14
+ * config ← env + resources 引导期:❗不给 services(结构性解耦)
15
+ * clients ← resources + config(可选) 外部服务客户端:❗不给 env / services / app
16
+ * services ← resources + config + clients 运行期开始:❗不给 env、不给 services
17
+ * middlewares ← app + services + config + clients (不给 resources / env);**有序数组**
18
+ * routers ← app + services + clients (不给 resources / env / config)
19
+ * ```
20
+ *
21
+ * 应用扩展 env(`Env`)**不是槽位**,而是 `defineServer` 的**第一个泛型**:可**单独手写**
22
+ * `defineServer<{ FOO?: string }>({...})`,也可不写、从 `resources` / `config` 的 `ctx` 类型标注
23
+ * 推导(`ResourcesContext<Env>`)。⚠️ 手写 `Env` 会让其余泛型停止推导(退化为 `object`,TS 限制),
24
+ * 详见 `defineServer` 文档。加载仍走内置逻辑,框架不提供自定义加载。
25
+ *
26
+ * 槽位只能引用**排在它前面**的产物(向下引用是编译错误);`env` 只属引导期
27
+ * (`resources` / `config` 可见,运行期三层一律看不到)。
28
+ *
29
+ * **`resources` 与 `clients` 的语义分界**:`resources` = **我们拥有的基础设施**(DB / Redis,
30
+ * 连接串来自 env);`clients` = **我们调用的外部服务**(地址 / 凭证来自业务配置)。
31
+ * 后者必然依赖 `config`,所以结构上只能排在 `config` 之后——这也解释了为什么不能把它
32
+ * 并进 `resources`。
33
+ *
34
+ * **`disposer` 是每个槽位 ctx 的标配**(不是某个槽位专属):在**创建某个东西的地方**当场
35
+ * `ctx.disposer.add(() => 释放它)`,创建与释放同处一地;`disposer` 是**出口**(不产出任何依赖),
36
+ * 因此不会造成循环,也不削弱「只给该阶段该看的字段」的约束。
37
+ *
38
+ * 生命周期:每个阶段一个独立的 `Disposer` 实例;关闭时**按阶段逆序 flush**
39
+ * `routers → middlewares → services → clients → config → resources`(services 阶段内部:先 flush
40
+ * disposer,再逐单元逆序 `dispose`)。详见 `lifecycle.ts` / `shutdown.ts`。
41
+ *
42
+ * 类型安全机制(N2 §1):
43
+ * - **M1 分阶段 ctx**:每个工厂只拿到「该阶段该看的字段」(**外加每个阶段都有的 `disposer`**);
44
+ * - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) => Resources`(不是 `Resources | Promise<Resources>`),下游一律
45
+ * `Awaited<Resources>` / `Awaited<Config>` / `Awaited<Clients>` / `Awaited<Services>`;
46
+ * - **M3 资源嵌套**:资源包挂在 `ctx.resources` 上(不再摊平到 ctx,键冲突从根上消失);
47
+ * - **M4 `Live<Value>` 静态开关**:资源包 / 客户端包里值为 `false` 的键在类型上剔除;
48
+ * - **M5 强类型 `services`**:`middlewares` / `routers` 的 `ctx.services` 是 `Services` 实体;
49
+ * - **M6 品牌**:`Server` 带 `__brand`(`SERVER_BRAND`),只能由 `defineServer` 产出,入口函数据此拒收裸对象。
50
+ *
51
+ * 命名全拼:`requestId` / `database` / `configuration` / `service`(不用 `db` / `cfg`)。
52
+ */
53
+ /** 骨架只按 `(obj, msg)` 形态打日志;`app.log` 与 `@taicode/common-base` 的 `Logger` 都满足 */
54
+ export interface LoggerLike {
55
+ debug: (obj: unknown, msg?: string) => void;
56
+ info: (obj: unknown, msg?: string) => void;
57
+ warn: (obj: unknown, msg?: string) => void;
58
+ error: (obj: unknown, msg?: string) => void;
59
+ }
60
+ /**
61
+ * 路由级类型安全的载体:带 `ZodTypeProvider` 的 `FastifyInstance`。
62
+ *
63
+ * 裸 `FastifyInstance` 会让路由的 `params` / `body` / `query` 全是 `unknown`;带上 provider 后
64
+ * `request.params.id` 有类型。
65
+ */
66
+ export type AppInstance = FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse, FastifyBaseLogger, ZodTypeProvider>;
67
+ /**
68
+ * 身份块:非 env 派生,出 spec 路径也要用。
69
+ *
70
+ * `version` **必填、无默认值**(缺它就是编译错误,杜绝「悄悄兜底成 1.0.0」)。
71
+ */
72
+ export interface AppIdentity {
73
+ /** 权限注册键 / cache 前缀 / 失效通道前缀 / 日志标签 */
74
+ readonly title: string;
75
+ readonly service: string;
76
+ /** openapi `info.version` + 权限操作自注册;建议从 package.json 读入 */
77
+ readonly version: string;
78
+ }
79
+ /**
80
+ * Redis 句柄(惰性包装)。
81
+ *
82
+ * 框架**不操作** Redis 命令,只持有并在关闭时透传 dispose;构造时不建连、不 ping。
83
+ * 因此保持开放类型,各仓注入自己的客户端实现不受约束。
84
+ */
85
+ export interface RedisHandle {
86
+ client: unknown;
87
+ dispose: () => void | Promise<void>;
88
+ }
89
+ /**
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
+ * | `clean()` | 框架(仅测试入口) | 用例间清理,`app.close()` 之前 |
112
+ * | `dispose()` | 框架(关闭流程) | 进程关闭,最后一步 |
113
+ *
114
+ * 三者互不替代:`setup()` 是「连接前把地基铺好」(状态驱动的迁移 / 预热),`clean()` 清的是
115
+ * **数据行**(不碰结构),`dispose()` 断开的是**连接**(一次性)。
116
+ *
117
+ * ## 用例互间的样本数据不在资源上
118
+ *
119
+ * 「清表 + 重新插入样本数据」这伴事**不属于资源**:资源只知道有哪些表 / 哪些 key,不知道谁往里
120
+ * 写了什么。写在资源上就意味着新增一个 model 要回去改一次清库清单,漏改只会安静地变脏。
121
+ *
122
+ * 因此它单独做成一套机制:**`testdata/` 目录 + 每脚本自带 `load()`**,只在
123
+ * 可清理环境执行(见 `test-support/testdata.ts`)。
124
+ *
125
+ * `clean()` 不是这套机制,而是它的**前置条件**:`testdata/` 负责「必要的那几行」存在,
126
+ * `clean()` 负责「此外的一切」不存在。两者在 `TestServer.reprovision()` 里按固定顺序串起来。
127
+ *
128
+ * ```ts
129
+ * resources: (ctx) => ({
130
+ * database: defineResource(createPrisma(ctx.env.DATABASE_URL), {
131
+ * // 自动执行:startServer / createTestServer 都会调,生产与测试同一份
132
+ * async setup() { await pushSchema(ctx.env.DATABASE_URL) },
133
+ * dispose: () => prisma.$disconnect(),
134
+ * }),
135
+ * cache: ctx.env.REDIS_URL ? defineResource(createRedis(ctx.env.REDIS_URL)) : false,
136
+ * })
137
+ * ```
138
+ */
139
+ export interface ResourceUnit<Value> {
140
+ /** 真正的资源对象(框架不碰) */
141
+ readonly value: Value;
142
+ /**
143
+ * 装配期准备(建连验证、同步库结构、预热……)。
144
+ *
145
+ * **何时跑**:`resources` 槽位构造完成后、`config` 槽位之前,按**对象声明序**逐项 `await`。
146
+ * 排在 `config` 之前是硬要求:`config` 常从数据库读(如 `SystemConfig` 表),库没准备好
147
+ * 它就连不上。
148
+ *
149
+ * **谁跑**:框架。两个入口都调,应用永不手调;`generateOpenApi` 不调(出 spec 不构造资源)。
150
+ *
151
+ * **不要在这里写 `if (ENV === 'test')`**:准备动作本身应当是状态驱动的(迁移工具会自己比对
152
+ * 现状、只补差量),生产与测试跑同一份代码。需要区分环境时,区分点应当来自 `ctx.env`,
153
+ * 而不是「跑不跑这个函数」。
154
+ */
155
+ setup?(): void | Promise<void>;
156
+ /**
157
+ * 清掉本资源持有的**全部数据**,保留结构(**仅测试期调用**)。
158
+ *
159
+ * **何时跑**:`TestServer.reprovision()` 的第 0 步,此时连接还没断。清完后实例会被关掉重装,
160
+ * 所以不必考虑「清完还能不能用」。
161
+ *
162
+ * **不碰结构**:表 / 索引 / 序列的定义不归这里,它在 `setup()` 或 npm script 里建好。
163
+ * `clean()` 只清行——典型实现是 `TRUNCATE ... RESTART IDENTITY CASCADE`。
164
+ *
165
+ * **成功的前提是错在单元自己**:方言、表清单、级联策略都只有持有连接的单元知道,框架只认
166
+ * 「清干净」这个契约。不提供 `truncateTestSchema(url)` 这类代劳函数:一旦框架代判,就会
167
+ * 出现「只清某张表 / 某个 schema」的假合法场景。
168
+ *
169
+ * **失败冒泡**:清理没做干净就继续跑,后面全是伪绿。
170
+ */
171
+ clean?(): void | Promise<void>;
172
+ /**
173
+ * 关闭时释放本资源(断连、清池、flush……)。
174
+ *
175
+ * **何时跑**:关闭流程的最后一步(`resources` 阶段),按声明序**逆序**逐项 `await`——
176
+ * 后创建的先销毁;此时 `services` / `clients` / `config` 都已关完,不会再有使用方。
177
+ *
178
+ * **unit 级失败只记日志、不阻断其他资源**(关闭不能半途中断)。
179
+ */
180
+ dispose?(): void | Promise<void>;
181
+ }
182
+ /** 资源单元的生命周期(`defineResource` 的第二个参数) */
183
+ export interface ResourceLifecycle {
184
+ setup?(): void | Promise<void>;
185
+ clean?(): void | Promise<void>;
186
+ dispose?(): void | Promise<void>;
187
+ }
188
+ /**
189
+ * 构造资源单元(唯一的正当方式)。
190
+ *
191
+ * 实现见 `core/resource.ts`(`types.ts` 只放类型,不放运行时值)。
192
+ */
193
+ export type DefineResource = <Value>(value: Value, lifecycle?: ResourceLifecycle) => ResourceUnit<Value>;
194
+ /** 单元 → 值;非单元原样通过(`defineResource` 是可选的,裸对象照旧可用) */
195
+ type UnwrapResource<Value> = Value extends ResourceUnit<infer Inner> ? Inner : Value;
196
+ /**
197
+ * 剔掉「静态关闭」的键,并从保留键的类型里抹掉 `false` 分支(编译期静态开关)。
198
+ *
199
+ * 三种写法,三种语义,都被精确表达:
200
+ * - `cache: false`(**纯 `false`**)→ **静态关闭**:`Live<Value>` 里该键**不存在**,访问直接编译失败;
201
+ * - `cache: Cache | false`(**条件构造**)→ 键**保留**,但类型收窄为 `Cache`(`false` 分支被
202
+ * `Exclude` 抹掉)——运行期 `liveResources` 会剔掉值为 `false` 的那一项,所以类型与运行时一致;
203
+ * - `cache: Cache | undefined`(**显式可选**)→ 键存在、类型保持 `Cache | undefined`(`undefined`
204
+ * 表示「运行时缺省」,与「静态关闭」语义不同,不受影响)。
205
+ *
206
+ * 旧版 `Value[K] extends false ? never : K` 只能处理「纯 `false`」,遇到 `Cache | false` 会把
207
+ * 整个键保留且类型仍含 `false`(与运行时剔除行为不符)。现版用 `Exclude<Value[K], false>` 对齐。
208
+ */
209
+ export type Live<Value> = {
210
+ [K in keyof Value as [Exclude<Value[K], false>] extends [never] ? never : K]: Exclude<Value[K], false>;
211
+ };
212
+ /**
213
+ * 框架在 ctx 上暴露的资源视图(`Awaited<>` 归一 + `false` 键剔除 + **单元解包**)。
214
+ *
215
+ * 解包是必须的:`ctx.resources.database` 必须是那个客户端本身,不能是
216
+ * `ResourceUnit<PrismaClient>` —— 否则每个消费方都要多写一层 `.value`,而单元外壳
217
+ * 是框架用来挂 `setup` 的实现细节。
218
+ */
219
+ export type ResourcesView<Resources> = Live<{
220
+ [K in keyof Awaited<Resources>]: UnwrapResource<Awaited<Resources>[K]>;
221
+ }>;
222
+ /** 框架在 ctx 上暴露的客户端视图(口径同 `ResourcesView`) */
223
+ export type ClientsView<Clients> = Live<Awaited<Clients>>;
224
+ /**
225
+ * 应用扩展后的 env 类型(固定 env + 应用声明的扩展字段)。
226
+ *
227
+ * `ConfigEnv` 是框架**固定约定**的最小集合(`ENV` / `API_PORT` / ...),不接受业务字段;
228
+ * 业务字段(如 `REDIS_KEY_PREFIX`、第三方 token)由应用声明:`Env` 是 `defineServer` 的**第一个
229
+ * 泛型**,可**手写** `defineServer<{ FOO?: string }>({...})`,也可从 `resources` / `config` 工厂的
230
+ * `ctx` 类型标注(`ResourcesContext<Env>` / `ConfigContext<Env>`)推导;两种方式都让扩展字段并进
231
+ * `ctx.env`、有类型地访问。(手写 `Env` 的代价见 `defineServer` 文档:其余泛型会退化为 `object`。)
232
+ *
233
+ * ⚠️ **只是类型声明,不影响加载**:env 的加载完全走框架内置逻辑(dotenv 分层 + 进程注入,
234
+ * 见 `env.ts`),框架**不提供自定义加载**。运行期扩展字段的值就是内置加载后的 `process.env[key]`
235
+ * (字符串直通,不映射 / 不校验 / 不给默认值——要默认值 / 归一化在 `resources` 里自己做)。
236
+ *
237
+ * 这样各仓不必再往 `ctx.env` 上开「`process.env.XXX` 无类型后门」——用泛型或 ctx 标注声明即可。
238
+ *
239
+ * ```ts
240
+ * // 命名工厂:参数标注扩展 env,`defineServer` 从中推导 Env(point-free 最简)
241
+ * function createResources(ctx: ResourcesContext<{ REDIS_KEY_PREFIX?: string }>) {
242
+ * return { cache: createCache({ prefix: ctx.env.REDIS_KEY_PREFIX ?? 'account' }) }
243
+ * }
244
+ * const server = defineServer({ app, resources: createResources, config, services, middlewares, routers })
245
+ * ```
246
+ */
247
+ export type AppEnv<Env> = ConfigEnv & Env;
248
+ /**
249
+ * 资源错误分类器(响应层用)。
250
+ *
251
+ * **每个资源注册自己的分类器**(一个资源一个):只认领自己错误域内的错误,认领不了返回 `null` 交给
252
+ * 下一个。由服务仓在 `resources(ctx)` 里通过 `ctx.registerClassifyError(...)` 注册;可注册多个,
253
+ * 按注册顺序求值取第一个非 `null` 的结果;全部未命中 / 一个都没注册 → 响应层落 `UNKNOWN_ERROR` 兜底。
254
+ */
255
+ export type ErrorClassifier = (error: unknown) => SystemError | null;
256
+ /**
257
+ * `resources(ctx)` 的入参:**`env` + `log` + `disposer`**(外加槽位专属的注册能力)。
258
+ *
259
+ * 结构上排除 `config` / `services` / `app` → 「资源依赖配置」这类循环**编译期写不出来**。
260
+ * `disposer` 是**出口**(只能注册释放函数,不产出依赖),因此不构成新的循环面:
261
+ * 资源在构造处当场 `ctx.disposer.add(() => 释放它)`,关闭时框架最后 flush。
262
+ *
263
+ * ```ts
264
+ * resources: ({ env, log, disposer, registerClassifyError }) => {
265
+ * const database = new PrismaClient({ adapter: new PrismaPg({ connectionString: env.DATABASE_URL }) })
266
+ * disposer.add(() => database.$disconnect())
267
+ *
268
+ * // 本资源认领自己的错误域(不认领就返回 null 交给下一个)
269
+ * registerClassifyError((error) => (isPrismaError(error) ? new SystemError('DATABASE_ERROR') : null))
270
+ *
271
+ * return { database }
272
+ * }
273
+ * ```
274
+ */
275
+ export interface ResourcesContext<Env = object> {
276
+ readonly env: AppEnv<Env>;
277
+ readonly log: LoggerLike;
278
+ /**
279
+ * 本阶段的释放器:**在创建资源的地方当场注册释放**。
280
+ *
281
+ * 关闭序中 `resources` 阶段**最后** flush(所有其它阶段都还活着)。
282
+ */
283
+ readonly disposer: Disposer;
284
+ /**
285
+ * **每个资源注册自己的错误分类器**(响应层用)。
286
+ *
287
+ * 一个资源一个分类器,各管各的错误域:只认领自己域内的错误,认领不了返回 `null` 交给下一个。
288
+ * 可注册多个:按注册顺序求值,取第一个非 `null` 的结果;全部未命中 → 响应层落 `UNKNOWN_ERROR` 兜底。
289
+ * 本方法是 **resources 槽位专属能力**(其他槽位的 ctx 不提供)。
290
+ */
291
+ registerClassifyError(classifier: ErrorClassifier): void;
292
+ }
293
+ export type ResourcesFactory<Resources, Env = object> = (ctx: ResourcesContext<Env>) => Resources;
294
+ /**
295
+ * `config(ctx)` 的入参:`env` + `log` + `disposer` + 资源视图。
296
+ *
297
+ * **没有** `services`(此时尚未构造)/ `app`(此时尚未装配)。
298
+ *
299
+ * ```ts
300
+ * config: async ({ resources }) => {
301
+ * const row = await resources.database.systemConfig.findFirst() // 启动期一次读库
302
+ * return { cors: row?.cors ?? defaultCors, webhook: { url: row?.webhookUrl } }
303
+ * }
304
+ * ```
305
+ *
306
+ * 返回值是**启动期快照**:进程内不再变,没有热更新通道。要能随时改的开关放数据库、请求期现读。
307
+ */
308
+ export interface ConfigContext<Resources, Env = object> {
309
+ readonly env: AppEnv<Env>;
310
+ readonly log: LoggerLike;
311
+ /** 本阶段的释放器(关闭时在 services 之后、resources 之前 flush) */
312
+ readonly disposer: Disposer;
313
+ readonly resources: ResourcesView<Resources>;
314
+ }
315
+ export type ConfigFactory<Resources, Config, Env = object> = (ctx: ConfigContext<Resources, Env>) => Config;
316
+ /**
317
+ * `clients(ctx)` 的入参:`log` + `disposer` + `resources` + `config`。
318
+ *
319
+ * 与 `ServiceContext` 同一依赖集,差别只在**时序与语义**:`clients` 在 `services` 之前构造,
320
+ * 因此服务单元可以共享同一份外部客户端,而**不需要**破「service 之间禁止互依」。
321
+ *
322
+ * ❗**故意没有 `env`**(运行期不得读 env);❗**故意没有 `services`**(此时尚未构造);
323
+ * ❗**故意没有 `app`**(拿不到 app 就不可能注册路由)。
324
+ *
325
+ * 语义边界:`resources` = **我们拥有的基础设施**(连接串来自 env);`clients` = **我们调用的
326
+ * 外部服务**(地址 / 凭证来自业务配置,即 `config`)。这也是它必须排在 `config` 之后的原因。
327
+ *
328
+ * ```ts
329
+ * clients: ({ config, disposer, log }) => {
330
+ * const http = createHttpClient({ baseUrl: config.upstream.baseUrl, log })
331
+ * disposer.add(() => http.close())
332
+ * return { upstream: http }
333
+ * }
334
+ * ```
335
+ *
336
+ * 没有外部客户端时**省略本槽位**(框架默认空客户端包)——独立成层只为「分层依赖」
337
+ * (排在 `config` 之后、`services` 之前),不要求「必须出现」。
338
+ */
339
+ export interface ClientsContext<Resources, Config> {
340
+ readonly log: LoggerLike;
341
+ /** 本阶段的释放器(关闭时在 services 之后、config 之前 flush) */
342
+ readonly disposer: Disposer;
343
+ readonly resources: ResourcesView<Resources>;
344
+ readonly config: Awaited<Config>;
345
+ }
346
+ export type ClientsFactory<Resources, Config, Clients> = (ctx: ClientsContext<NoInfer<Resources>, NoInfer<Config>>) => Clients;
347
+ /**
348
+ * `services(ctx)` 的入参:`log` + `disposer` + `resources` + `config` + `clients`。
349
+ *
350
+ * ❗**故意没有 `env`**(服务层属运行期,不得读 env);❗**故意没有 `services`**(禁 service 互依)。
351
+ *
352
+ * ```ts
353
+ * services: ({ resources, config, clients, disposer, log }) => {
354
+ * const user = createUserService({ database: resources.database, config, log })
355
+ * const queue = createQueue({ redis: resources.redis }) // 常驻单元:自己挂 init / dispose
356
+ * disposer.add(() => queue.stop())
357
+ * return { user, queue }
358
+ * }
359
+ * ```
360
+ *
361
+ * 两个约束:
362
+ * - **不得返回原始类型值**(`string` / `number` / `boolean`)——那是配置,不是服务。
363
+ * - **service 之间不得互依**:要共享的东西放 `clients`;需要常驻资源的单元自己实现
364
+ * `init()` / `dispose()`(框架会在 `middlewares` 之前 `init`、关闭时逆序 `dispose`)。
365
+ */
366
+ export interface ServiceContext<Resources, Config, Clients = unknown> {
367
+ readonly log: LoggerLike;
368
+ /** 本阶段的释放器(关闭时**先** flush 它,再逐单元逆序 `dispose`) */
369
+ readonly disposer: Disposer;
370
+ readonly resources: ResourcesView<Resources>;
371
+ readonly config: Awaited<Config>;
372
+ readonly clients: ClientsView<Clients>;
373
+ }
374
+ /**
375
+ * 一个已构造好的中间件注册函数:接收 app,挂钩子 / 注册插件。
376
+ *
377
+ * 中间件**不参与生命周期**(要常驻资源就放 `services`);注册后不再暴露内部钩子名。
378
+ */
379
+ export type Middleware = (app: AppInstance) => void | Promise<void>;
380
+ /**
381
+ * 具名中间件单元:`register` + 一个仅用于日志 / 报错定位的 `name`。
382
+ *
383
+ * 标准中间件工厂(`createCorsMiddleware` / `createAuthMiddleware` / `createRateLimitMiddleware`)
384
+ * 直接返回带默认名字的单元,调用方把它们按顺序摆进数组即可,不必自己起名。
385
+ */
386
+ export interface MiddlewareUnit {
387
+ /** 名字:仅用于注册期日志 / 报错定位,**不参与生命周期、不影响注册序**(顺序由数组位置决定) */
388
+ readonly name: string;
389
+ /** 注册函数 */
390
+ readonly register: Middleware;
391
+ }
392
+ /**
393
+ * `middlewares` 数组里的一项:**具名单元**或**裸注册函数**。
394
+ *
395
+ * 裸函数会被框架补一个占位名(`middleware[i]`,`i` 为数组下标)用于日志;要可读的日志名
396
+ * 就返回 `MiddlewareUnit`(标准工厂已经这么做了)。
397
+ */
398
+ export type MiddlewareEntry = MiddlewareUnit | Middleware;
399
+ /**
400
+ * `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config` + `clients`。
401
+ *
402
+ * ❗**故意没有 `resources` / `env`** —— 中间件要访问数据层必须穿过 service。
403
+ *
404
+ * ```ts
405
+ * middlewares: (ctx) => [
406
+ * createCorsMiddleware(ctx),
407
+ * createAuthMiddleware(ctx, { client: createAuthClient(ctx.services) }),
408
+ * createRateLimitMiddleware(ctx, { store: ctx.services.rateLimitStore }),
409
+ * ]
410
+ * ```
411
+ */
412
+ export interface MiddlewareContext<Config, Services, Clients = unknown> {
413
+ readonly app: AppInstance;
414
+ readonly log: LoggerLike;
415
+ /** 本阶段的释放器(关闭时最早 flush:在 services / clients / config / resources 之前) */
416
+ readonly disposer: Disposer;
417
+ readonly services: Awaited<Services>;
418
+ readonly config: Awaited<Config>;
419
+ readonly clients: ClientsView<Clients>;
420
+ }
421
+ /**
422
+ * `middlewares` 返回一个**有序数组** —— **数组位置即注册序**,再无歧义。
423
+ *
424
+ * 为什么是数组而不是 `Record<string, Middleware>`:注册序是中间件的核心语义(cors 必须在
425
+ * auth 之前、auth 必须在限流之前),而对象键的枚举序由 JS 引擎决定(纯数字键还会被重排到
426
+ * 最前)——用「顺序由引擎决定、且有已知陷阱」的结构承载「必须严格有序」的东西本身自相矛盾。
427
+ * 数组把顺序交还给书写顺序,既直观又没有数字键坑。
428
+ *
429
+ * 每一项可以是标准工厂返回的具名单元(`MiddlewareUnit`),也可以是一个裸注册函数
430
+ * (`Middleware`,框架会补占位名用于日志)。
431
+ */
432
+ export type MiddlewaresFactory<Config, Services, Clients = unknown> = (ctx: MiddlewareContext<Config, Services, Clients>) => MiddlewareEntry[];
433
+ /**
434
+ * `routers(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `clients`。
435
+ *
436
+ * ❗**故意没有 `resources` / `env` / `config`** —— 路由**只能穿过 service**,绕不过层直连数据层。
437
+ *
438
+ * ```ts
439
+ * routers: ({ app, services }) => {
440
+ * app.register(createUserRouter(services.user))
441
+ * }
442
+ * ```
443
+ *
444
+ * ❗**注册期不得调用 `services` 的方法**(只能把 service 捕获进 handler、请求期使用):
445
+ * `generateOpenApi` 走这条路径时不构造 `services`,注册期就读它会让出 spec 直接失败。
446
+ * 没业务路由也要写 `routers: () => {}`。
447
+ */
448
+ export interface RouterContext<Services, Clients = unknown> {
449
+ readonly app: AppInstance;
450
+ readonly log: LoggerLike;
451
+ /** 本阶段的释放器(关闭时**最先** flush) */
452
+ readonly disposer: Disposer;
453
+ readonly services: Awaited<Services>;
454
+ readonly clients: ClientsView<Clients>;
455
+ }
456
+ export type RoutersFactory<Services, Clients = unknown> = (ctx: RouterContext<Services, Clients>) => void | Promise<void>;
457
+ /**
458
+ * `services` 返回值形状闸门**的判据**(不是闸门本身):
459
+ * - 原始类型值(`string` / `number` / `boolean`)**拒绝**(防把配置塞进 services)。
460
+ *
461
+ * 注意施加位置:本类型**不再出现在 `services` 工厂的返回类型上**,只作为
462
+ * {@link ServicesGate} 的条件类型判据、由 `defineServer` 追加在**入参**上。
463
+ * 旧写法(挂在返回值上)会让 `Server` 产物无法回喂 `defineServer` 做组合定义,
464
+ * 详见 `ServicesGate` 的 docblock。
465
+ */
466
+ export type ServicesShape<Services> = {
467
+ [K in keyof Services]: Services[K] extends string | number | boolean ? never : Services[K];
468
+ };
469
+ /** 形状非法时被点名的那几个键(`string` / `number` / `boolean` 的键) */
470
+ export type InvalidServiceKeys<Services> = {
471
+ [K in keyof Services]: Services[K] extends string | number | boolean ? K : never;
472
+ }[keyof Services];
473
+ /**
474
+ * `services` 形状闸门的**施加位置**:`defineServer` 的**入参**,不是返回类型。
475
+ *
476
+ * ## 为什么不在返回类型上做交集
477
+ *
478
+ * 旧写法是 `services: (ctx) => Services & ServicesShape<Services>` —— 闸门挂在工厂**返回值**上。
479
+ * 它有两个代价,第二个是致命的:
480
+ *
481
+ * 1. 报错点远离原因:写错时错误落在「返回类型不可赋值」,指向具体的某个属性,而不是
482
+ * 「`services` 里不许放配置」。
483
+ * 2. **组合定义(composition)无法编译**。`Server` 产物的 `services` 类型里已经含一层
484
+ * `& ServicesShape<Services>`;再喂回 `defineServer({ ...server })` 时,新调用的
485
+ * `Services` 需要满足 `S & Shape<S> & Shape<S & Shape<S>>` —— `ServicesShape` 被套
486
+ * 两层,且带 `NoInfer<>` 的家庭成员使反向推断不可逆,`Services` 只能回落成 `object`。
487
+ * 后果:`middlewares` / `routers` 一夜之间拿不到 `ctx.services` 的类型。
488
+ * (框架文档自己推荐的模块级测试写法正是 `defineServer({ ...server, routers })`。)
489
+ *
490
+ * ## 现在的写法
491
+ *
492
+ * 工厂返回**裸 `Services`**(可以正常推导、spread 后也可以正常反推),闸门作为**入参的
493
+ * 条件类型**追加:
494
+ *
495
+ * ```ts
496
+ * config: ServerConfig<R, C, Cl, S, E> & ServicesGate<S>
497
+ * ```
498
+ *
499
+ * - 形状合法 → `unknown`,交集恒等,什么都不挡。
500
+ * - 形状非法 → 要求一个**不存在**的 `__services_invalid` 属性,编译失败,且报错文案里
501
+ * 直接点名违规的键(`{ readonly __services_invalid: "port" }`)。
502
+ *
503
+ * 用 `[Services] extends [...]` 的元组包裹是为了**关掉分布式条件类型**;否则联合类型的
504
+ * `Services` 会被拆开逐个判断。
505
+ */
506
+ export type ServicesGate<Services> = [Services] extends [ServicesShape<Services>] ? unknown : {
507
+ readonly __services_invalid: InvalidServiceKeys<Services>;
508
+ };
509
+ /**
510
+ * `defineServer()` 入参:一个身份块 + 工厂槽位(键顺序=分层)。
511
+ *
512
+ * ```
513
+ * app → resources(env) → config(env+resources) → (clients?)(resources+config)
514
+ * → services(resources+config+clients) → middlewares(app+services+config+clients)
515
+ * → routers(app+services+clients)
516
+ * ```
517
+ *
518
+ * `clients` 槽位**可选**(省略即空客户端包、`Clients` 退化为空)。
519
+ *
520
+ * **应用扩展 env(`Env`)不是槽位**,而是 `defineServer` 的**第一个泛型**(本接口里排最后一位,
521
+ * 但 `defineServer` 把它提到首位以便单独手写):手写 `defineServer<{ FOO?: string }>({...})`,或
522
+ * 不写、从 `resources` / `config` 的 `ctx` 类型标注推导。不声明即无扩展(`Env = object`)。
523
+ * 手写 `Env` 会让其余泛型退化为 `object`(TS 限制,详见 `defineServer`)。加载走内置逻辑。
524
+ *
525
+ * `Services` 上的形状闸门(`ServicesShape<Services>`,N2 §2.5)**不写在本接口上**,而是由
526
+ * `defineServer` 追加在入参上(`& ServicesGate<Services>`):本接口的 `services` 返回裸
527
+ * `Services`,保证类型可正常推导、且 `Server` 产物能原样喂回 `defineServer` 做组合定义。
528
+ * 详见 `ServicesGate`。
529
+ */
530
+ export interface ServerConfig<Resources, Config, Clients = object, Services = object, Env = object> {
531
+ app: AppIdentity;
532
+ /**
533
+ * 资源工厂。`ctx` 类型可写 `ResourcesContext<Env>` 声明应用扩展 env —— `Env` 从这里推导、
534
+ * 并进 `ctx.env`(见 `AppEnv`)。不声明即 `Env = object`(无扩展)。
535
+ */
536
+ resources: ResourcesFactory<Resources, Env>;
537
+ config: ConfigFactory<NoInfer<Resources>, Config, Env>;
538
+ /**
539
+ * 外部服务客户端(**我们调用的、地址/凭证来自业务配置的外部服务**)。
540
+ *
541
+ * 存在的唯一理由是「`services` 之间禁止互依」与「客户端需要 `config`」这两条约束
542
+ * 的交集:多个 service / middleware 必须共享同一份客户端,而它们之间没有共享通道。
543
+ *
544
+ * **可选**:没有外部客户端就**省略**本槽位(框架默认 `() => ({})`,`Clients` 退化为空)——
545
+ * 不再要求写一行仪式性的 `clients: () => ({})`。独立成层的意义只在于「分层依赖」
546
+ * (客户端排在 `config` 之后、`services` 之前),不在于「必须出现」。
547
+ */
548
+ clients?: ClientsFactory<NoInfer<Resources>, NoInfer<Config>, Clients>;
549
+ /**
550
+ * 业务单元工厂。
551
+ *
552
+ * 返回**裸 `Services`**:形状闸门(不许返回 `string` / `number` / `boolean`)在
553
+ * `defineServer` 的入参上一并施加(`ServicesGate`),不挂在返回类型上——否则
554
+ * `Server` 产物无法再喂回 `defineServer` 做组合定义。
555
+ *
556
+ * `Services` 在这里**故意不加 `NoInfer<>`**:中间件与路由的 `ctx.services` 依赖它的
557
+ * 推断结果,一旦反向推断被阻断,`Services` 会塌成 `object`。
558
+ */
559
+ services: (ctx: ServiceContext<NoInfer<Resources>, NoInfer<Config>, NoInfer<Clients>>) => Services;
560
+ middlewares: MiddlewaresFactory<NoInfer<Config>, Services, NoInfer<Clients>>;
561
+ /**
562
+ * 业务路由注册(**必填**)。
563
+ *
564
+ * 框架已自动注册 5 条系统路由(`/` / `/health` / `/system/health` / `/system/info` /
565
+ * `/system/openapi`,见 `core/modules/system-routes/`),应用侧无需再写 `createSystemRouter`;
566
+ * 本槽位只用于注册**业务**路由。没有业务路由的应用也必须显式传入(写 `() => {}`),
567
+ * 以保持槽位结构恒定、无「可省略」分支。
568
+ *
569
+ * `Services` 不加 `NoInfer<>`:理由同 `services` 槽位。
570
+ */
571
+ routers: RoutersFactory<Services, NoInfer<Clients>>;
572
+ }
573
+ /** 品牌字段值(`Server` 的 `__brand`) */
574
+ export declare const SERVER_BRAND = "taicode.server";
575
+ /** 纯定义产物:同一份定义可同时喂给「启动」与「出 spec」 */
576
+ export type Server<Resources, Config, Clients = object, Services = object, Env = object> = ServerConfig<Resources, Config, Clients, Services, Env> & {
577
+ readonly __brand: typeof SERVER_BRAND;
578
+ };
579
+ /** `generateOpenApi()` 的返回(OpenAPI 文档对象) */
580
+ export type OpenApiDocument = Record<string, unknown>;
581
+ export {};
582
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { MiddlewareContext, MiddlewareUnit } from '../core/types.js';
2
+ export declare function createCorsMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>): MiddlewareUnit;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,28 @@
1
+ import cors from '@fastify/cors';
2
+ import { readConfigKey } from '../core/boot/context.js';
3
+ export function createCorsMiddleware(ctx) {
4
+ const register = async (app) => {
5
+ let warned = false;
6
+ // 请求期现读:`cors` 配置变化立即生效;构造期不读
7
+ const resolveOrigins = () => {
8
+ const configured = readConfigKey(ctx.config, 'cors');
9
+ return configured?.enabled ? configured.origins : undefined;
10
+ };
11
+ await app.register(cors, {
12
+ origin: (origin, callback) => {
13
+ const origins = resolveOrigins();
14
+ if (origins === undefined || origins === '*') {
15
+ if (!warned) {
16
+ warned = true;
17
+ ctx.log.warn({}, '[middleware] cors origins not configured; allowing any origin');
18
+ }
19
+ callback(null, true);
20
+ return;
21
+ }
22
+ callback(null, origins.includes(origin ?? ''));
23
+ },
24
+ credentials: true,
25
+ });
26
+ };
27
+ return { name: 'cors', register };
28
+ }
package/output/index.d.ts CHANGED
@@ -1,6 +1,12 @@
1
- export * from './schema/index.js';
1
+ export * from './core/modules/schema/builders.js';
2
+ export { createSuccessResponse, createErrorResponse } from './core/modules/response/index.js';
3
+ export type { ResponseEnvelope, SuccessResponse } from './core/modules/response/index.js';
4
+ export { createContext } from './core/modules/context/index.js';
5
+ export type { RequestContext } from './core/modules/context/index.js';
6
+ export * from './redis/index.js';
7
+ export * from './rate-limit/limiter.js';
8
+ export * from './rate-limit/memory-store.js';
9
+ export * from './rate-limit/redis-store.js';
10
+ export * from './rate-limit/types.js';
2
11
  export * from './redis-queue/index.js';
3
- export * from './redis-cache/index.js';
4
- export * from './rate-limiter/index.js';
5
- export * from './context/index.js';
6
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AACA,cAAc,mBAAmB,CAAA;AACjC,cAAc,wBAAwB,CAAA;AACtC,cAAc,wBAAwB,CAAA;AACtC,cAAc,yBAAyB,CAAA;AACvC,cAAc,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../source/index.ts"],"names":[],"mappings":"AAiBA,cAAc,mCAAmC,CAAA;AACjD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAC7F,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAC/D,YAAY,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AACrE,cAAc,kBAAkB,CAAA;AAChC,cAAc,yBAAyB,CAAA;AACvC,cAAc,8BAA8B,CAAA;AAC5C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,uBAAuB,CAAA;AACrC,cAAc,wBAAwB,CAAA"}