@taicode/common-server 4.0.0-beta.2 → 4.0.0-beta.4

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 (276) 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/core/boot/context.js +106 -0
  13. package/output/core/boot/lifecycle.d.ts +94 -0
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/{app → core/boot}/lifecycle.js +45 -14
  16. package/output/core/boot/shutdown.d.ts +30 -0
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/core/boot/shutdown.js +45 -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 +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/core/types.d.ts +338 -0
  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} +12 -7
  124. package/output/redis/index.d.ts.map +1 -0
  125. package/output/{app/redis.js → redis/index.js} +15 -5
  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 +16 -15
  135. package/LICENSE +0 -21
  136. package/output/app/auth.test.d.ts +0 -2
  137. package/output/app/auth.test.d.ts.map +0 -1
  138. package/output/app/auth.test.js +0 -148
  139. package/output/app/boot.d.ts +0 -39
  140. package/output/app/boot.d.ts.map +0 -1
  141. package/output/app/boot.js +0 -140
  142. package/output/app/config.test.d.ts +0 -2
  143. package/output/app/config.test.d.ts.map +0 -1
  144. package/output/app/config.test.js +0 -132
  145. package/output/app/context.d.ts +0 -70
  146. package/output/app/context.d.ts.map +0 -1
  147. package/output/app/context.js +0 -73
  148. package/output/app/core/cookie.d.ts +0 -25
  149. package/output/app/core/cookie.d.ts.map +0 -1
  150. package/output/app/core/identity.d.ts +0 -12
  151. package/output/app/core/identity.d.ts.map +0 -1
  152. package/output/app/core/index.d.ts +0 -44
  153. package/output/app/core/index.d.ts.map +0 -1
  154. package/output/app/core/index.js +0 -57
  155. package/output/app/core/logging.d.ts +0 -13
  156. package/output/app/core/logging.d.ts.map +0 -1
  157. package/output/app/core/openapi.d.ts +0 -17
  158. package/output/app/core/openapi.d.ts.map +0 -1
  159. package/output/app/core/request-id.d.ts.map +0 -1
  160. package/output/app/core/response.d.ts +0 -41
  161. package/output/app/core/response.d.ts.map +0 -1
  162. package/output/app/define-server.negative-cases.d.ts +0 -40
  163. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  164. package/output/app/define-server.negative-cases.js +0 -268
  165. package/output/app/define-server.test.d.ts +0 -2
  166. package/output/app/define-server.test.d.ts.map +0 -1
  167. package/output/app/define-server.test.js +0 -97
  168. package/output/app/entries.d.ts +0 -29
  169. package/output/app/entries.d.ts.map +0 -1
  170. package/output/app/entries.js +0 -64
  171. package/output/app/env.d.ts +0 -59
  172. package/output/app/env.d.ts.map +0 -1
  173. package/output/app/env.js +0 -66
  174. package/output/app/env.test.js +0 -124
  175. package/output/app/error-contract.test.d.ts +0 -2
  176. package/output/app/error-contract.test.d.ts.map +0 -1
  177. package/output/app/error-contract.test.js +0 -227
  178. package/output/app/errors.d.ts +0 -20
  179. package/output/app/errors.d.ts.map +0 -1
  180. package/output/app/errors.js +0 -51
  181. package/output/app/generate-openapi.test.d.ts +0 -2
  182. package/output/app/generate-openapi.test.d.ts.map +0 -1
  183. package/output/app/generate-openapi.test.js +0 -123
  184. package/output/app/lifecycle-logging.test.d.ts +0 -2
  185. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  186. package/output/app/lifecycle-logging.test.js +0 -155
  187. package/output/app/lifecycle.d.ts +0 -70
  188. package/output/app/lifecycle.d.ts.map +0 -1
  189. package/output/app/lifecycle.test.d.ts +0 -2
  190. package/output/app/lifecycle.test.d.ts.map +0 -1
  191. package/output/app/lifecycle.test.js +0 -152
  192. package/output/app/middleware/auth.d.ts +0 -71
  193. package/output/app/middleware/auth.d.ts.map +0 -1
  194. package/output/app/middleware/auth.js +0 -190
  195. package/output/app/middleware/context.d.ts +0 -13
  196. package/output/app/middleware/context.d.ts.map +0 -1
  197. package/output/app/middleware/context.js +0 -15
  198. package/output/app/middleware/cors.d.ts +0 -14
  199. package/output/app/middleware/cors.d.ts.map +0 -1
  200. package/output/app/middleware/cors.js +0 -30
  201. package/output/app/middleware/index.d.ts +0 -17
  202. package/output/app/middleware/index.d.ts.map +0 -1
  203. package/output/app/middleware/index.js +0 -16
  204. package/output/app/middleware/rate-limit.d.ts +0 -24
  205. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  206. package/output/app/middleware/rate-limit.js +0 -46
  207. package/output/app/middleware-order.test.d.ts +0 -2
  208. package/output/app/middleware-order.test.d.ts.map +0 -1
  209. package/output/app/middleware-order.test.js +0 -148
  210. package/output/app/permission-operations.d.ts +0 -48
  211. package/output/app/permission-operations.d.ts.map +0 -1
  212. package/output/app/permission-operations.js +0 -77
  213. package/output/app/permission-operations.test.d.ts +0 -2
  214. package/output/app/permission-operations.test.d.ts.map +0 -1
  215. package/output/app/permission-operations.test.js +0 -72
  216. package/output/app/permission-registration.test.d.ts +0 -2
  217. package/output/app/permission-registration.test.d.ts.map +0 -1
  218. package/output/app/permission-registration.test.js +0 -138
  219. package/output/app/redis.d.ts.map +0 -1
  220. package/output/app/registered-operations.test.d.ts +0 -2
  221. package/output/app/registered-operations.test.d.ts.map +0 -1
  222. package/output/app/registered-operations.test.js +0 -57
  223. package/output/app/request-id.test.d.ts +0 -2
  224. package/output/app/request-id.test.d.ts.map +0 -1
  225. package/output/app/request-id.test.js +0 -130
  226. package/output/app/resources.test.d.ts +0 -2
  227. package/output/app/resources.test.d.ts.map +0 -1
  228. package/output/app/resources.test.js +0 -199
  229. package/output/app/shutdown.d.ts +0 -22
  230. package/output/app/shutdown.d.ts.map +0 -1
  231. package/output/app/shutdown.js +0 -30
  232. package/output/app/shutdown.test.d.ts +0 -2
  233. package/output/app/shutdown.test.d.ts.map +0 -1
  234. package/output/app/shutdown.test.js +0 -103
  235. package/output/app/signal.d.ts +0 -12
  236. package/output/app/signal.d.ts.map +0 -1
  237. package/output/app/smoke.test.d.ts +0 -2
  238. package/output/app/smoke.test.d.ts.map +0 -1
  239. package/output/app/smoke.test.js +0 -210
  240. package/output/app/test-support.d.ts +0 -21
  241. package/output/app/test-support.d.ts.map +0 -1
  242. package/output/app/test-support.js +0 -24
  243. package/output/app/type-augment.d.ts.map +0 -1
  244. package/output/app/types.d.ts +0 -228
  245. package/output/app/types.d.ts.map +0 -1
  246. package/output/context/index.d.ts +0 -6
  247. package/output/context/index.d.ts.map +0 -1
  248. package/output/context/index.js +0 -14
  249. package/output/rate-limiter/index.d.ts +0 -5
  250. package/output/rate-limiter/index.d.ts.map +0 -1
  251. package/output/rate-limiter/index.js +0 -4
  252. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  253. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  254. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  255. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  256. package/output/rate-limiter/types.d.ts.map +0 -1
  257. package/output/redis-cache/index.d.ts +0 -4
  258. package/output/redis-cache/index.d.ts.map +0 -1
  259. package/output/redis-cache/index.js +0 -1
  260. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  261. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  262. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  263. package/output/schema/index.d.ts.map +0 -1
  264. /package/output/{app → core}/env.test.d.ts +0 -0
  265. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  266. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  267. /package/output/{context → core/modules/context}/index.test.js +0 -0
  268. /package/output/{app → core}/type-augment.js +0 -0
  269. /package/output/{app → core}/types.js +0 -0
  270. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  271. /package/output/{rate-limiter → rate-limit}/memory-store.d.ts +0 -0
  272. /package/output/{rate-limiter → rate-limit}/memory-store.js +0 -0
  273. /package/output/{rate-limiter → rate-limit}/redis-store.d.ts +0 -0
  274. /package/output/{rate-limiter → rate-limit}/redis-store.js +0 -0
  275. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  276. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -0,0 +1,338 @@
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
+ * 顶层是「一个身份块 + 六个工厂槽位」,**键顺序即分层**:
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` 只属引导期
22
+ * (`resources` / `config` 可见,运行期三层一律看不到)。
23
+ *
24
+ * **`resources` 与 `clients` 的语义分界**:`resources` = **我们拥有的基础设施**(DB / Redis,
25
+ * 连接串来自 env);`clients` = **我们调用的外部服务**(地址 / 凭证来自业务配置)。
26
+ * 后者必然依赖 `config`,所以结构上只能排在 `config` 之后——这也解释了为什么不能把它
27
+ * 并进 `resources`。
28
+ *
29
+ * **`disposer` 是每个槽位 ctx 的标配**(不是某个槽位专属):在**创建某个东西的地方**当场
30
+ * `ctx.disposer.add(() => 释放它)`,创建与释放同处一地;`disposer` 是**出口**(不产出任何依赖),
31
+ * 因此不会造成循环,也不削弱「只给该阶段该看的字段」的约束。
32
+ *
33
+ * 生命周期:每个阶段一个独立的 `Disposer` 实例;关闭时**按阶段逆序 flush**
34
+ * `routers → middlewares → services → clients → config → resources`(services 阶段内部:先 flush
35
+ * disposer,再逐单元逆序 `dispose`)。详见 `lifecycle.ts` / `shutdown.ts`。
36
+ *
37
+ * 类型安全机制(N2 §1):
38
+ * - **M1 分阶段 ctx**:每个工厂只拿到「该阶段该看的字段」(**外加每个阶段都有的 `disposer`**);
39
+ * - **M2 `Awaited<>` 归一**:工厂签名写 `(ctx) => R`(不是 `R | Promise<R>`),下游一律
40
+ * `Awaited<R>` / `Awaited<C>` / `Awaited<CL>` / `Awaited<S>`;
41
+ * - **M3 资源嵌套**:资源包挂在 `ctx.resources` 上(不再摊平到 ctx,键冲突从根上消失);
42
+ * - **M4 `Live<T>` 静态开关**:资源包 / 客户端包里值为 `false` 的键在类型上剔除;
43
+ * - **M5 强类型 `services`**:`middlewares` / `routers` 的 `ctx.services` 是 `S` 实体;
44
+ * - **M6 品牌 + 类型守卫**:`Server` 带品牌,`isServer()` 收窄;
45
+ * - **M7 负向类型测试**:`define-server.negative-cases.ts`,由 `tsc --noEmit` 覆盖。
46
+ *
47
+ * 命名全拼:`requestId` / `database` / `configuration` / `service`(不用 `db` / `cfg`)。
48
+ */
49
+ /** 骨架只按 `(obj, msg)` 形态打日志;`app.log` 与 `@taicode/common-base` 的 `Logger` 都满足 */
50
+ export interface LoggerLike {
51
+ debug: (obj: unknown, msg?: string) => void;
52
+ info: (obj: unknown, msg?: string) => void;
53
+ warn: (obj: unknown, msg?: string) => void;
54
+ error: (obj: unknown, msg?: string) => void;
55
+ }
56
+ /**
57
+ * 路由级类型安全的载体:带 `ZodTypeProvider` 的 `FastifyInstance`。
58
+ *
59
+ * 裸 `FastifyInstance` 会让路由的 `params` / `body` / `query` 全是 `unknown`;带上 provider 后
60
+ * `request.params.id` 有类型。
61
+ */
62
+ export type AppInstance = FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse, FastifyBaseLogger, ZodTypeProvider>;
63
+ /**
64
+ * 身份块:非 env 派生,出 spec 路径也要用。
65
+ *
66
+ * `version` **必填、无默认值**(缺它就是编译错误,杜绝「悄悄兜底成 1.0.0」)。
67
+ */
68
+ export interface AppIdentity {
69
+ /** 权限注册键 / cache 前缀 / 失效通道前缀 / 日志标签 */
70
+ readonly service: string;
71
+ readonly title: string;
72
+ /** openapi `info.version` + 权限操作自注册;建议从 package.json 读入 */
73
+ readonly version: string;
74
+ }
75
+ /**
76
+ * Redis 句柄(惰性包装)。
77
+ *
78
+ * 框架**不操作** Redis 命令,只持有并在关闭时透传 dispose;构造时不建连、不 ping。
79
+ * 因此保持开放类型,各仓注入自己的客户端实现不受约束。
80
+ */
81
+ export interface RedisHandle {
82
+ client: unknown;
83
+ dispose: () => void | Promise<void>;
84
+ }
85
+ /**
86
+ * 剔掉值为 `false` 的键(编译期静态开关)。
87
+ *
88
+ * 资源包里写 `cache: false` 表示**静态关闭**:`Live<T>` 里该键**不存在**,访问直接编译失败;
89
+ * 而 `cache: Cache | undefined`(显式可选)表示**运行时缺省**:键存在,类型为 `Cache | undefined`。
90
+ */
91
+ export type Live<T> = {
92
+ [K in keyof T as T[K] extends false ? never : K]: T[K];
93
+ };
94
+ /** 框架在 ctx 上暴露的资源视图(`Awaited<>` 归一 + `false` 键剔除) */
95
+ export type ResourcesView<R> = Live<Awaited<R>>;
96
+ /** 框架在 ctx 上暴露的客户端视图(口径同 `ResourcesView`) */
97
+ export type ClientsView<CL> = Live<Awaited<CL>>;
98
+ /**
99
+ * 资源错误分类器(响应层用)。
100
+ *
101
+ * **每个资源注册自己的分类器**(一个资源一个):只认领自己错误域内的错误,认领不了返回 `null` 交给
102
+ * 下一个。由服务仓在 `resources(ctx)` 里通过 `ctx.registerClassifyError(...)` 注册;可注册多个,
103
+ * 按注册顺序求值取第一个非 `null` 的结果;全部未命中 / 一个都没注册 → 响应层落 `UNKNOWN_ERROR` 兜底。
104
+ */
105
+ export type ErrorClassifier = (error: unknown) => SystemError | null;
106
+ /**
107
+ * `resources(ctx)` 的入参:**`env` + `log` + `disposer`**(外加槽位专属的注册能力)。
108
+ *
109
+ * 结构上排除 `config` / `services` / `app` → 「资源依赖配置」这类循环**编译期写不出来**。
110
+ * `disposer` 是**出口**(只能注册释放函数,不产出依赖),因此不构成新的循环面:
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
+ * ```
124
+ */
125
+ export interface ResourcesContext {
126
+ readonly env: ConfigEnv;
127
+ readonly log: LoggerLike;
128
+ /**
129
+ * 本阶段的释放器:**在创建资源的地方当场注册释放**。
130
+ *
131
+ * 关闭序中 `resources` 阶段**最后** flush(所有其它阶段都还活着)。
132
+ */
133
+ readonly disposer: Disposer;
134
+ /**
135
+ * **每个资源注册自己的错误分类器**(响应层用)。
136
+ *
137
+ * 一个资源一个分类器,各管各的错误域:只认领自己域内的错误,认领不了返回 `null` 交给下一个。
138
+ * 可注册多个:按注册顺序求值,取第一个非 `null` 的结果;全部未命中 → 响应层落 `UNKNOWN_ERROR` 兜底。
139
+ * 本方法是 **resources 槽位专属能力**(其他槽位的 ctx 不提供)。
140
+ */
141
+ registerClassifyError(classifier: ErrorClassifier): void;
142
+ }
143
+ export type ResourcesFactory<R> = (ctx: ResourcesContext) => R;
144
+ /**
145
+ * `config(ctx)` 的入参:`env` + `log` + `disposer` + 资源视图。
146
+ *
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
+ * 返回值是**启动期快照**:进程内不再变,没有热更新通道。要能随时改的开关放数据库、请求期现读。
157
+ */
158
+ export interface ConfigContext<R> {
159
+ readonly env: ConfigEnv;
160
+ readonly log: LoggerLike;
161
+ /** 本阶段的释放器(关闭时在 services 之后、resources 之前 flush) */
162
+ readonly disposer: Disposer;
163
+ readonly resources: ResourcesView<R>;
164
+ }
165
+ export type ConfigFactory<R, C> = (ctx: ConfigContext<R>) => C;
166
+ /**
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
+ * ```
185
+ *
186
+ * 没有外部客户端时写 `clients: () => ({})`——槽位恒定,不留「可省略」分支。
187
+ */
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>;
194
+ }
195
+ export type ClientsFactory<R, C, CL> = (ctx: ClientsContext<NoInfer<R>, NoInfer<C>>) => CL;
196
+ /**
197
+ * `services(ctx)` 的入参:`log` + `disposer` + `resources` + `config` + `clients`。
198
+ *
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`)。
214
+ */
215
+ export interface ServiceContext<R, C, CL = unknown> {
216
+ readonly log: LoggerLike;
217
+ /** 本阶段的释放器(关闭时**先** flush 它,再逐单元逆序 `dispose`) */
218
+ readonly disposer: Disposer;
219
+ readonly resources: ResourcesView<R>;
220
+ readonly config: Awaited<C>;
221
+ readonly clients: ClientsView<CL>;
222
+ }
223
+ /**
224
+ * 一个已构造好的中间件:接收 app 的注册函数。
225
+ *
226
+ * 中间件**不参与生命周期**(要常驻资源就放 `services`);注册后不再暴露内部钩子名。
227
+ */
228
+ export type Middleware = (app: AppInstance) => void | Promise<void>;
229
+ /**
230
+ * `middlewares(ctx)` 的入参:`app` + `log` + `disposer` + `services` + `config` + `clients`。
231
+ *
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` 的数字键陷阱)。
243
+ */
244
+ export interface MiddlewareContext<C, S, CL = unknown> {
245
+ readonly app: AppInstance;
246
+ readonly log: LoggerLike;
247
+ /** 本阶段的释放器(关闭时最早 flush:在 services / clients / config / resources 之前) */
248
+ readonly disposer: Disposer;
249
+ readonly services: Awaited<S>;
250
+ readonly config: Awaited<C>;
251
+ readonly clients: ClientsView<CL>;
252
+ }
253
+ /**
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`。
265
+ *
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: () => {}`。
277
+ */
278
+ export interface RouterContext<S, CL = unknown> {
279
+ readonly app: AppInstance;
280
+ readonly log: LoggerLike;
281
+ /** 本阶段的释放器(关闭时**最先** flush) */
282
+ readonly disposer: Disposer;
283
+ readonly services: Awaited<S>;
284
+ readonly clients: ClientsView<CL>;
285
+ }
286
+ export type RoutersFactory<S, CL = unknown> = (ctx: RouterContext<S, CL>) => void | Promise<void>;
287
+ /**
288
+ * `services` 返回值形状闸门:
289
+ * - 原始类型值(`string` / `number` / `boolean`)**拒绝**(防把配置塞进 services)。
290
+ */
291
+ export type ServicesShape<S> = {
292
+ [K in keyof S]: S[K] extends string | number | boolean ? never : S[K];
293
+ };
294
+ /**
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
+ * ```
302
+ *
303
+ * `S` 上的两道闸门(`ServicesShape<S>`,N2 §2.5)写在 `services` 的返回类型上:
304
+ * `S & ServicesShape<S>` —— 形状合法时恒等,非法时该值塌成 `never` → 编译失败。
305
+ */
306
+ export interface ServerConfig<R, C, CL, S> {
307
+ app: AppIdentity;
308
+ resources: ResourcesFactory<R>;
309
+ config: ConfigFactory<NoInfer<R>, C>;
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>>;
329
+ }
330
+ /** 品牌字段值(`Server` 的 `__brand`) */
331
+ export declare const SERVER_BRAND = "taicode.server";
332
+ /** 纯定义产物:同一份定义可同时喂给「启动」与「出 spec」 */
333
+ export type Server<R, C, CL, S> = ServerConfig<R, C, CL, S> & {
334
+ readonly __brand: typeof SERVER_BRAND;
335
+ };
336
+ /** `generateOpenApi()` 的返回(OpenAPI 文档对象) */
337
+ export type OpenApiDocument = Record<string, unknown>;
338
+ //# 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 { 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,23 +1,25 @@
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
  *
