@taicode/common-server 4.0.0-beta.3 → 4.0.0-beta.5

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 (270) hide show
  1. package/output/app/index.d.ts +13 -15
  2. package/output/app/index.d.ts.map +1 -1
  3. package/output/app/index.js +256 -18
  4. package/output/auth/index.d.ts +72 -0
  5. package/output/auth/index.d.ts.map +1 -0
  6. package/output/auth/index.js +176 -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 +89 -0
  11. package/output/core/boot/context.d.ts.map +1 -0
  12. package/output/{app → core/boot}/context.js +44 -17
  13. package/output/{app → core/boot}/lifecycle.d.ts +13 -15
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/{app → core/boot}/lifecycle.js +15 -12
  16. package/output/{app → core/boot}/shutdown.d.ts +9 -6
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/{app → core/boot}/shutdown.js +7 -4
  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 +107 -0
  23. package/output/core/boot/steps.d.ts.map +1 -0
  24. package/output/core/boot/steps.js +200 -0
  25. package/output/core/entries.d.ts +123 -0
  26. package/output/core/entries.d.ts.map +1 -0
  27. package/output/core/entries.js +193 -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 +217 -0
  31. package/output/core/env.d.ts +62 -0
  32. package/output/core/env.d.ts.map +1 -0
  33. package/output/core/env.js +96 -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 +22 -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} +22 -11
  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 +171 -0
  81. package/output/core/test-support/database.d.ts +96 -0
  82. package/output/core/test-support/database.d.ts.map +1 -0
  83. package/output/core/test-support/database.js +144 -0
  84. package/output/core/test-support/database.test.d.ts +2 -0
  85. package/output/core/test-support/database.test.d.ts.map +1 -0
  86. package/output/core/test-support/database.test.js +110 -0
  87. package/output/core/test-support/environment.d.ts +33 -0
  88. package/output/core/test-support/environment.d.ts.map +1 -0
  89. package/output/core/test-support/environment.js +42 -0
  90. package/output/core/test-support/test-server.d.ts +94 -0
  91. package/output/core/test-support/test-server.d.ts.map +1 -0
  92. package/output/core/test-support/test-server.js +60 -0
  93. package/output/core/test-support/test-server.test.d.ts +2 -0
  94. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  95. package/output/core/test-support/test-server.test.js +162 -0
  96. package/output/{app → core}/type-augment.d.ts +2 -2
  97. package/output/core/type-augment.d.ts.map +1 -0
  98. package/output/{app → core}/types.d.ts +153 -66
  99. package/output/core/types.d.ts.map +1 -0
  100. package/output/cors/index.d.ts +3 -0
  101. package/output/cors/index.d.ts.map +1 -0
  102. package/output/cors/index.js +27 -0
  103. package/output/index.d.ts +8 -4
  104. package/output/index.d.ts.map +1 -1
  105. package/output/index.js +24 -5
  106. package/output/rate-limit/index.d.ts +23 -0
  107. package/output/rate-limit/index.d.ts.map +1 -0
  108. package/output/rate-limit/index.js +42 -0
  109. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  110. package/output/rate-limit/limiter.d.ts.map +1 -0
  111. package/output/rate-limit/memory-store.d.ts.map +1 -0
  112. package/output/rate-limit/redis-store.d.ts.map +1 -0
  113. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  114. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  115. package/output/{rate-limiter → rate-limit}/types.d.ts +0 -1
  116. package/output/rate-limit/types.d.ts.map +1 -0
  117. package/output/rate-limit/types.js +1 -0
  118. package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
  119. package/output/redis/cache.d.ts.map +1 -0
  120. package/output/redis/cache.test.d.ts +2 -0
  121. package/output/redis/cache.test.d.ts.map +1 -0
  122. package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
  123. package/output/{app/redis.d.ts → redis/index.d.ts} +7 -6
  124. package/output/redis/index.d.ts.map +1 -0
  125. package/output/{app/redis.js → redis/index.js} +10 -4
  126. package/output/redis/index.test.d.ts +2 -0
  127. package/output/{context → redis}/index.test.d.ts.map +1 -1
  128. package/output/redis/index.test.js +55 -0
  129. package/output/redis-queue/index.js +1 -1
  130. package/output/redis-queue/test-helpers.d.ts +1 -1
  131. package/output/redis-queue/test-helpers.js +1 -1
  132. package/output/redis-queue/types.d.ts +3 -12
  133. package/output/redis-queue/types.d.ts.map +1 -1
  134. package/package.json +5 -5
  135. package/output/app/auth.test.d.ts +0 -2
  136. package/output/app/auth.test.d.ts.map +0 -1
  137. package/output/app/auth.test.js +0 -148
  138. package/output/app/boot.d.ts +0 -44
  139. package/output/app/boot.d.ts.map +0 -1
  140. package/output/app/boot.js +0 -158
  141. package/output/app/config.test.d.ts +0 -2
  142. package/output/app/config.test.d.ts.map +0 -1
  143. package/output/app/config.test.js +0 -132
  144. package/output/app/context.d.ts +0 -74
  145. package/output/app/context.d.ts.map +0 -1
  146. package/output/app/core/cookie.d.ts +0 -25
  147. package/output/app/core/cookie.d.ts.map +0 -1
  148. package/output/app/core/identity.d.ts +0 -12
  149. package/output/app/core/identity.d.ts.map +0 -1
  150. package/output/app/core/index.d.ts +0 -44
  151. package/output/app/core/index.d.ts.map +0 -1
  152. package/output/app/core/index.js +0 -57
  153. package/output/app/core/logging.d.ts +0 -13
  154. package/output/app/core/logging.d.ts.map +0 -1
  155. package/output/app/core/openapi.d.ts +0 -17
  156. package/output/app/core/openapi.d.ts.map +0 -1
  157. package/output/app/core/request-id.d.ts.map +0 -1
  158. package/output/app/core/response.d.ts +0 -41
  159. package/output/app/core/response.d.ts.map +0 -1
  160. package/output/app/define-server.negative-cases.d.ts +0 -40
  161. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  162. package/output/app/define-server.negative-cases.js +0 -268
  163. package/output/app/define-server.test.d.ts +0 -2
  164. package/output/app/define-server.test.d.ts.map +0 -1
  165. package/output/app/define-server.test.js +0 -97
  166. package/output/app/entries.d.ts +0 -31
  167. package/output/app/entries.d.ts.map +0 -1
  168. package/output/app/entries.js +0 -66
  169. package/output/app/env.d.ts +0 -59
  170. package/output/app/env.d.ts.map +0 -1
  171. package/output/app/env.js +0 -66
  172. package/output/app/env.test.js +0 -124
  173. package/output/app/error-contract.test.d.ts +0 -2
  174. package/output/app/error-contract.test.d.ts.map +0 -1
  175. package/output/app/error-contract.test.js +0 -227
  176. package/output/app/errors.d.ts +0 -20
  177. package/output/app/errors.d.ts.map +0 -1
  178. package/output/app/errors.js +0 -51
  179. package/output/app/generate-openapi.test.d.ts +0 -2
  180. package/output/app/generate-openapi.test.d.ts.map +0 -1
  181. package/output/app/generate-openapi.test.js +0 -123
  182. package/output/app/lifecycle-logging.test.d.ts +0 -2
  183. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  184. package/output/app/lifecycle-logging.test.js +0 -175
  185. package/output/app/lifecycle.d.ts.map +0 -1
  186. package/output/app/lifecycle.test.d.ts +0 -2
  187. package/output/app/lifecycle.test.d.ts.map +0 -1
  188. package/output/app/lifecycle.test.js +0 -153
  189. package/output/app/middleware/auth.d.ts +0 -71
  190. package/output/app/middleware/auth.d.ts.map +0 -1
  191. package/output/app/middleware/auth.js +0 -190
  192. package/output/app/middleware/context.d.ts +0 -13
  193. package/output/app/middleware/context.d.ts.map +0 -1
  194. package/output/app/middleware/context.js +0 -15
  195. package/output/app/middleware/cors.d.ts +0 -14
  196. package/output/app/middleware/cors.d.ts.map +0 -1
  197. package/output/app/middleware/cors.js +0 -30
  198. package/output/app/middleware/index.d.ts +0 -17
  199. package/output/app/middleware/index.d.ts.map +0 -1
  200. package/output/app/middleware/index.js +0 -16
  201. package/output/app/middleware/rate-limit.d.ts +0 -24
  202. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  203. package/output/app/middleware/rate-limit.js +0 -46
  204. package/output/app/middleware-order.test.d.ts +0 -2
  205. package/output/app/middleware-order.test.d.ts.map +0 -1
  206. package/output/app/middleware-order.test.js +0 -149
  207. package/output/app/permission-operations.d.ts +0 -48
  208. package/output/app/permission-operations.d.ts.map +0 -1
  209. package/output/app/permission-operations.js +0 -77
  210. package/output/app/permission-operations.test.d.ts +0 -2
  211. package/output/app/permission-operations.test.d.ts.map +0 -1
  212. package/output/app/permission-operations.test.js +0 -72
  213. package/output/app/permission-registration.test.d.ts +0 -2
  214. package/output/app/permission-registration.test.d.ts.map +0 -1
  215. package/output/app/permission-registration.test.js +0 -138
  216. package/output/app/redis.d.ts.map +0 -1
  217. package/output/app/registered-operations.test.d.ts +0 -2
  218. package/output/app/registered-operations.test.d.ts.map +0 -1
  219. package/output/app/registered-operations.test.js +0 -57
  220. package/output/app/request-id.test.d.ts +0 -2
  221. package/output/app/request-id.test.d.ts.map +0 -1
  222. package/output/app/request-id.test.js +0 -130
  223. package/output/app/resources.test.d.ts +0 -2
  224. package/output/app/resources.test.d.ts.map +0 -1
  225. package/output/app/resources.test.js +0 -222
  226. package/output/app/shutdown.d.ts.map +0 -1
  227. package/output/app/shutdown.test.d.ts +0 -2
  228. package/output/app/shutdown.test.d.ts.map +0 -1
  229. package/output/app/shutdown.test.js +0 -210
  230. package/output/app/signal.d.ts +0 -12
  231. package/output/app/signal.d.ts.map +0 -1
  232. package/output/app/smoke.test.d.ts +0 -2
  233. package/output/app/smoke.test.d.ts.map +0 -1
  234. package/output/app/smoke.test.js +0 -210
  235. package/output/app/test-support.d.ts +0 -21
  236. package/output/app/test-support.d.ts.map +0 -1
  237. package/output/app/test-support.js +0 -24
  238. package/output/app/type-augment.d.ts.map +0 -1
  239. package/output/app/types.d.ts.map +0 -1
  240. package/output/context/index.d.ts +0 -6
  241. package/output/context/index.d.ts.map +0 -1
  242. package/output/context/index.js +0 -14
  243. package/output/rate-limiter/index.d.ts +0 -5
  244. package/output/rate-limiter/index.d.ts.map +0 -1
  245. package/output/rate-limiter/index.js +0 -4
  246. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  247. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  248. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  249. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  250. package/output/rate-limiter/types.d.ts.map +0 -1
  251. package/output/redis-cache/index.d.ts +0 -4
  252. package/output/redis-cache/index.d.ts.map +0 -1
  253. package/output/redis-cache/index.js +0 -1
  254. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  255. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  256. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  257. package/output/schema/index.d.ts.map +0 -1
  258. /package/output/{app → core}/env.test.d.ts +0 -0
  259. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  260. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  261. /package/output/{context → core/modules/context}/index.test.js +0 -0
  262. /package/output/{app → core}/type-augment.js +0 -0
  263. /package/output/{app → core}/types.js +0 -0
  264. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  265. /package/output/{rate-limiter → rate-limit}/memory-store.d.ts +0 -0
  266. /package/output/{rate-limiter → rate-limit}/memory-store.js +0 -0
  267. /package/output/{rate-limiter → rate-limit}/redis-store.d.ts +0 -0
  268. /package/output/{rate-limiter → rate-limit}/redis-store.js +0 -0
  269. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  270. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -1,40 +1,45 @@
