@taicode/common-server 4.0.0-beta.1 → 4.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (292) hide show
  1. package/output/app/index.d.ts +18 -15
  2. package/output/app/index.d.ts.map +1 -1
  3. package/output/app/index.js +330 -18
  4. package/output/auth/index.d.ts +73 -0
  5. package/output/auth/index.d.ts.map +1 -0
  6. package/output/auth/index.js +196 -0
  7. package/output/core/assemble.d.ts +64 -0
  8. package/output/core/assemble.d.ts.map +1 -0
  9. package/output/core/assemble.js +88 -0
  10. package/output/core/boot/context.d.ts +109 -0
  11. package/output/core/boot/context.d.ts.map +1 -0
  12. package/output/core/boot/context.js +145 -0
  13. package/output/core/boot/lifecycle.d.ts +206 -0
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/core/boot/lifecycle.js +255 -0
  16. package/output/core/boot/shutdown.d.ts +34 -0
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/core/boot/shutdown.js +50 -0
  19. package/output/core/boot/signal.d.ts +13 -0
  20. package/output/core/boot/signal.d.ts.map +1 -0
  21. package/output/{app → core/boot}/signal.js +3 -2
  22. package/output/core/boot/steps.d.ts +138 -0
  23. package/output/core/boot/steps.d.ts.map +1 -0
  24. package/output/core/boot/steps.js +259 -0
  25. package/output/core/entries.d.ts +151 -0
  26. package/output/core/entries.d.ts.map +1 -0
  27. package/output/core/entries.js +242 -0
  28. package/output/core/entries.test.d.ts +2 -0
  29. package/output/core/entries.test.d.ts.map +1 -0
  30. package/output/core/entries.test.js +458 -0
  31. package/output/core/env.d.ts +83 -0
  32. package/output/core/env.d.ts.map +1 -0
  33. package/output/core/env.js +124 -0
  34. package/output/{app → core}/env.test.d.ts.map +1 -1
  35. package/output/core/env.test.js +105 -0
  36. package/output/core/modules/auth-provider/index.d.ts +10 -0
  37. package/output/core/modules/auth-provider/index.d.ts.map +1 -0
  38. package/output/core/modules/auth-provider/index.js +9 -0
  39. package/output/core/modules/auth-provider/types.d.ts +71 -0
  40. package/output/core/modules/auth-provider/types.d.ts.map +1 -0
  41. package/output/core/modules/context/index.d.ts +18 -0
  42. package/output/core/modules/context/index.d.ts.map +1 -0
  43. package/output/core/modules/context/index.js +23 -0
  44. package/output/core/modules/context/index.test.d.ts.map +1 -0
  45. package/output/core/modules/cookie/index.d.ts +21 -0
  46. package/output/core/modules/cookie/index.d.ts.map +1 -0
  47. package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
  48. package/output/core/modules/identity/index.d.ts +4 -0
  49. package/output/core/modules/identity/index.d.ts.map +1 -0
  50. package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
  51. package/output/core/modules/logging/index.d.ts +5 -0
  52. package/output/core/modules/logging/index.d.ts.map +1 -0
  53. package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
  54. package/output/core/modules/openapi/index.d.ts +7 -0
  55. package/output/core/modules/openapi/index.d.ts.map +1 -0
  56. package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
  57. package/output/core/modules/permission-operations/index.d.ts +26 -0
  58. package/output/core/modules/permission-operations/index.d.ts.map +1 -0
  59. package/output/core/modules/permission-operations/index.js +36 -0
  60. package/output/core/modules/permission-operations/index.test.d.ts +2 -0
  61. package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
  62. package/output/core/modules/permission-operations/index.test.js +45 -0
  63. package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
  64. package/output/core/modules/request-id/index.d.ts.map +1 -0
  65. package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
  66. package/output/core/modules/response/index.d.ts +50 -0
  67. package/output/core/modules/response/index.d.ts.map +1 -0
  68. package/output/{app/core/response.js → core/modules/response/index.js} +31 -18
  69. package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
  70. package/output/core/modules/schema/builders.d.ts.map +1 -0
  71. package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
  72. package/output/core/modules/schema/index.d.ts +34 -0
  73. package/output/core/modules/schema/index.d.ts.map +1 -0
  74. package/output/core/modules/schema/index.js +62 -0
  75. package/output/core/modules/schema/index.test.d.ts +2 -0
  76. package/output/core/modules/schema/index.test.d.ts.map +1 -0
  77. package/output/core/modules/schema/index.test.js +55 -0
  78. package/output/core/modules/system-routes/index.d.ts +15 -0
  79. package/output/core/modules/system-routes/index.d.ts.map +1 -0
  80. package/output/core/modules/system-routes/index.js +205 -0
  81. package/output/core/resource.d.ts +56 -0
  82. package/output/core/resource.d.ts.map +1 -0
  83. package/output/core/resource.js +56 -0
  84. package/output/core/test-support/database.d.ts +89 -0
  85. package/output/core/test-support/database.d.ts.map +1 -0
  86. package/output/core/test-support/database.js +131 -0
  87. package/output/core/test-support/database.test.d.ts +2 -0
  88. package/output/core/test-support/database.test.d.ts.map +1 -0
  89. package/output/core/test-support/database.test.js +111 -0
  90. package/output/core/test-support/environment.d.ts +38 -0
  91. package/output/core/test-support/environment.d.ts.map +1 -0
  92. package/output/core/test-support/environment.js +45 -0
  93. package/output/core/test-support/service-context.d.ts +64 -0
  94. package/output/core/test-support/service-context.d.ts.map +1 -0
  95. package/output/core/test-support/service-context.js +63 -0
  96. package/output/core/test-support/service-context.test.d.ts +2 -0
  97. package/output/core/test-support/service-context.test.d.ts.map +1 -0
  98. package/output/core/test-support/service-context.test.js +55 -0
  99. package/output/core/test-support/test-server.d.ts +154 -0
  100. package/output/core/test-support/test-server.d.ts.map +1 -0
  101. package/output/core/test-support/test-server.js +114 -0
  102. package/output/core/test-support/test-server.test.d.ts +2 -0
  103. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  104. package/output/core/test-support/test-server.test.js +296 -0
  105. package/output/core/test-support/testdata.d.ts +255 -0
  106. package/output/core/test-support/testdata.d.ts.map +1 -0
  107. package/output/core/test-support/testdata.js +271 -0
  108. package/output/core/test-support/testdata.test.d.ts +2 -0
  109. package/output/core/test-support/testdata.test.d.ts.map +1 -0
  110. package/output/core/test-support/testdata.test.js +334 -0
  111. package/output/{app → core}/type-augment.d.ts +2 -2
  112. package/output/core/type-augment.d.ts.map +1 -0
  113. package/output/core/types.d.ts +582 -0
  114. package/output/core/types.d.ts.map +1 -0
  115. package/output/cors/index.d.ts +3 -0
  116. package/output/cors/index.d.ts.map +1 -0
  117. package/output/cors/index.js +28 -0
  118. package/output/index.d.ts +10 -4
  119. package/output/index.d.ts.map +1 -1
  120. package/output/index.js +25 -5
  121. package/output/rate-limit/index.d.ts +23 -0
  122. package/output/rate-limit/index.d.ts.map +1 -0
  123. package/output/rate-limit/index.js +43 -0
  124. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  125. package/output/rate-limit/limiter.d.ts.map +1 -0
  126. package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
  127. package/output/rate-limit/memory-store.d.ts.map +1 -0
  128. package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
  129. package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
  130. package/output/rate-limit/redis-store.d.ts.map +1 -0
  131. package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
  132. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  133. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  134. package/output/rate-limit/types.d.ts +45 -0
  135. package/output/rate-limit/types.d.ts.map +1 -0
  136. package/output/rate-limit/types.js +1 -0
  137. package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
  138. package/output/redis/cache.d.ts.map +1 -0
  139. package/output/redis/cache.test.d.ts +2 -0
  140. package/output/redis/cache.test.d.ts.map +1 -0
  141. package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
  142. package/output/{app/redis.d.ts → redis/index.d.ts} +12 -7
  143. package/output/redis/index.d.ts.map +1 -0
  144. package/output/{app/redis.js → redis/index.js} +15 -5
  145. package/output/redis/index.test.d.ts +2 -0
  146. package/output/{context → redis}/index.test.d.ts.map +1 -1
  147. package/output/redis/index.test.js +55 -0
  148. package/output/redis-queue/index.js +1 -1
  149. package/output/redis-queue/test-helpers.d.ts +1 -1
  150. package/output/redis-queue/test-helpers.js +1 -1
  151. package/output/redis-queue/types.d.ts +3 -12
  152. package/output/redis-queue/types.d.ts.map +1 -1
  153. package/package.json +5 -5
  154. package/output/app/auth.test.d.ts +0 -2
  155. package/output/app/auth.test.d.ts.map +0 -1
  156. package/output/app/auth.test.js +0 -148
  157. package/output/app/boot.d.ts +0 -48
  158. package/output/app/boot.d.ts.map +0 -1
  159. package/output/app/boot.js +0 -123
  160. package/output/app/config.test.d.ts +0 -2
  161. package/output/app/config.test.d.ts.map +0 -1
  162. package/output/app/config.test.js +0 -132
  163. package/output/app/context.d.ts +0 -68
  164. package/output/app/context.d.ts.map +0 -1
  165. package/output/app/context.js +0 -73
  166. package/output/app/core/cookie.d.ts +0 -25
  167. package/output/app/core/cookie.d.ts.map +0 -1
  168. package/output/app/core/identity.d.ts +0 -12
  169. package/output/app/core/identity.d.ts.map +0 -1
  170. package/output/app/core/index.d.ts +0 -44
  171. package/output/app/core/index.d.ts.map +0 -1
  172. package/output/app/core/index.js +0 -57
  173. package/output/app/core/logging.d.ts +0 -13
  174. package/output/app/core/logging.d.ts.map +0 -1
  175. package/output/app/core/openapi.d.ts +0 -17
  176. package/output/app/core/openapi.d.ts.map +0 -1
  177. package/output/app/core/request-id.d.ts.map +0 -1
  178. package/output/app/core/response.d.ts +0 -41
  179. package/output/app/core/response.d.ts.map +0 -1
  180. package/output/app/define-server.negative-cases.d.ts +0 -32
  181. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  182. package/output/app/define-server.negative-cases.js +0 -209
  183. package/output/app/define-server.test.d.ts +0 -2
  184. package/output/app/define-server.test.d.ts.map +0 -1
  185. package/output/app/define-server.test.js +0 -97
  186. package/output/app/entries.d.ts +0 -29
  187. package/output/app/entries.d.ts.map +0 -1
  188. package/output/app/entries.js +0 -64
  189. package/output/app/env.d.ts +0 -59
  190. package/output/app/env.d.ts.map +0 -1
  191. package/output/app/env.js +0 -66
  192. package/output/app/env.test.js +0 -124
  193. package/output/app/error-contract.test.d.ts +0 -2
  194. package/output/app/error-contract.test.d.ts.map +0 -1
  195. package/output/app/error-contract.test.js +0 -125
  196. package/output/app/errors.d.ts +0 -20
  197. package/output/app/errors.d.ts.map +0 -1
  198. package/output/app/errors.js +0 -51
  199. package/output/app/generate-openapi.test.d.ts +0 -2
  200. package/output/app/generate-openapi.test.d.ts.map +0 -1
  201. package/output/app/generate-openapi.test.js +0 -123
  202. package/output/app/lifecycle-logging.test.d.ts +0 -2
  203. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  204. package/output/app/lifecycle-logging.test.js +0 -155
  205. package/output/app/lifecycle.d.ts +0 -70
  206. package/output/app/lifecycle.d.ts.map +0 -1
  207. package/output/app/lifecycle.js +0 -99
  208. package/output/app/lifecycle.test.d.ts +0 -2
  209. package/output/app/lifecycle.test.d.ts.map +0 -1
  210. package/output/app/lifecycle.test.js +0 -152
  211. package/output/app/middleware/auth.d.ts +0 -71
  212. package/output/app/middleware/auth.d.ts.map +0 -1
  213. package/output/app/middleware/auth.js +0 -190
  214. package/output/app/middleware/context.d.ts +0 -13
  215. package/output/app/middleware/context.d.ts.map +0 -1
  216. package/output/app/middleware/context.js +0 -15
  217. package/output/app/middleware/cors.d.ts +0 -14
  218. package/output/app/middleware/cors.d.ts.map +0 -1
  219. package/output/app/middleware/cors.js +0 -30
  220. package/output/app/middleware/index.d.ts +0 -17
  221. package/output/app/middleware/index.d.ts.map +0 -1
  222. package/output/app/middleware/index.js +0 -16
  223. package/output/app/middleware/rate-limit.d.ts +0 -24
  224. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  225. package/output/app/middleware/rate-limit.js +0 -46
  226. package/output/app/middleware-order.test.d.ts +0 -2
  227. package/output/app/middleware-order.test.d.ts.map +0 -1
  228. package/output/app/middleware-order.test.js +0 -148
  229. package/output/app/permission-operations.d.ts +0 -48
  230. package/output/app/permission-operations.d.ts.map +0 -1
  231. package/output/app/permission-operations.js +0 -77
  232. package/output/app/permission-operations.test.d.ts +0 -2
  233. package/output/app/permission-operations.test.d.ts.map +0 -1
  234. package/output/app/permission-operations.test.js +0 -72
  235. package/output/app/permission-registration.test.d.ts +0 -2
  236. package/output/app/permission-registration.test.d.ts.map +0 -1
  237. package/output/app/permission-registration.test.js +0 -138
  238. package/output/app/redis.d.ts.map +0 -1
  239. package/output/app/registered-operations.test.d.ts +0 -2
  240. package/output/app/registered-operations.test.d.ts.map +0 -1
  241. package/output/app/registered-operations.test.js +0 -57
  242. package/output/app/request-id.test.d.ts +0 -2
  243. package/output/app/request-id.test.d.ts.map +0 -1
  244. package/output/app/request-id.test.js +0 -130
  245. package/output/app/resources.test.d.ts +0 -2
  246. package/output/app/resources.test.d.ts.map +0 -1
  247. package/output/app/resources.test.js +0 -197
  248. package/output/app/shutdown.d.ts +0 -22
  249. package/output/app/shutdown.d.ts.map +0 -1
  250. package/output/app/shutdown.js +0 -30
  251. package/output/app/shutdown.test.d.ts +0 -2
  252. package/output/app/shutdown.test.d.ts.map +0 -1
  253. package/output/app/shutdown.test.js +0 -103
  254. package/output/app/signal.d.ts +0 -12
  255. package/output/app/signal.d.ts.map +0 -1
  256. package/output/app/smoke.test.d.ts +0 -2
  257. package/output/app/smoke.test.d.ts.map +0 -1
  258. package/output/app/smoke.test.js +0 -210
  259. package/output/app/test-support.d.ts +0 -21
  260. package/output/app/test-support.d.ts.map +0 -1
  261. package/output/app/test-support.js +0 -24
  262. package/output/app/type-augment.d.ts.map +0 -1
  263. package/output/app/types.d.ts +0 -211
  264. package/output/app/types.d.ts.map +0 -1
  265. package/output/context/index.d.ts +0 -6
  266. package/output/context/index.d.ts.map +0 -1
  267. package/output/context/index.js +0 -14
  268. package/output/rate-limiter/index.d.ts +0 -5
  269. package/output/rate-limiter/index.d.ts.map +0 -1
  270. package/output/rate-limiter/index.js +0 -4
  271. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  272. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  273. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  274. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  275. package/output/rate-limiter/types.d.ts +0 -27
  276. package/output/rate-limiter/types.d.ts.map +0 -1
  277. package/output/redis-cache/index.d.ts +0 -4
  278. package/output/redis-cache/index.d.ts.map +0 -1
  279. package/output/redis-cache/index.js +0 -1
  280. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  281. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  282. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  283. package/output/schema/index.d.ts.map +0 -1
  284. /package/output/{app → core}/env.test.d.ts +0 -0
  285. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  286. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  287. /package/output/{context → core/modules/context}/index.test.js +0 -0
  288. /package/output/{app → core}/type-augment.js +0 -0
  289. /package/output/{app → core}/types.js +0 -0
  290. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  291. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  292. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