7
- * 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis):
7
+ * 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis);释放**在创建处**登记:
8
8
  *
9
9
  * ```ts
10
10
  * resources: (ctx) => {
11
11
  * const redis = createRedis(ctx.env, ctx.log) // 无 REDIS_URL → undefined
12
+ * if (redis) {
13
+ * ctx.disposer.add(() => redis.dispose()) // 关闭时 resources 阶段最后 flush
14
+ * }
12
15
  * return { redis, cache: createCache({ env: ctx.env, prefix: 'account', redis }) }
13
16
  * }
14
17
  * ```
15
18
  *
16
19
  * `REDIS_URL` 由 helper 直接读 `env`(固定约定),**不**取自 `config`;`redis` / `cache` 一律是
17
20
  * **惰性包装**:构造时不建连、不 ping,首次真正使用才建连 —— 「出 spec 不连库/Redis」结构性成立。
21
+ * 释放永远走调用方在 `ctx.disposer` 里的登记,框架不替资源持有句柄。
18
22
  */
19
- /** 一级/二级缓存默认 TTL(秒),与 `RedisCache` 默认一致 */
20
- export declare const DEFAULT_REDIS_CACHE_TTL_SECONDS = 300;
21
23
  /**
22
24
  * 构造 Redis 句柄(惰性包装:不建连、不 ping)。
23
25
  *
@@ -42,4 +44,7 @@ export interface CacheSetupParams {
42
44
  * - 都没有 → 纯进程内一级缓存(不建连)。
43
45
  */
44
46
  export declare function createCache(params: CacheSetupParams): RedisCache;
45
- //# 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"}