1
- import type { Disposer } from '@taicode/common-base';
1
+ import type { Disposer, SystemError } from '@taicode/common-base';
2
2
  import type { FastifyBaseLogger, FastifyInstance, RawServerDefault } from 'fastify';
3
3
  import type { ZodTypeProvider } from 'fastify-type-provider-zod';
4
4
  import type { IncomingMessage, ServerResponse } from 'node:http';
5
5
  import type { ConfigEnv } from './env.js';
6
- import type { SystemError } from './errors.js';
7
6
  /**
8
7
  * `defineServer` 槽位口径与接口类型(N1 / N2,**唯一规格来源**)。
9
8
  *
10
- * 顶层是「一个身份块 + 五个工厂槽位」,**键顺序即分层**:
9
+ * 顶层是「一个身份块 + 六个工厂槽位」,**键顺序即分层**:
11
10
  *
12
11
  * ```
13
12
  * app (身份块,无依赖)
14
- * resources ← env 引导期:唯一读 env 的地方
15
- * config ← env + resources 引导期:❗不给 services(结构性解耦)
16
- * services ← resources + config 运行期开始:❗不给 env、不给 services
17
- * middlewares ← app + services + config (不给 resources / env)
18
- * routers ← app + services (不给 resources / env / config)
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
19
  * ```
20
20
  *
21
21
  * 槽位只能引用**排在它前面**的产物(向下引用是编译错误);`env` 只属引导期
22
22
  * (`resources` / `config` 可见,运行期三层一律看不到)。
23
23
  *
24
+ * **`resources` 与 `clients` 的语义分界**:`resources` = **我们拥有的基础设施**(DB / Redis,
25
+ * 连接串来自 env);`clients` = **我们调用的外部服务**(地址 / 凭证来自业务配置)。
26
+ * 后者必然依赖 `config`,所以结构上只能排在 `config` 之后——这也解释了为什么不能把它
27
+ * 并进 `resources`。
28
+ *
24
29
  * **`disposer` 是每个槽位 ctx 的标配**(不是某个槽位专属):在**创建某个东西的地方**当场
25
30
  * `ctx.disposer.add(() => 释放它)`,创建与释放同处一地;`disposer` 是**出口**(不产出任何依赖),
26
31
  * 因此不会造成循环,也不削弱「只给该阶段该看的字段」的约束。
27
32
  *
28
33
  * 生命周期:每个阶段一个独立的 `Disposer` 实例;关闭时**按阶段逆序 flush**
29
- * `routers → middlewares → services → config → resources`(services 阶段内部:先 flush
34
+ * `routers → middlewares → services → clients → config → resources`(services 阶段内部:先 flush
30
35
  * disposer,再逐单元逆序 `dispose`)。详见 `lifecycle.ts` / `shutdown.ts`。
31
36
  *
32
37
  * 类型安全机制(N2 §1):
33
38
  * - **M1 分阶段 ctx**:每个工厂只拿到「该阶段该看的字段」(**外加每个阶段都有的 `disposer`**);
34
39
  * - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) => R`(不是 `R | Promise<R>`),下游一律
35
- * `Awaited<R>` / `Awaited<C>` / `Awaited<S>`;
40
+ * `Awaited<R>` / `Awaited<C>` / `Awaited<CL>` / `Awaited<S>`;
36
41
  * - **M3 资源嵌套**:资源包挂在 `ctx.resources` 上(不再摊平到 ctx,键冲突从根上消失);
37
- * - **M4 `Live<T>` 静态开关**:资源包里值为 `false` 的键在类型上剔除;
42
+ * - **M4 `Live<T>` 静态开关**:资源包 / 客户端包里值为 `false` 的键在类型上剔除;
38
43
  * - **M5 强类型 `services`**:`middlewares` / `routers` 的 `ctx.services` 是 `S` 实体;
39
44
  * - **M6 品牌 + 类型守卫**:`Server` 带品牌,`isServer()` 收窄;
40
45
  * - **M7 负向类型测试**:`define-server.negative-cases.ts`,由 `tsc --noEmit` 覆盖。
@@ -48,8 +53,6 @@ export interface LoggerLike {
48
53
  warn: (obj: unknown, msg?: string) => void;
49
54
  error: (obj: unknown, msg?: string) => void;
50
55
  }
51
- /** 同步或异步(`Awaited<>` 归一的原料) */
52
- export type MaybePromise<T> = T | Promise<T>;
53
56
  /**
54
57
  * 路由级类型安全的载体:带 `ZodTypeProvider` 的 `FastifyInstance`。
55
58
  *
@@ -90,6 +93,8 @@ export type Live<T> = {
90
93
  };
91
94
  /** 框架在 ctx 上暴露的资源视图(`Awaited<>` 归一 + `false` 键剔除) */