package/output/index.js CHANGED
@@ -1,6 +1,26 @@
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 { createSuccessResponse, createErrorResponse } from './core/modules/response/index.js';
20
+ export { createContext } from './core/modules/context/index.js';
21
+ export * from './redis/index.js';
22
+ export * from './rate-limit/limiter.js';
23
+ export * from './rate-limit/memory-store.js';
24
+ export * from './rate-limit/redis-store.js';
25
+ export * from './rate-limit/types.js';
3
26
  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 { MiddlewareContext, MiddlewareUnit } 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): MiddlewareUnit;
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,EAAc,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGhD;;;;;;;;;GASG;AAEH,MAAM,WAAW,0BAA0B;IACzC,yBAAyB;IACzB,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB;AAqBD,wBAAgB,yBAAyB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,cAAc,CAoB3J;AAGD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,YAAY,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAA;AAClE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,43 @@
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
+ const register = (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
+ return { name: 'rateLimit', register };
39
+ }
40
+ // ── 限流实现(与中间件同一模块)─────────────────────────────────
41
+ export { RateLimiter, createRateLimitKey } from './limiter.js';
42
+ export { MemoryRateLimitStore } from './memory-store.js';
43
+ 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"}
@@ -7,6 +7,12 @@ export declare class MemoryRateLimitStore implements RateLimitStore {
7
7
  set(key: string, info: RateLimitInfo): Promise<void>;
8
8
  delete(key: string): Promise<void>;
9
9
  cleanup(): Promise<void>;
10
+ /**
11
+ * 清空全部计数,**保留定时清理器**(它只是 GC,不属于「数据」)。
12
+ *
13
+ * 只影响本实例:进程内 store 的键本来就只在本进程可见。
14
+ */
15
+ clear(): Promise<void>;
10
16
  destroy(): void;
11
17
  }