92
95
  export type ResourcesView<R> = Live<Awaited<R>>;
96
+ /** 框架在 ctx 上暴露的客户端视图(口径同 `ResourcesView`) */
97
+ export type ClientsView<CL> = Live<Awaited<CL>>;
93
98
  /**
94
99
  * 资源错误分类器(响应层用)。
95
100
  *
@@ -104,6 +109,18 @@ export type ErrorClassifier = (error: unknown) => SystemError | null;
104
109
  * 结构上排除 `config` / `services` / `app` → 「资源依赖配置」这类循环**编译期写不出来**。
105
110
  * `disposer` 是**出口**(只能注册释放函数,不产出依赖),因此不构成新的循环面:
106
111
  * 资源在构造处当场 `ctx.disposer.add(() => 释放它)`,关闭时框架最后 flush。
112
+ *
113
+ * ```ts
114
+ * resources: ({ env, log, disposer, registerClassifyError }) => {
115
+ * const database = new PrismaClient({ adapter: new PrismaPg({ connectionString: env.DATABASE_URL }) })
116
+ * disposer.add(() => database.$disconnect())
117
+ *
118
+ * // 本资源认领自己的错误域(不认领就返回 null 交给下一个)
119
+ * registerClassifyError((error) => (isPrismaError(error) ? new SystemError('DATABASE_ERROR') : null))
120
+ *
121
+ * return { database }
122
+ * }
123
+ * ```
107
124
  */
108
125
  export interface ResourcesContext {
109
126
  readonly env: ConfigEnv;
@@ -128,6 +145,15 @@ export type ResourcesFactory<R> = (ctx: ResourcesContext) => R;
128
145
  * `config(ctx)` 的入参:`env` + `log` + `disposer` + 资源视图。
129
146
  *
130
147
  * **没有** `services`(此时尚未构造)/ `app`(此时尚未装配)。
148
+ *
149
+ * ```ts
150
+ * config: async ({ resources }) => {
151
+ * const row = await resources.database.systemConfig.findFirst() // 启动期一次读库
152
+ * return { cors: row?.cors ?? defaultCors, webhook: { url: row?.webhookUrl } }
153
+ * }
154
+ * ```
155
+ *
156
+ * 返回值是**启动期快照**:进程内不再变,没有热更新通道。要能随时改的开关放数据库、请求期现读。
131
157
  */
132
158
  export interface ConfigContext<R> {
133
159
  readonly env: ConfigEnv;
@@ -138,114 +164,175 @@ export interface ConfigContext<R> {
138
164
  }
139
165
  export type ConfigFactory<R, C> = (ctx: ConfigContext<R>) => C;
140
166
  /**
141
- * 一个 service 值:可选实现 `init` / `dispose`。
167
+ * `clients(ctx)` 的入参:`log` + `disposer` + `resources` + `config`。
168
+ *
169
+ * 与 `ServiceContext` 同一依赖集,差别只在**时序与语义**:`clients` 在 `services` 之前构造,
170
+ * 因此服务单元可以共享同一份外部客户端,而**不需要**破「service 之间禁止互依」。
171
+ *
172
+ * ❗**故意没有 `env`**(运行期不得读 env);❗**故意没有 `services`**(此时尚未构造);
173
+ * ❗**故意没有 `app`**(拿不到 app 就不可能注册路由)。
174
+ *
175
+ * 语义边界:`resources` = **我们拥有的基础设施**(连接串来自 env);`clients` = **我们调用的
176
+ * 外部服务**(地址 / 凭证来自业务配置,即 `config`)。这也是它必须排在 `config` 之后的原因。
177
+ *
178
+ * ```ts
179
+ * clients: ({ config, disposer, log }) => {
180
+ * const http = createHttpClient({ baseUrl: config.upstream.baseUrl, log })
181
+ * disposer.add(() => http.close())
182
+ * return { upstream: http }
183
+ * }
184
+ * ```
142
185
  *
143
- * **没有 `name` / `dependsOn`**:名字用对象键,依赖不存在(`services` ctx 没有 `services` 字段,
144
- * 编译期禁止互依)。顺序语义只有一条:按**对象声明序** `init` / **逆序** `dispose`。
186
+ * 没有外部客户端时写 `clients: () => ({})`——槽位恒定,不留「可省略」分支。
145
187
  */
146
- export interface ServiceUnit {
147
- init?(): void | Promise<void>;
148
- dispose?(): void | Promise<void>;
188
+ export interface ClientsContext<R, C> {
189
+ readonly log: LoggerLike;
190
+ /** 本阶段的释放器(关闭时在 services 之后、config 之前 flush) */
191
+ readonly disposer: Disposer;
192
+ readonly resources: ResourcesView<R>;
193
+ readonly config: Awaited<C>;
149
194
  }
195
+ export type ClientsFactory<R, C, CL> = (ctx: ClientsContext<NoInfer<R>, NoInfer<C>>) => CL;
150
196
  /**
151
- * `services(ctx)` 的入参:`log` + `disposer` + `resources` + `config`。
197
+ * `services(ctx)` 的入参:`log` + `disposer` + `resources` + `config` + `clients`。
152
198
  *
153
199
  * ❗**故意没有 `env`**(服务层属运行期,不得读 env);❗**故意没有 `services`**(禁 service 互依)。
200
+ *
201
+ * ```ts
202
+ * services: ({ resources, config, clients, disposer, log }) => {
203
+ * const user = createUserService({ database: resources.database, config, log })
204
+ * const queue = createQueue({ redis: resources.redis }) // 常驻单元:自己挂 init / dispose
205
+ * disposer.add(() => queue.stop())
206
+ * return { user, queue }
207
+ * }
208
+ * ```
209
+ *
210
+ * 两个约束:
211
+ * - **不得返回原始类型值**(`string` / `number` / `boolean`)——那是配置,不是服务。
212
+ * - **service 之间不得互依**:要共享的东西放 `clients`;需要常驻资源的单元自己实现
213
+ * `init()` / `dispose()`(框架会在 `middlewares` 之前 `init`、关闭时逆序 `dispose`)。
154
214
  */
155
- export interface ServiceContext<R, C> {
215
+ export interface ServiceContext<R, C, CL = unknown> {
156
216
  readonly log: LoggerLike;
157
217
  /** 本阶段的释放器(关闭时**先** flush 它,再逐单元逆序 `dispose`) */
158
218
  readonly disposer: Disposer;
159
219
  readonly resources: ResourcesView<R>;
160
220
  readonly config: Awaited<C>;
221
+ readonly clients: ClientsView<CL>;
161
222
  }
162
- export type ServicesFactory<R, C, S> = (ctx: ServiceContext<R, C>) => S;
163
223
  /**
164
- * 中间件单元:一个注册函数(`register(app)`),按**数组序**注册。
224
+ * 一个已构造好的中间件:接收 app 的注册函数。
165
225
  *
166
- * `name` 仅用于报错定位 / 日志跟踪;**不参与生命周期**(要常驻资源就放 `services`)。
226
+ * 中间件**不参与生命周期**(要常驻资源就放 `services`);注册后不再暴露内部钩子名。
167
227
  */
168
- export interface MiddlewareUnit {
169
- readonly name?: string;
170
- register(app: AppInstance): void | Promise<void>;
171
- }
228
+ export type Middleware = (app: AppInstance) => void | Promise<void>;
172
229
  /**
173
- * `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config`。
230
+ * `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config` + `clients`。
174
231
  *
175
232
  * ❗**故意没有 `resources` / `env`** —— 中间件要访问数据层必须穿过 service。
233
+ *
234
+ * ```ts
235
+ * middlewares: (ctx) => ({
236
+ * cors: createCorsMiddleware(ctx),
237
+ * auth: createAuthMiddleware(ctx, { client: createAuthClient(ctx.services) }),
238
+ * rateLimit: createRateLimitMiddleware(ctx, { limit: ctx.config.rateLimit }),
239
+ * })
240
+ * ```
241
+ *
242
+ * 返回对象是**键即名字、键序即注册序**(见下方 `MiddlewaresFactory` 的数字键陷阱)。
176
243
  */
177
- export interface MiddlewareContext<C, S> {
244
+ export interface MiddlewareContext<C, S, CL = unknown> {
178
245
  readonly app: AppInstance;
179
246
  readonly log: LoggerLike;
180
- /** 本阶段的释放器(关闭时最早 flush:在 services / config / resources 之前) */
247
+ /** 本阶段的释放器(关闭时最早 flush:在 services / clients / config / resources 之前) */
181
248
  readonly disposer: Disposer;
182
249
  readonly services: Awaited<S>;
183
250
  readonly config: Awaited<C>;
251
+ readonly clients: ClientsView<CL>;
184
252
  }
185
- export type MiddlewaresFactory<C, S> = (ctx: MiddlewareContext<C, S>) => MiddlewareUnit[];
186
253
  /**
187
- * `routers(ctx)` 的入参:`app` + `log` + `disposer` + `services`。
254
+ * `middlewares` 必须返回 `{ 名字: 注册函数 }` —— **键即 name、键序即注册序**。
255
+ *
256
+ * 键只用于报错定位 / 注册期日志,**不参与生命周期**。
257
+ *
258
+ * ⚠️ **坑**:JS 对象的枚举序是「整数样式的键在前(按数值升序),其余按插入序」。所以
259
+ * **纯数字键会被引擎重排到最前**,注册序随之改变——键名避免纯数字(用 `auth` / `rateLimit`
260
+ * 这类标识符即可)。除此外,注册序 = 对象键顺序。
261
+ */
262
+ export type MiddlewaresFactory<C, S, CL = unknown> = (ctx: MiddlewareContext<C, S, CL>) => Record<string, Middleware>;
263
+ /**
264
+ * `routers(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `clients`。
188
265
  *
189
266
  * ❗**故意没有 `resources` / `env` / `config`** —— 路由**只能穿过 service**,绕不过层直连数据层。
267
+ *
268
+ * ```ts
269
+ * routers: ({ app, services }) => {
270
+ * app.register(createUserRouter(services.user))
271
+ * }
272
+ * ```
273
+ *
274
+ * ❗**注册期不得调用 `services` 的方法**(只能把 service 捕获进 handler、请求期使用):
275
+ * `generateOpenApi` 走这条路径时不构造 `services`,注册期就读它会让出 spec 直接失败。
276
+ * 没业务路由也要写 `routers: () => {}`。
190
277
  */
191
- export interface RouterContext<S> {
278
+ export interface RouterContext<S, CL = unknown> {
192
279
  readonly app: AppInstance;
193
280
  readonly log: LoggerLike;
194
281
  /** 本阶段的释放器(关闭时**最先** flush) */
195
282
  readonly disposer: Disposer;
196
283
  readonly services: Awaited<S>;
284
+ readonly clients: ClientsView<CL>;
197
285
  }
198
- export type RoutersFactory<S> = (ctx: RouterContext<S>) => void | Promise<void>;
199
- /**
200
- * 若某个 service 值声明了 `dependsOn`,该值类型塌成 `never` → 赋值报错。
201
- */
202
- export type AssertNoDependencies<S> = {
203
- [K in keyof S]: S[K] extends {
204
- dependsOn: unknown;
205
- } ? never : S[K];
206
- };
286
+ export type RoutersFactory<S, CL = unknown> = (ctx: RouterContext<S, CL>) => void | Promise<void>;
207
287
  /**
208
288
  * `services` 返回值形状闸门:
209
- * - 原始类型值(`string` / `number` / `boolean`)**拒绝**(防把配置塞进 services);
210
- * - 声明了 `dependsOn` 的值**拒绝**(禁 service 互依)。
289
+ * - 原始类型值(`string` / `number` / `boolean`)**拒绝**(防把配置塞进 services)。
211
290
  */
212
291
  export type ServicesShape<S> = {
213
- [K in keyof S]: S[K] extends string | number | boolean ? never : S[K] extends {
214
- dependsOn: unknown;
215
- } ? never : S[K];
292
+ [K in keyof S]: S[K] extends string | number | boolean ? never : S[K];
216
293
  };
217
294
  /**
218
- * `defineServer()` 入参:一个身份块 + 五个工厂槽位(键顺序=分层)。
295
+ * `defineServer()` 入参:一个身份块 + **六个**工厂槽位(键顺序=分层)。
296
+ *
297
+ * ```
298
+ * app → resources(env) → config(env+resources) → clients(resources+config)
299
+ * → services(resources+config+clients) → middlewares(app+services+config+clients)
300
+ * → routers(app+services+clients)
301
+ * ```
219
302
  *
220
303
  * `S` 上的两道闸门(`ServicesShape<S>`,N2 §2.5)写在 `services` 的返回类型上:
221
304
  * `S & ServicesShape<S>` —— 形状合法时恒等,非法时该值塌成 `never` → 编译失败。
222
305
  */
223
- export interface ServerConfig<R, C, S> {
306
+ export interface ServerConfig<R, C, CL, S> {
224
307
  app: AppIdentity;
225
308
  resources: ResourcesFactory<R>;
226
309
  config: ConfigFactory<NoInfer<R>, C>;
227
- services: (ctx: ServiceContext<NoInfer<R>, NoInfer<C>>) => S & ServicesShape<S>;
228
- middlewares: MiddlewaresFactory<NoInfer<C>, NoInfer<S>>;
229
- routers: RoutersFactory<NoInfer<S>>;
310
+ /**
311
+ * 外部服务客户端(**我们调用的、地址/凭证来自业务配置的外部服务**)。
312
+ *
313
+ * 存在的唯一理由是「`services` 之间禁止互依」与「客户端需要 `config`」这两条约束
314
+ * 的交集:多个 service / middleware 必须共享同一份客户端,而它们之间没有共享通道。
315
+ * 没有这种需求就写 `() => ({})` —— 槽位恒定,不留「可省略」分支。
316
+ */
317
+ clients: ClientsFactory<NoInfer<R>, NoInfer<C>, CL>;
318
+ services: (ctx: ServiceContext<NoInfer<R>, NoInfer<C>, NoInfer<CL>>) => S & ServicesShape<S>;
319
+ middlewares: MiddlewaresFactory<NoInfer<C>, NoInfer<S>, NoInfer<CL>>;
320
+ /**
321
+ * 业务路由注册(**必填**)。
322
+ *
323
+ * 框架已自动注册 5 条系统路由(`/` / `/health` / `/system/health` / `/system/info` /
324
+ * `/system/openapi`,见 `core/modules/system-routes/`),应用侧无需再写 `createSystemRouter`;
325
+ * 本槽位只用于注册**业务**路由。没有业务路由的应用也必须显式传入(写 `() => {}`),
326
+ * 以保持槽位结构恒定、无「可省略」分支。
327
+ */
328
+ routers: RoutersFactory<NoInfer<S>, NoInfer<CL>>;
230
329
  }
231
330
  /** 品牌字段值(`Server` 的 `__brand`) */
232
331
  export declare const SERVER_BRAND = "taicode.server";
233
332
  /** 纯定义产物:同一份定义可同时喂给「启动」与「出 spec」 */
234
- export type Server<R, C, S> = ServerConfig<R, C, S> & {
333
+ export type Server<R, C, CL, S> = ServerConfig<R, C, CL, S> & {
235
334
  readonly __brand: typeof SERVER_BRAND;
236
335
  };
237
- /** `startServer()` 的逃生选项 */
238
- export interface StartServerOptions<R = unknown, C = unknown, S = unknown> {
239
- /** 是否 listen + 装信号;默认 `NODE_ENV !== 'test'` */
240
- listen?: boolean;
241
- /** 整体覆盖关闭流程 */
242
- shutdown?: (ctx: ShutdownContext<R, C, S>) => Promise<void>;
243
- }
244
- /** 关闭阶段视图:`services` ctx + `app` + `services` 实体 */
245
- export interface ShutdownContext<R, C, S> extends ServiceContext<R, C> {
246
- readonly app: AppInstance;
247
- readonly services: Awaited<S>;
248
- }
249
336
  /** `generateOpenApi()` 的返回(OpenAPI 文档对象) */
250
337
  export type OpenApiDocument = Record<string, unknown>;
251
338
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAMH,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAC5C;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,eAAe,CACvC,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,CAChB,CAAA;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACpC;AAED;;;;;GAKG;AACH,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAEhF,qDAAqD;AACrD,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAA;AAE/C,6CAA6C;AAC7C,MAAM,MAAM,WAAW,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;AAM/C;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,OAAO,KAAK,WAAW,GAAG,IAAI,CAAA;AAEpE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B;;;;;;OAMG;IACH,qBAAqB,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAA;CACzD;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,KAAK,CAAC,CAAA;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;IACvB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,mDAAmD;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;CACrC;AAED,MAAM,MAAM,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAC5B;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAA;AAE1F;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO;IAChD,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,CAAA;CAClC;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAEnE;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO;IACnD,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,CAAA;CAClC;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAErH;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO;IAC5C,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;IACzB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,+BAA+B;IAC/B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,CAAA;CAClC;AAED,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,EAAE,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;AAMjG;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;CACtE,CAAA;AAMD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACvC,GAAG,EAAE,WAAW,CAAA;IAChB,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC9B,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACpC;;;;;;OAMG;IACH,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACnD,QAAQ,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;IAC5F,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;IACpE;;;;;;;OAOG;IACH,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;CACjD;AAED,kCAAkC;AAClC,eAAO,MAAM,YAAY,mBAAmB,CAAA;AAE5C,oCAAoC;AACpC,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG;IAC5D,QAAQ,CAAC,OAAO,EAAE,OAAO,YAAY,CAAA;CACtC,CAAA;AAED,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { Middleware, MiddlewareContext } from '../core/types.js';
2
+ export declare function createCorsMiddleware<C = unknown, S = unknown>(ctx: MiddlewareContext<C, S>): Middleware;
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,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAkBrE,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CA0BvG"}
@@ -0,0 +1,27 @@
1
+ import cors from '@fastify/cors';
2
+ import { readConfigKey } from '../core/boot/context.js';
3
+ export function createCorsMiddleware(ctx) {
4
+ return 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
+ }
package/output/index.d.ts CHANGED
@@ -1,6 +1,10 @@
1
- export * from './schema/index.js';
1
+ export * from './core/modules/schema/builders.js';
2
+ export { createContext } from './core/modules/context/index.js';
3
+ export type { RequestContext } from './core/modules/context/index.js';
4
+ export * from './redis/index.js';
5
+ export * from './rate-limit/limiter.js';
6
+ export * from './rate-limit/memory-store.js';
7
+ export * from './rate-limit/redis-store.js';
8
+ export * from './rate-limit/types.js';
2
9
  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
10
  //# 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,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"}
package/output/index.js CHANGED
@@ -1,6 +1,25 @@
1
- // 根 index.ts,统一导出 mock 目录下所有能力
2
- export * from './schema/index.js';
1
+ // 包根出口(`@taicode/common-server`):业务侧直接使用的**构件**——
2
+ // 路由 / 查询 schema 工厂、请求上下文构造、Redis 缓存、限流实现、Redis 队列。
3
+ //
4
+ // 目录布局(见 `common/product/designs/create-server/design.md` §7.1):
5
+ // ```
6
+ // source/
7
+ // ├── core/ 核心设计(锁定:不可替换、不可修改,**不对外导出装配**)
8
+ // ├── app/ 服务入口 barrel(defineServer / startServer / generateOpenApi)
9
+ // ├── auth/ 可选:鉴权中间件
10
+ // ├── cors/ 可选:CORS 中间件
11
+ // ├── rate-limit/ 可选:限流中间件 + 限流实现
12
+ // ├── redis/ 请求期 Redis / 缓存
13
+ // └── redis-queue/ 可选:Redis 队列
14
+ // ```
15
+ //
16
+ // 框架自己装配的一切(`createFastify` / `registerCore` / 各 core 单元 / `request.ctx` 装配 /
17
+ // 默认中间件预设)都不在这里:**固定下来的东西不再交给外面组装**。
18
+ export * from './core/modules/schema/builders.js';
19
+ export { createContext } from './core/modules/context/index.js';
20
+ export * from './redis/index.js';
21
+ export * from './rate-limit/limiter.js';
22
+ export * from './rate-limit/memory-store.js';
23
+ export * from './rate-limit/redis-store.js';
24
+ export * from './rate-limit/types.js';
3
25
  export * from './redis-queue/index.js';
4
- export * from './redis-cache/index.js';
5
- export * from './rate-limiter/index.js';
6
- export * from './context/index.js';
@@ -0,0 +1,23 @@
1
+ import type { Middleware, MiddlewareContext } from '../core/types.js';
2
+ import type { RateLimitStore } from './types.js';
3
+ /**
4
+ * 标准中间件单元 `rateLimit`(可替换;**不在默认预设内**)。
5
+ *
6
+ * **规则只有一个来源:路由的 `config.rateLimit`**(与 `auth` 的 `accessControls` 同一取向)。
7
+ * 存储后端由调用方注入(缺省进程内 memory)。`TOO_MANY_REQUESTS` 经 `UserError` 仍映射 **400**。
8
+ * 路由未声明 `config.rateLimit` 时该项空转。
9
+ *
10
+ * 注:中间件的 ctx 看不到 `resources`(中间件不得直连数据层),因此 Redis store 需由调用方
11
+ * 在 `services` 侧构造后注入(`store`),框架不再自行探测 Redis。
12
+ */
13
+ export interface RateLimitMiddlewareOptions {
14
+ /** 存储后端(缺省进程内 memory) */
15
+ store?: RateLimitStore;
16
+ }
17
+ export declare function createRateLimitMiddleware<C = unknown, S = unknown>(_ctx: MiddlewareContext<C, S>, options?: RateLimitMiddlewareOptions): Middleware;
18
+ export { RateLimiter, createRateLimitKey } from './limiter.js';
19
+ export { MemoryRateLimitStore } from './memory-store.js';
20
+ export { RedisRateLimitStore } from './redis-store.js';
21
+ export type { RedisRateLimitStoreOptions } from './redis-store.js';
22
+ export type { RateLimitConfig, RateLimitInfo, RateLimiterOptions, RateLimitResult, RateLimitStore } from './types.js';
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/rate-limit/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACrE,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,UAAU,CAkBvJ;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"}
@@ -0,0 +1,42 @@
1
+ import { UserError } from '@taicode/common-base';
2
+ import { createRateLimitKey, RateLimiter } from './limiter.js';
3
+ import { MemoryRateLimitStore } from './memory-store.js';
4
+ import { readServerIdentity } from '../core/modules/identity/index.js';
5
+ function resolveRule(request) {
6
+ return request.routeOptions?.config?.rateLimit;
7
+ }
8
+ function buildKeys(request, keys) {
9
+ return keys.map((key) => {
10
+ if (key === 'ip') {
11
+ return `ip:${request.ip}`;
12
+ }
13
+ if (key === 'user') {
14
+ return `user:${String(request.user?.id ?? 'anonymous')}`;
15
+ }
16
+ if (key === 'path') {
17
+ return `path:${request.routeOptions?.url ?? request.url}`;
18
+ }
19
+ return `${key}:${String(request[key] ?? '')}`;
20
+ });
21
+ }
22
+ export function createRateLimitMiddleware(_ctx, options = {}) {
23
+ return (app) => {
24
+ const limiter = new RateLimiter({ store: options.store ?? new MemoryRateLimitStore() });
25
+ const prefix = `${readServerIdentity(app)?.service ?? ''}:`;
26
+ app.addHook('preValidation', async (request) => {
27
+ const rule = resolveRule(request);
28
+ if (!rule) {
29
+ return;
30
+ }
31
+ const keys = buildKeys(request, rule.keys ?? ['ip']).map((key) => createRateLimitKey(prefix, key));
32
+ const result = await limiter.checkMultiLimit(keys, { limit: rule.limit, window: rule.window });
33
+ if (!result.allowed) {
34
+ throw new UserError('TOO_MANY_REQUESTS', undefined, '请求过于频繁');
35
+ }
36
+ });
37
+ };
38
+ }
39
+ // ── 限流实现(与中间件同一模块)─────────────────────────────────
40
+ export { RateLimiter, createRateLimitKey } from './limiter.js';
41
+ export { MemoryRateLimitStore } from './memory-store.js';
42
+ export { RedisRateLimitStore } from './redis-store.js';
@@ -14,4 +14,4 @@ export declare class RateLimiter {
14
14
  * 创建限流键的辅助函数
15
15
  */
16
16
  export declare function createRateLimitKey(prefix: string, ...parts: string[]): string;
17
- //# sourceMappingURL=rate-limiter.d.ts.map
17
+ //# sourceMappingURL=limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../../source/rate-limit/limiter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAkB,MAAM,YAAY,CAAA;AAErH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;gBAEjB,OAAO,GAAE,kBAAuB;IAItC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;IAoC1E,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI9D;;OAEG;IACG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;CAezF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAE7E"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../source/rate-limit/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAO/D,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,eAAe,CAA8B;gBAEzC,iBAAiB,SAAQ;IAY/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAY/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,OAAO,IAAI,IAAI;CAOhB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-store.d.ts","sourceRoot":"","sources":["../../source/rate-limit/redis-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAG1D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,0BAA0B;IACzC,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kCAAkC;IAClC,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAA;CAC1B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC,OAAO,CAAC,gBAAgB,CAAI;gBAEhB,OAAO,GAAE,0BAA+B;IAyBvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,UAAU,IAAI,IAAI;IAKZ,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAqB/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/C,4BAA4B;IACf,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,QAAQ;CAOjB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-store.test.d.ts","sourceRoot":"","sources":["../../source/rate-limit/redis-store.test.ts"],"names":[],"mappings":""}
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
- import { createRedisClient } from '../redis-cache/redis-cache.js';
3
- import { RateLimiter } from './rate-limiter.js';
2
+ import { createRedisClient } from '../redis/cache.js';
3
+ import { RateLimiter } from './limiter.js';
4
4
  import { RedisRateLimitStore } from './redis-store.js';
5
5
  const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
6
6
  describe('RedisRateLimitStore', () => {
@@ -19,7 +19,6 @@ export interface RateLimitStore {
19
19
  export interface RateLimitConfig {
20
20
  limit: number;
21
21
  window: number;
22
- lockout?: number;
23
22
  }
24
23
  export interface RateLimiterOptions {
25
24
  store?: RateLimitStore;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/rate-limit/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,IAAI,CAAA;IACjB,SAAS,EAAE,IAAI,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,IAAI,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;IAC/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAClC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -136,4 +136,4 @@ export interface CreateRedisClientOptions {
136
136
  connectTimeoutMs?: number;
137
137
  }
138
138
  export declare function createRedisClient(options: CreateRedisClientOptions): RedisClientType;
139
- //# sourceMappingURL=redis-cache.d.ts.map
139
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../source/redis/cache.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,OAAO,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,uBAAuB;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,EAAE,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAA;CACjC;AAaD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAEvC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAEhC,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,OAAO,CAAC,gBAAgB,CAAI;gBAEhB,OAAO,EAAE,iBAAiB;IA6BtC,uBAAuB;IACvB,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,sBAAsB;IACtB,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,wCAAwC;IACxC,IAAW,WAAW,IAAI,OAAO,CAEhC;IAED;;;OAGG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,0BAA0B;IACnB,UAAU,IAAI,IAAI;IAOzB;;;;;OAKG;IACU,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrC;;;OAGG;IACI,KAAK,IAAI,IAAI;IAQP,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAItC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C;;;;;;OAMG;IACU,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwB5D;;;;;;OAMG;IACU,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IA6BhG;;;;OAIG;IACH,OAAO,CAAC,QAAQ;IAIhB,8CAA8C;YAChC,aAAa;YAmBb,aAAa;IAY3B,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;CAOjB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,eAAe,CAkBpF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../source/redis/cache.test.ts"],"names":[],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
- import { RedisCache, createRedisClient } from './redis-cache.js';
2
+ import { RedisCache, createRedisClient } from './cache.js';
3
3
  const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
4
4
  describe('RedisCache', () => {
5
5
  describe('纯一级缓存(未配置 Redis)', () => {
@@ -1,6 +1,6 @@
1
- import { RedisCache } from '../redis-cache/index.js';
2
- import type { ConfigEnv } from './env.js';
3
- import type { LoggerLike, RedisHandle } from './types.js';
1
+ import { RedisCache } from './cache.js';
2
+ import type { ConfigEnv } from '../core/env.js';
3
+ import type { LoggerLike, RedisHandle } from '../core/types.js';
4
4
  /**
5
5
  * 引导期 Redis / cache helper(N2 §2.13)。
6
6
  *
@@ -20,8 +20,6 @@ import type { LoggerLike, RedisHandle } from './types.js';
20
20
  * **惰性包装**:构造时不建连、不 ping,首次真正使用才建连 —— 「出 spec 不连库/Redis」结构性成立。
21
21
  * 释放永远走调用方在 `ctx.disposer` 里的登记,框架不替资源持有句柄。
22
22
  */
23
- /** 一级/二级缓存默认 TTL(秒),与 `RedisCache` 默认一致 */
24
- export declare const DEFAULT_REDIS_CACHE_TTL_SECONDS = 300;
25
23
  /**
26
24
  * 构造 Redis 句柄(惰性包装:不建连、不 ping)。
27
25
  *
@@ -46,4 +44,7 @@ export interface CacheSetupParams {
46
44
  * - 都没有 → 纯进程内一级缓存(不建连)。
47
45
  */
48
46
  export declare function createCache(params: CacheSetupParams): RedisCache;
49
- //# sourceMappingURL=redis.d.ts.map
47
+ export { RedisCache, createRedisClient } from './cache.js';
48
+ export type { CreateRedisClientOptions, RedisCacheL1Options, RedisCacheOptions } from './cache.js';
49
+ export type { RedisClientType } from 'redis';
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../source/redis/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,CAwBrF;AAED,uBAAuB;AACvB,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAA;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU,CAYhE;AAGD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAC1D,YAAY,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAClG,YAAY,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA"}