12
18
  //# sourceMappingURL=memory-store.d.ts.map
@@ -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;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,IAAI,IAAI;CAOhB"}
@@ -36,6 +36,14 @@ export class MemoryRateLimitStore {
36
36
  }
37
37
  }
38
38
  }
39
+ /**
40
+ * 清空全部计数,**保留定时清理器**(它只是 GC,不属于「数据」)。
41
+ *
42
+ * 只影响本实例:进程内 store 的键本来就只在本进程可见。
43
+ */
44
+ async clear() {
45
+ this.store.clear();
46
+ }
39
47
  destroy() {
40
48
  if (this.cleanupInterval) {
41
49
  clearInterval(this.cleanupInterval);
@@ -39,6 +39,24 @@ export declare class RedisRateLimitStore implements RateLimitStore {
39
39
  delete(key: string): Promise<void>;
40
40
  /** Redis 的键自带过期时间,无需额外清理 */
41
41
  cleanup(): Promise<void>;
42
+ /**
43
+ * 清空**本前缀下**的全部计数(`SCAN` + `DEL`,不用 `KEYS`——后者在大库上会阻塞)。
44
+ *
45
+ * `fallback` 一并清掉:降级期间写入的计数也是计数,漏清就会留下「只有降级那段没重置」
46
+ * 的诡异状态。
47
+ *
48
+ * 前缀是构造参数,所以它只会删自己这批键,不会碰同一 Redis 上其它用途的数据。
49
+ *
50
+ * ## 已知不足:扫描中途失败只记日志、不抛
51
+ *
52
+ * 与 `get` / `set` 的降级策略保持一致(Redis 抖动不该让业务抛错),但 `clear()` 的
53
+ * 唯一用途是「让下一条用例从零配额开始」:静默失败 = 旧桶保留 = 又变成「顺序相关的
54
+ * 偶发红」,正是本方法要消灭的形态。
55
+ *
56
+ * TODO(需单独评审,行为变更):测试路径上让失败冒泡(或返回 `boolean` 交由
57
+ * `cleanServiceUnits()` 判错),生产路径继续降级。当前 Redis 侧无对应单测。
58
+ */
59
+ clear(): Promise<void>;
42
60
  private buildKey;
43
61
  private logError;
44
62
  }
@@ -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;;;;;;;;;;;;;;;;OAgBG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBnC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,QAAQ;CAOjB"}
@@ -101,6 +101,41 @@ export class RedisRateLimitStore {
101
101
  async cleanup() {
102
102
  await this.fallback.cleanup?.();
103
103
  }
104
+ /**
105
+ * 清空**本前缀下**的全部计数(`SCAN` + `DEL`,不用 `KEYS`——后者在大库上会阻塞)。
106
+ *
107
+ * `fallback` 一并清掉:降级期间写入的计数也是计数,漏清就会留下「只有降级那段没重置」
108
+ * 的诡异状态。
109
+ *
110
+ * 前缀是构造参数,所以它只会删自己这批键,不会碰同一 Redis 上其它用途的数据。
111
+ *
112
+ * ## 已知不足:扫描中途失败只记日志、不抛
113
+ *
114
+ * 与 `get` / `set` 的降级策略保持一致(Redis 抖动不该让业务抛错),但 `clear()` 的
115
+ * 唯一用途是「让下一条用例从零配额开始」:静默失败 = 旧桶保留 = 又变成「顺序相关的
116
+ * 偶发红」,正是本方法要消灭的形态。
117
+ *
118
+ * TODO(需单独评审,行为变更):测试路径上让失败冒泡(或返回 `boolean` 交由
119
+ * `cleanServiceUnits()` 判错),生产路径继续降级。当前 Redis 侧无对应单测。
120
+ */
121
+ async clear() {
122
+ await this.fallback.clear?.();
123
+ if (!this.redis?.isReady)
124
+ return;
125
+ try {
126
+ let cursor = '0';
127
+ do {
128
+ const reply = await this.redis.scan(cursor, { MATCH: `${this.prefix}*`, COUNT: 200 });
129
+ cursor = reply.cursor;
130
+ if (reply.keys.length > 0) {
131
+ await this.redis.del(reply.keys);
132
+ }
133
+ } while (cursor !== '0');
134
+ }
135
+ catch (error) {
136
+ this.logError('clear', error);
137
+ }
138
+ }
104
139
  buildKey(key) {
105
140
  return `${this.prefix}${key}`;
106
141
  }
@@ -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', () => {
@@ -0,0 +1,45 @@
1
+ export interface RateLimitInfo {
2
+ count: number;
3
+ windowStart: Date;
4
+ windowEnd: Date;
5
+ }
6
+ export interface RateLimitResult {
7
+ allowed: boolean;
8
+ remaining: number;
9
+ resetAt: Date;
10
+ limit: number;
11
+ window: number;
12
+ }
13
+ export interface RateLimitStore {
14
+ get(key: string): Promise<RateLimitInfo | null>;
15
+ set(key: string, info: RateLimitInfo): Promise<void>;
16
+ delete(key: string): Promise<void>;
17
+ cleanup?(): Promise<void>;
18
+ /**
19
+ * **清空全部计数**(不是「删一个键」——那是 `delete`)。
20
+ *
21
+ * ## 为什么这是存储层的职责,而不是「测试自己换个 key」
22
+ *
23
+ * 限流的键是 `ip:` / `user:` / `path:` —— 由请求本身的属性拼成,测试**改不了**(除非
24
+ * 伪造 `remoteAddress`)。没有 `clear()` 的时候,想「从干净配额开始」只能给每次请求换一个
25
+ * 假 IP:一个测试文件里铺一串 `ipSeq` 计数器,注释里还得解释「为什么不能回退序号」。
26
+ *
27
+ * 这完全是**测试在迁就实现细节**:用例真正想表达的是「这条用例从零计数开始」,而它被
28
+ * 迫描述成「这次请用 10.31.0.7 访问」。有了 `clear()`,`beforeEach` 一行搞定,而且
29
+ * 不再需要任何假 IP——测试与真实请求的差别越小,证明力越强。
30
+ *
31
+ * ## 可选,但不是可忽略
32
+ *
33
+ * 声明为可选只为了让**第三方自定义 store** 不必立刻实现它;框架自带的两个实现都有。
34
+ * 测试里用 `store.clear?.()` 而不是硬调。
35
+ */
36
+ clear?(): Promise<void>;
37
+ }
38
+ export interface RateLimitConfig {
39
+ limit: number;
40
+ window: number;
41
+ }
42
+ export interface RateLimiterOptions {
43
+ store?: RateLimitStore;
44
+ }
45
+ //# sourceMappingURL=types.d.ts.map
@@ -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;IACzB;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACxB;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"}
@@ -1,21 +1,23 @@
1
- import { createRedisClient, RedisCache } from '../redis-cache/index.js';
1
+ import { createRedisClient, RedisCache } from './cache.js';
2
2
  /**
3
3
  * 引导期 Redis / cache helper(N2 §2.13)。
4
4
  *
5
- * 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis):
5
+ * 在 `resources(ctx)` 槽位里按需调用(框架自己**不**创建 Redis);释放**在创建处**登记:
6
6
  *
7
7
  * ```ts
8
8
  * resources: (ctx) => {
9
9
  * const redis = createRedis(ctx.env, ctx.log) // 无 REDIS_URL → undefined
10
+ * if (redis) {
11
+ * ctx.disposer.add(() => redis.dispose()) // 关闭时 resources 阶段最后 flush
12
+ * }
10
13
  * return { redis, cache: createCache({ env: ctx.env, prefix: 'account', redis }) }
11
14
  * }
12
15
  * ```
13
16
  *
14
17
  * `REDIS_URL` 由 helper 直接读 `env`(固定约定),**不**取自 `config`;`redis` / `cache` 一律是
15
18
  * **惰性包装**:构造时不建连、不 ping,首次真正使用才建连 —— 「出 spec 不连库/Redis」结构性成立。
19
+ * 释放永远走调用方在 `ctx.disposer` 里的登记,框架不替资源持有句柄。
16
20
  */
17
- /** 一级/二级缓存默认 TTL(秒),与 `RedisCache` 默认一致 */
18
- export const DEFAULT_REDIS_CACHE_TTL_SECONDS = 300;
19
21
  /**
20
22
  * 构造 Redis 句柄(惰性包装:不建连、不 ping)。
21
23
  *
@@ -36,7 +38,13 @@ export function createRedis(env, log) {
36
38
  await client.quit().catch(() => undefined);
37
39
  }
38
40
  else {
39
- client.destroy();
41
+ // 未建连(从未使用过)时 `destroy()` 会抛 "The client is closed";释放必须是幂等的
42
+ try {
43
+ client.destroy();
44
+ }
45
+ catch {
46
+ // 已关闭:无事可做
47
+ }
40
48
  }
41
49
  },
42
50
  };
@@ -59,3 +67,5 @@ export function createCache(params) {
59
67
  params.log?.debug({}, '[redis] REDIS_URL not configured; cache falls back to in-process L1');
60
68
  return new RedisCache({ prefix: params.prefix });
61
69
  }
70
+ // ── 缓存 / 客户端实现(与引导期 helper 同一模块)───────────────
71
+ export { RedisCache, createRedisClient } from './cache.js';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../source/context/index.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../source/redis/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RedisCache } from './cache.js';
3
+ import { createCache, createRedis } from './index.js';
4
+ function createRecordingLogger() {
5
+ const entries = [];
6
+ const record = (level) => (obj, msg) => {
7
+ entries.push({ level, obj, msg });
8
+ };
9
+ return { entries, debug: record('debug'), info: record('info'), warn: record('warn'), error: record('error') };
10
+ }
11
+ /** 测试用 `ConfigEnv`(固定读取约定:`ENV` / `API_PORT` / `LOG_LEVEL` / `DATABASE_URL` / `REDIS_URL`) */
12
+ function createTestConfigEnv(overrides = {}) {
13
+ return { ENV: 'test', API_PORT: '', LOG_LEVEL: 'info', DATABASE_URL: '', REDIS_URL: undefined, ...overrides };
14
+ }
15
+ describe('createRedis', () => {
16
+ it('无 REDIS_URL → undefined,并留一条 debug 线索', () => {
17
+ const log = createRecordingLogger();
18
+ const handle = createRedis(createTestConfigEnv({ REDIS_URL: undefined }), log);
19
+ expect(handle).toBeUndefined();
20
+ expect(log.entries.filter((entry) => entry.level === 'debug')).toHaveLength(1);
21
+ });
22
+ it('有 REDIS_URL → 句柄(client + dispose),构造期不建连', () => {
23
+ const handle = createRedis(createTestConfigEnv({ REDIS_URL: 'redis://127.0.0.1:6379/0' }));
24
+ expect(handle).toBeDefined();
25
+ expect(handle?.client).toBeDefined();
26
+ expect(typeof handle?.dispose).toBe('function');
27
+ // 惰性:尚未 connect,客户端未打开
28
+ expect((handle?.client).isOpen).toBe(false);
29
+ });
30
+ it('dispose:未建连时直接销毁客户端,不抛错、可重复调用', async () => {
31
+ const handle = createRedis(createTestConfigEnv({ REDIS_URL: 'redis://127.0.0.1:6379/0' }));
32
+ await expect(handle?.dispose()).resolves.toBeUndefined();
33
+ await expect(handle?.dispose()).resolves.toBeUndefined();
34
+ });
35
+ });
36
+ describe('createCache', () => {
37
+ it('无 redis 句柄、无 REDIS_URL → 纯进程内 L1(不建连)', () => {
38
+ const log = createRecordingLogger();
39
+ const cache = createCache({ env: createTestConfigEnv({ REDIS_URL: undefined }), prefix: 'test:', log });
40
+ expect(cache).toBeInstanceOf(RedisCache);
41
+ expect(log.entries.filter((entry) => entry.level === 'debug')).toHaveLength(1);
42
+ });
43
+ it('复用传入的 redis 句柄(一条连接同时给 cache 与其它消费者)', () => {
44
+ const handle = createRedis(createTestConfigEnv({ REDIS_URL: 'redis://127.0.0.1:6379/0' }));
45
+ const cache = createCache({ env: createTestConfigEnv(), prefix: 'test:', redis: handle });
46
+ expect(cache).toBeInstanceOf(RedisCache);
47
+ });
48
+ it('无句柄但有 REDIS_URL → 自建客户端(由 RedisCache 自己持有)', () => {
49
+ const cache = createCache({
50
+ env: createTestConfigEnv({ REDIS_URL: 'redis://127.0.0.1:6379/0' }),
51
+ prefix: 'test:',
52
+ });
53
+ expect(cache).toBeInstanceOf(RedisCache);
54
+ });
55
+ });
@@ -2,5 +2,5 @@
2
2
  export { RedisQueueProvider } from './redis-queue-provider.js';
3
3
  export { RedisQueueConsumer } from './redis-queue-consumer.js';
4
4
  export { RedisQueueBatchConsumer } from './redis-queue-batch-consumer.js';
5
- // 测试辅助函数导出
5
+ // 测试辅助函数导出(与队列实现同一出口:谁提供的功能,谁提供配套的测试工具)
6
6
  export * as test from './test-helpers.js';
@@ -14,7 +14,7 @@ import type { AnyRegistry, QueueStats, RedisQueueRegistry, Task } from './types.
14
14
  *
15
15
  * @example
16
16
  * ```ts
17
- * import { dispatchQueueTask } from '@taicode/common-server/redis-queue/test-helpers'
17
+ * import { dispatchQueueTask } from '@taicode/common-server/redis-queue'
18
18
  * import { RedisQueueProvider, RedisQueueConsumer } from '@taicode/common-server/redis-queue'
19
19
  *
20
20
  * describe('My Queue Tests', () => {
@@ -11,7 +11,7 @@ import { RedisQueueConsumer } from './redis-queue-consumer.js';
11
11
  *
12
12
  * @example
13
13
  * ```ts
14
- * import { dispatchQueueTask } from '@taicode/common-server/redis-queue/test-helpers'
14
+ * import { dispatchQueueTask } from '@taicode/common-server/redis-queue'
15
15
  * import { RedisQueueProvider, RedisQueueConsumer } from '@taicode/common-server/redis-queue'
16
16
  *
17
17
  * describe('My Queue Tests', () => {
@@ -109,24 +109,18 @@ export interface RedisQueueCommonConfig {
109
109
  /**
110
110
  * 队列生产者配置
111
111
  */
112
- export interface RedisQueueProviderConfig {
113
- redisUrl?: string;
114
- redisClient?: RedisClientType;
115
- cleanupDelay?: number;
112
+ export interface RedisQueueProviderConfig extends RedisQueueCommonConfig {
116
113
  processingDelay?: number;
117
114
  }
118
115
  /**
119
116
  * 队列消费者配置
120
117
  */
121
- export interface RedisQueueConsumerConfig<T extends TaskData = TaskData> {
122
- redisUrl?: string;
123
- redisClient?: RedisClientType;
118
+ export interface RedisQueueConsumerConfig<T extends TaskData = TaskData> extends RedisQueueCommonConfig {
124
119
  handler: TaskHandler<T>;
125
120
  maxRetries?: number;
126
121
  concurrency?: number;
127
122
  consumerInterval?: number;
128
123
  processingTimeout?: number;
129
- cleanupDelay?: number;
130
124
  }
131
125
  /**
132
126
  * 任务处理器函数
@@ -135,14 +129,11 @@ export type TaskHandler<T extends TaskData = TaskData> = (data: T) => Promise<Ca
135
129
  /**
136
130
  * 批量消费者配置
137
131
  */
138
- export interface BatchConsumerConfig<T extends TaskData = TaskData> {
139
- redisUrl?: string;
140
- redisClient?: RedisClientType;
132
+ export interface BatchConsumerConfig<T extends TaskData = TaskData> extends RedisQueueCommonConfig {
141
133
  handler: BatchTaskHandler<T>;
142
134
  batchSize?: number;
143
135
  maxRetries?: number;
144
136
  concurrency?: number;
145
- cleanupDelay?: number;
146
137
  consumerInterval?: number;
147
138
  processingTimeout?: number;
148
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/redis-queue/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../source/redis-queue/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AAEH,MAAM,WAAW,kBAAkB;CAElC;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAA;AAEtE;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAE7C;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;CAAE,CAAA;AAE1D;;GAEG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,GAAG,QAAQ;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,CAAC,CAAA;IACP,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,sBAAsB;IACtE,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,CAAE,SAAQ,sBAAsB;IACrG,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAA;AAE3G;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,CAAE,SAAQ,sBAAsB;IAChG,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taicode/common-server",
3
- "version": "4.0.0-beta.1",
3
+ "version": "4.0.0-beta.10",
4
4
  "author": "Alain",
5
5
  "license": "ISC",
6
6
  "description": "",
@@ -27,7 +27,7 @@
27
27
  "@fastify/cookie": "^11",
28
28
  "@fastify/cors": "^11",
29
29
  "@fastify/swagger": "^9",
30
- "@scalar/fastify-api-reference": "^1",
30
+ "@taicode/auth-sdk": "^1.0.9",
31
31
  "dotenv": "^17.3.1",
32
32
  "fastify-type-provider-zod": "^6",
33
33
  "zod": "^4.3.6"
@@ -35,7 +35,7 @@
35
35
  "peerDependencies": {
36
36
  "fastify": "^5.5.0",
37
37
  "redis": "^5.11.0",
38
- "@taicode/common-base": "^4.0.0-beta.1"
38
+ "@taicode/common-base": "^4.0.0-beta.10"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@eslint/js": "^9.39.3",
@@ -49,14 +49,14 @@
49
49
  "redis": "^5.11.0",
50
50
  "typescript-eslint": "^8.56.1",
51
51
  "vitest": "^3.2.4",
52
- "@taicode/common-base": "^4.0.0-beta.1"
52
+ "@taicode/common-base": "^4.0.0-beta.10"
53
53
  },
54
54
  "scripts": {
55
55
  "test": "vitest run",
56
56
  "test:ci": "vitest run",
57
57
  "lint": "eslint && tsc --noEmit",
58
58
  "typecheck": "tsc --noEmit",
59
- "build": "tsc -p tsconfig.json",
59
+ "build": "rm -rf output && tsc -p tsconfig.json",
60
60
  "dev": "tsc -p tsconfig.json --watch"
61
61
  }
62
62
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=auth.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auth.test.d.ts","sourceRoot":"","sources":["../../source/app/auth.test.ts"],"names":[],"mappings":""}