@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
@@ -1,59 +0,0 @@
1
- /**
2
- * 默认 env 文件分层(低 → 高优先级,后者覆盖前者):
3
- *
4
- * .env.dev 已提交进仓库的 dev 默认值(克隆后 `pnpm run dev` 开箱即用)
5
- * .env 本机私有覆盖(已被 gitignore)
6
- * .env.local 本机更高优先级覆盖(已被 gitignore)
7
- *
8
- * 真实环境变量(进程/容器注入)优先级最高,任何 .env 文件都不会覆盖它们 —— 这条保证让 CI、
9
- * helm、`docker run -e` 等显式注入始终生效,也避免测试探针
10
- * (`check-test-database-isolation`)注入的连接串被仓库里的默认值顶掉。
11
- *
12
- * 解析器与四家现状保持一致,统一使用 `dotenv`,以保证多行值、引号/转义、`export` 前缀等
13
- * 细节语义完全等价(设计 §3.1 ①:与现状逐字一致)。
14
- */
15
- export declare const ENV_FILE_LAYERS: readonly [".env.dev", ".env", ".env.local"];
16
- /** `ENV=test` 时追加的最高层(设计 §3.1 ①) */
17
- export declare const TEST_ENV_FILE_LAYER = ".env.test.local";
18
- /** `LOG_LEVEL` 未设置时的默认级别(设计 §3.1 ① / §5.1 `logging`) */
19
- export declare const DEFAULT_LOG_LEVEL = "info";
20
- /**
21
- * 按分层顺序加载 env 文件(设计 §3.1 ①)。
22
- *
23
- * `startServer` 内部会**固定调用**本函数;工具进程(`vitest.config.ts` / `scripts/*.ts`)不在
24
- * `startServer` 覆盖范围内,须自行显式调用本函数。
25
- *
26
- * @param extraLayers 追加在最高优先级的额外层,例如测试配置传 `['.env.test.local']`
27
- */
28
- export declare function loadEnvFiles(extraLayers?: readonly string[]): void;
29
- /**
30
- * 框架在配置前固定加载的 env(N2 §2.2)。
31
- *
32
- * 只读固定约定值:`ENV` / `API_PORT` / `LOG_LEVEL` / `DATABASE_URL` / `REDIS_URL`;
33
- * `debug` 由 `ENV === 'development'` 派生;**绝不回退 `PORT`**(`PORT` 属 nginx)。
34
- *
35
- * **不给索引签名**:访问 `env.XXX` 拼错即编译错误。各仓若有额外 env,不扩展这个类型 ——
36
- * 走 `process.env.XXX`(在 `resources(ctx)` 里读),框架不背书。
37
- */
38
- export interface ConfigEnv {
39
- /** 运行环境标记;`debug` 由 `ENV === 'development'` 派生 */
40
- ENV: string;
41
- /** 监听端口;**只读 `API_PORT`,绝不回退 `PORT`** */
42
- API_PORT: string;
43
- /** 日志级别;默认 `info` */
44
- LOG_LEVEL: string;
45
- /** 数据层连接串(`resources` 只读它构造数据层客户端) */
46
- DATABASE_URL: string;
47
- /** Redis 连接串;缺省 → redis 资源为 `undefined` */
48
- REDIS_URL?: string;
49
- }
50
- /** 读取固定 env(不加载文件);`startServer` 会先 `loadRuntimeEnvFiles()` 再调用本函数 */
51
- export declare function resolveConfigEnv(): ConfigEnv;
52
- /**
53
- * `startServer` 最早阶段的固定 env 加载(设计 §3.1 ①):
54
- * `.env.dev < .env < .env.local`(外部注入最高),`ENV=test` 时再追加 `.env.test.local`。
55
- *
56
- * 注意 `generateOpenApi` **不**调用本函数(它不要求配置有效,设计 §3.2 规则 A/B)。
57
- */
58
- export declare function loadRuntimeEnvFiles(): ConfigEnv;
59
- //# sourceMappingURL=env.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../source/app/env.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,6CAA8C,CAAA;AAE1E,oCAAoC;AACpC,eAAO,MAAM,mBAAmB,oBAAoB,CAAA;AAEpD,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,SAAS,CAAA;AAUvC;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,SAAS,MAAM,EAAO,GAAG,IAAI,CAUtE;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAA;IACX,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,qBAAqB;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAA;IACpB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,IAAI,SAAS,CAQ5C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,SAAS,CAM/C"}
package/output/app/env.js DELETED
@@ -1,66 +0,0 @@
1
- import { config } from 'dotenv';
2
- /**
3
- * 默认 env 文件分层(低 → 高优先级,后者覆盖前者):
4
- *
5
- * .env.dev 已提交进仓库的 dev 默认值(克隆后 `pnpm run dev` 开箱即用)
6
- * .env 本机私有覆盖(已被 gitignore)
7
- * .env.local 本机更高优先级覆盖(已被 gitignore)
8
- *
9
- * 真实环境变量(进程/容器注入)优先级最高,任何 .env 文件都不会覆盖它们 —— 这条保证让 CI、
10
- * helm、`docker run -e` 等显式注入始终生效,也避免测试探针
11
- * (`check-test-database-isolation`)注入的连接串被仓库里的默认值顶掉。
12
- *
13
- * 解析器与四家现状保持一致,统一使用 `dotenv`,以保证多行值、引号/转义、`export` 前缀等
14
- * 细节语义完全等价(设计 §3.1 ①:与现状逐字一致)。
15
- */
16
- export const ENV_FILE_LAYERS = ['.env.dev', '.env', '.env.local'];
17
- /** `ENV=test` 时追加的最高层(设计 §3.1 ①) */
18
- export const TEST_ENV_FILE_LAYER = '.env.test.local';
19
- /** `LOG_LEVEL` 未设置时的默认级别(设计 §3.1 ① / §5.1 `logging`) */
20
- export const DEFAULT_LOG_LEVEL = 'info';
21
- /**
22
- * 首次 import 时的真实环境变量快照(此时还没有任何 .env 文件被加载)。
23
- * 加载结束后原样恢复,实现「外部注入 > .env.local > .env > .env.dev」。
24
- */
25
- const EXTERNAL_ENV = new Map(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
26
- /**
27
- * 按分层顺序加载 env 文件(设计 §3.1 ①)。
28
- *
29
- * `startServer` 内部会**固定调用**本函数;工具进程(`vitest.config.ts` / `scripts/*.ts`)不在
30
- * `startServer` 覆盖范围内,须自行显式调用本函数。
31
- *
32
- * @param extraLayers 追加在最高优先级的额外层,例如测试配置传 `['.env.test.local']`
33
- */
34
- export function loadEnvFiles(extraLayers = []) {
35
- const layers = [...ENV_FILE_LAYERS, ...extraLayers];
36
- layers.forEach((path, index) => {
37
- // 第一层不覆盖已有值,后续层逐层覆盖前一层
38
- config({ path, override: index > 0 });
39
- });
40
- for (const [key, value] of EXTERNAL_ENV) {
41
- process.env[key] = value;
42
- }
43
- }
44
- /** 读取固定 env(不加载文件);`startServer` 会先 `loadRuntimeEnvFiles()` 再调用本函数 */
45
- export function resolveConfigEnv() {
46
- return {
47
- ENV: process.env.ENV ?? '',
48
- API_PORT: process.env.API_PORT ?? '',
49
- LOG_LEVEL: process.env.LOG_LEVEL ?? DEFAULT_LOG_LEVEL,
50
- DATABASE_URL: process.env.DATABASE_URL ?? '',
51
- REDIS_URL: process.env.REDIS_URL,
52
- };
53
- }
54
- /**
55
- * `startServer` 最早阶段的固定 env 加载(设计 §3.1 ①):
56
- * `.env.dev < .env < .env.local`(外部注入最高),`ENV=test` 时再追加 `.env.test.local`。
57
- *
58
- * 注意 `generateOpenApi` **不**调用本函数(它不要求配置有效,设计 §3.2 规则 A/B)。
59
- */
60
- export function loadRuntimeEnvFiles() {
61
- loadEnvFiles();
62
- if ((process.env.ENV ?? '') === 'test') {
63
- loadEnvFiles([TEST_ENV_FILE_LAYER]);
64
- }
65
- return resolveConfigEnv();
66
- }
@@ -1,124 +0,0 @@
1
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
- /**
6
- * env 加载契约(设计 §3.1 ①)。
7
- *
8
- * 固定约定:`.env.dev` < `.env` < `.env.local`(外部注入最高),`ENV=test` 追加 `.env.test.local`;
9
- * 固定读取 `ENV` / `API_PORT` / `LOG_LEVEL`,端口**只读 `API_PORT`,绝不回退 `PORT`**。
10
- */
11
- const ENV_KEYS = [
12
- 'LAYER',
13
- 'OVERRIDE',
14
- 'ONLY_DEV',
15
- 'ONLY_ENV',
16
- 'ONLY_LOCAL',
17
- 'EXTERNAL_ONLY',
18
- 'ONLY_TEST',
19
- 'EXPORTED',
20
- 'QUOTED',
21
- 'MULTI',
22
- 'HASHED',
23
- 'ENV',
24
- 'API_PORT',
25
- 'PORT',
26
- 'LOG_LEVEL',
27
- ];
28
- function clearEnvKeys() {
29
- for (const key of ENV_KEYS) {
30
- delete process.env[key];
31
- }
32
- }
33
- /** 重新求值 `env.js`(`EXTERNAL_ENV` 快照在模块首次求值时抓取) */
34
- async function importEnvModule() {
35
- vi.resetModules();
36
- return import('./env.js');
37
- }
38
- describe('env 加载(设计 §3.1 ①)', () => {
39
- let previousCwd;
40
- let directory;
41
- beforeEach(() => {
42
- previousCwd = process.cwd();
43
- directory = mkdtempSync(join(tmpdir(), 'common-app-env-'));
44
- writeFileSync(join(directory, '.env.dev'), 'LAYER=dev\nONLY_DEV=dev\nOVERRIDE=dev\n');
45
- writeFileSync(join(directory, '.env'), 'LAYER=env\nONLY_ENV=env\nOVERRIDE=env\n');
46
- writeFileSync(join(directory, '.env.local'), 'LAYER=local\nONLY_LOCAL=local\nOVERRIDE=local\n');
47
- process.chdir(directory);
48
- clearEnvKeys();
49
- });
50
- afterEach(() => {
51
- process.chdir(previousCwd);
52
- rmSync(directory, { recursive: true, force: true });
53
- clearEnvKeys();
54
- vi.resetModules();
55
- });
56
- it('分层优先级:外部注入 > .env.local > .env > .env.dev', async () => {
57
- process.env.LAYER = 'external';
58
- process.env.EXTERNAL_ONLY = 'external';
59
- const { loadEnvFiles } = await importEnvModule();
60
- loadEnvFiles();
61
- expect(process.env.LAYER).toBe('external');
62
- expect(process.env.EXTERNAL_ONLY).toBe('external');
63
- expect(process.env.OVERRIDE).toBe('local');
64
- expect(process.env.ONLY_DEV).toBe('dev');
65
- expect(process.env.ONLY_ENV).toBe('env');
66
- expect(process.env.ONLY_LOCAL).toBe('local');
67
- });
68
- it('追加层(如 .env.test.local)优先级最高', async () => {
69
- writeFileSync(join(directory, '.env.test.local'), 'OVERRIDE=test-local\nONLY_TEST=1\n');
70
- const { loadEnvFiles } = await importEnvModule();
71
- loadEnvFiles(['.env.test.local']);
72
- expect(process.env.OVERRIDE).toBe('test-local');
73
- expect(process.env.ONLY_TEST).toBe('1');
74
- });
75
- it('缺失的 env 文件不报错,已有层照常生效', async () => {
76
- rmSync(join(directory, '.env.local'));
77
- const { loadEnvFiles } = await importEnvModule();
78
- expect(() => loadEnvFiles()).not.toThrow();
79
- expect(process.env.ONLY_DEV).toBe('dev');
80
- expect(process.env.OVERRIDE).toBe('env');
81
- });
82
- it('dotenv 语义:export 前缀 / 引号 / 转义 / 多行 / 行尾注释(与四家现状逐字一致)', async () => {
83
- writeFileSync(join(directory, '.env.local'), ['export EXPORTED=1', 'QUOTED="a b"', 'MULTI="line1\\nline2"', 'HASHED=value # 行尾注释'].join('\n'));
84
- const { loadEnvFiles } = await importEnvModule();
85
- loadEnvFiles();
86
- expect(process.env.EXPORTED).toBe('1');
87
- expect(process.env.QUOTED).toBe('a b');
88
- expect(process.env.MULTI).toBe('line1\nline2');
89
- expect(process.env.HASHED).toBe('value');
90
- });
91
- it('loadRuntimeEnvFiles:ENV=test 时追加 .env.test.local', async () => {
92
- writeFileSync(join(directory, '.env.dev'), 'LAYER=dev\nOVERRIDE=dev\nENV=test\nAPI_PORT=4321\n');
93
- writeFileSync(join(directory, '.env.test.local'), 'LOG_LEVEL=debug\nONLY_TEST=1\n');
94
- const { loadRuntimeEnvFiles } = await importEnvModule();
95
- const env = loadRuntimeEnvFiles();
96
- expect(env.ENV).toBe('test');
97
- expect(env.API_PORT).toBe('4321');
98
- expect(env.LOG_LEVEL).toBe('debug');
99
- expect(process.env.ONLY_TEST).toBe('1');
100
- });
101
- it('loadRuntimeEnvFiles:ENV 非 test 时不加载 .env.test.local', async () => {
102
- writeFileSync(join(directory, '.env.local'), 'ENV=development\n');
103
- writeFileSync(join(directory, '.env.test.local'), 'LOG_LEVEL=debug\nONLY_TEST=1\n');
104
- const { loadRuntimeEnvFiles } = await importEnvModule();
105
- const env = loadRuntimeEnvFiles();
106
- expect(env.ENV).toBe('development');
107
- expect(env.LOG_LEVEL).toBe('info');
108
- expect(process.env.ONLY_TEST).toBeUndefined();
109
- });
110
- it('固定读取:只认 API_PORT(绝不回退 PORT),LOG_LEVEL 默认 info,ENV 缺省空串', async () => {
111
- process.env.PORT = '9999';
112
- const { resolveConfigEnv } = await importEnvModule();
113
- const env = resolveConfigEnv();
114
- expect(env.API_PORT).toBe('');
115
- expect(env.LOG_LEVEL).toBe('info');
116
- expect(env.ENV).toBe('');
117
- });
118
- it('固有常量:分层顺序 / 测试层 / 默认日志级别', async () => {
119
- const { ENV_FILE_LAYERS, TEST_ENV_FILE_LAYER, DEFAULT_LOG_LEVEL } = await importEnvModule();
120
- expect(ENV_FILE_LAYERS).toEqual(['.env.dev', '.env', '.env.local']);
121
- expect(TEST_ENV_FILE_LAYER).toBe('.env.test.local');
122
- expect(DEFAULT_LOG_LEVEL).toBe('info');
123
- });
124
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=error-contract.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"error-contract.test.d.ts","sourceRoot":"","sources":["../../source/app/error-contract.test.ts"],"names":[],"mappings":""}
@@ -1,125 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { z } from 'zod';
3
- import { defineServer, startServer } from './entries.js';
4
- import { SystemError, UserError } from './errors.js';
5
- /**
6
- * 错误契约——**强制、不可配置**。
7
- *
8
- * 响应信封 `{ status, message, data }`(错误时 `data: null`,`status` 放错误码字符串);
9
- * 映射固定:`UserError`→400(`error.status` 字面量)/ `SystemError`→500 / zod→400 / 未知→500 /
10
- * 未匹配路由→404。错误只给稳定**码**,文案不归框架。
11
- */
12
- /** zod validator 由 core 的 `schema` 统一装配,运行时按 zod 校验(类型上仍是 FastifySchema) */
13
- const validatedQuerySchema = {
14
- querystring: z.object({ count: z.coerce.number().int().min(1) }),
15
- };
16
- function createErrorContractServer() {
17
- return defineServer({
18
- app: { service: 'error-contract', title: 'Error Contract', version: '1.0.0' },
19
- resources: () => ({}),
20
- config: () => ({}),
21
- services: () => ({}),
22
- middlewares: () => [],
23
- routers: (ctx) => {
24
- ctx.app.get('/ok', () => ({ hello: 'world' }));
25
- ctx.app.get('/envelope', () => ({ status: 'CUSTOM', message: 'm', data: 1 }));
26
- ctx.app.get('/user-error', () => {
27
- throw new UserError('INVALID_TOKEN', undefined, 'token 无效');
28
- });
29
- ctx.app.get('/system-error', () => {
30
- throw new SystemError('DATABASE_UNAVAILABLE', undefined, '数据库不可用');
31
- });
32
- ctx.app.get('/unknown-error', () => {
33
- throw new Error('boom');
34
- });
35
- ctx.app.get('/validated', { schema: validatedQuerySchema }, (request) => ({
36
- count: request.query.count,
37
- }));
38
- },
39
- });
40
- }
41
- describe('错误契约', () => {
42
- it('成功信封:{ status: SUCCESS, message: ok, data }', async () => {
43
- const app = await startServer(createErrorContractServer(), { listen: false });
44
- const response = await app.inject({ method: 'GET', url: '/ok' });
45
- expect(response.statusCode).toBe(200);
46
- expect(response.json()).toEqual({ status: 'SUCCESS', message: 'ok', data: { hello: 'world' } });
47
- await app.close();
48
- });
49
- it('已是信封的载荷不被二次包裹', async () => {
50
- const app = await startServer(createErrorContractServer(), { listen: false });
51
- const response = await app.inject({ method: 'GET', url: '/envelope' });
52
- expect(response.statusCode).toBe(200);
53
- expect(response.json()).toEqual({ status: 'CUSTOM', message: 'm', data: 1 });
54
- await app.close();
55
- });
56
- it('UserError → 400(取 `error.status` 字面量),status 放错误码、data 为 null', async () => {
57
- const app = await startServer(createErrorContractServer(), { listen: false });
58
- const response = await app.inject({ method: 'GET', url: '/user-error' });
59
- expect(response.statusCode).toBe(400);
60
- expect(response.json()).toEqual({ status: 'INVALID_TOKEN', message: 'token 无效', data: null });
61
- await app.close();
62
- });
63
- it('SystemError → 500(取 `error.status` 字面量)', async () => {
64
- const app = await startServer(createErrorContractServer(), { listen: false });
65
- const response = await app.inject({ method: 'GET', url: '/system-error' });
66
- expect(response.statusCode).toBe(500);
67
- expect(response.json()).toEqual({ status: 'DATABASE_UNAVAILABLE', message: '数据库不可用', data: null });
68
- await app.close();
69
- });
70
- it('未知错误 → 500 + UNKNOWN_ERROR', async () => {
71
- const app = await startServer(createErrorContractServer(), { listen: false });
72
- const response = await app.inject({ method: 'GET', url: '/unknown-error' });
73
- expect(response.statusCode).toBe(500);
74
- expect(response.json()).toEqual({ status: 'UNKNOWN_ERROR', message: 'boom', data: null });
75
- await app.close();
76
- });
77
- it('zod 校验失败 → 400(INVALID_INPUT);通过校验 → 200', async () => {
78
- const app = await startServer(createErrorContractServer(), { listen: false });
79
- const invalid = await app.inject({ method: 'GET', url: '/validated?count=0' });
80
- expect(invalid.statusCode).toBe(400);
81
- const invalidBody = invalid.json();
82
- expect(invalidBody.status).toBe('INVALID_INPUT');
83
- expect(invalidBody.data).toBeNull();
84
- expect(invalidBody.message).toContain('count');
85
- const valid = await app.inject({ method: 'GET', url: '/validated?count=3' });
86
- expect(valid.statusCode).toBe(200);
87
- expect(valid.json()).toEqual({ status: 'SUCCESS', message: 'ok', data: { count: 3 } });
88
- await app.close();
89
- });
90
- it('未匹配路由 → 404 + NOT_IMPLEMENTED', async () => {
91
- const app = await startServer(createErrorContractServer(), { listen: false });
92
- const response = await app.inject({ method: 'GET', url: '/missing' });
93
- expect(response.statusCode).toBe(404);
94
- const body = response.json();
95
- expect(body.status).toBe('NOT_IMPLEMENTED');
96
- expect(body.data).toBeNull();
97
- expect(body.message).toContain('GET /missing');
98
- await app.close();
99
- });
100
- it('资源包里的 `classifyError` 被用作数据层错误分类(→500)', async () => {
101
- const server = defineServer({
102
- app: { service: 'error-contract-database', title: 'Error Contract Database', version: '1.0.0' },
103
- resources: () => ({
104
- database: {
105
- classifyError: (error) => error instanceof Error && error.message === 'pg down'
106
- ? new SystemError('DATABASE_ERROR', { operation: 'query' }, 'pg down')
107
- : null,
108
- },
109
- }),
110
- config: () => ({}),
111
- services: () => ({}),
112
- middlewares: () => [],
113
- routers: (ctx) => {
114
- ctx.app.get('/database-down', () => {
115
- throw new Error('pg down');
116
- });
117
- },
118
- });
119
- const app = await startServer(server, { listen: false });
120
- const response = await app.inject({ method: 'GET', url: '/database-down' });
121
- expect(response.statusCode).toBe(500);
122
- expect(response.json()).toEqual({ status: 'DATABASE_ERROR', message: 'pg down', data: null });
123
- await app.close();
124
- });
125
- });
@@ -1,20 +0,0 @@
1
- import type { ErrorContext } from '@taicode/common-base';
2
- export declare class UserError extends Error {
3
- readonly type: string;
4
- readonly context?: ErrorContext | undefined;
5
- /** 类型守卫(`Symbol.for` 品牌,不用 `instanceof`) */
6
- static is(value: unknown): value is UserError;
7
- /** 固定 HTTP 状态(字面量类型) */
8
- readonly status: 400;
9
- constructor(type: string, context?: ErrorContext | undefined, message?: string);
10
- }
11
- export declare class SystemError extends Error {
12
- readonly type: string;
13
- readonly context?: ErrorContext | undefined;
14
- /** 类型守卫(`Symbol.for` 品牌,不用 `instanceof`) */
15
- static is(value: unknown): value is SystemError;
16
- /** 固定 HTTP 状态(字面量类型) */
17
- readonly status: 500;
18
- constructor(type: string, context?: ErrorContext | undefined, message?: string);
19
- }
20
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../source/app/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAsBxD,qBAAa,SAAU,SAAQ,KAAK;aASC,IAAI,EAAE,MAAM;aAAkB,OAAO,CAAC,EAAE,YAAY;IARvF,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS;IAI7C,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAG,GAAG,CAAS;gBAEK,IAAI,EAAE,MAAM,EAAkB,OAAO,CAAC,EAAE,YAAY,YAAA,EAAE,OAAO,SAAK;CAKtG;AAED,qBAAa,WAAY,SAAQ,KAAK;aASD,IAAI,EAAE,MAAM;aAAkB,OAAO,CAAC,EAAE,YAAY;IARvF,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW;IAI/C,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAG,GAAG,CAAS;gBAEK,IAAI,EAAE,MAAM,EAAkB,OAAO,CAAC,EAAE,YAAY,YAAA,EAAE,OAAO,SAAK;CAKtG"}
@@ -1,51 +0,0 @@
1
- /**
2
- * 错误契约(N2 §2.12)。
3
- *
4
- * 框架直接提供并导出 `UserError` / `SystemError`:
5
- * - `status` 是**字面量**(`400` / `500`)—— 映射表写成
6
- * `Record<UserError['status'] | SystemError['status'], number>` 时若改错直接编译失败;
7
- * - `is()` 是**类型守卫**,走 `Symbol.for` 品牌(**不用 `instanceof`**:跨包 / 编译后 / 原型链丢失
8
- * 都仍然可靠);品牌与 `@taicode/common-base` 同名,故基座抛出的同类错误同样被识别;
9
- * - 业务只抛这两个错误,状态码映射由框架固定(`UserError`→400 / `SystemError`→500)。
10
- *
11
- * 构造签名保持 `(code, context?, message?)`。
12
- */
13
- const USER_ERROR_BRAND = Symbol.for('@taicode/common/UserError');
14
- const SYSTEM_ERROR_BRAND = Symbol.for('@taicode/common/SystemError');
15
- function hasBrand(value, brand) {
16
- return typeof value === 'object' && value !== null && value[brand] === true;
17
- }
18
- export class UserError extends Error {
19
- type;
20
- context;
21
- /** 类型守卫(`Symbol.for` 品牌,不用 `instanceof`) */
22
- static is(value) {
23
- return hasBrand(value, USER_ERROR_BRAND);
24
- }
25
- /** 固定 HTTP 状态(字面量类型) */
26
- status = 400;
27
- constructor(type, context, message = '') {
28
- super(message, context);
29
- this.type = type;
30
- this.context = context;
31
- this.name = 'UserError';
32
- Object.defineProperty(this, USER_ERROR_BRAND, { value: true });
33
- }
34
- }
35
- export class SystemError extends Error {
36
- type;
37
- context;
38
- /** 类型守卫(`Symbol.for` 品牌,不用 `instanceof`) */
39
- static is(value) {
40
- return hasBrand(value, SYSTEM_ERROR_BRAND);
41
- }
42
- /** 固定 HTTP 状态(字面量类型) */
43
- status = 500;
44
- constructor(type, context, message = '') {
45
- super(message, context);
46
- this.type = type;
47
- this.context = context;
48
- this.name = 'SystemError';
49
- Object.defineProperty(this, SYSTEM_ERROR_BRAND, { value: true });
50
- }
51
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generate-openapi.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-openapi.test.d.ts","sourceRoot":"","sources":["../../source/app/generate-openapi.test.ts"],"names":[],"mappings":""}
@@ -1,123 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { z } from 'zod';
3
- import { defineServer, generateOpenApi, startServer } from './entries.js';
4
- import { createRedis } from './redis.js';
5
- /**
6
- * `generateOpenApi()` 红线。
7
- *
8
- * 全程**不得要求配置有效**:不跑 `config` 槽位、不要求 `DATABASE_URL` / `REDIS_URL` 有效、不建连;
9
- * `resources`(只构造不建连)/ `services` / 中间件注册 / 路由注册照常执行。
10
- * 跳过集:`config` / `initLifecycleUnits` / `listen`。
11
- */
12
- const routeSchema = {
13
- querystring: z.object({ name: z.string().optional() }),
14
- };
15
- describe('generateOpenApi 红线', () => {
16
- it('不跑 config、不要求 DATABASE_URL / REDIS_URL,能出 spec', async () => {
17
- delete process.env.DATABASE_URL;
18
- delete process.env.REDIS_URL;
19
- let configCalls = 0;
20
- const server = defineServer({
21
- app: { service: 'openapi-test', title: 'OpenAPI Test', version: '1.2.3' },
22
- resources: () => ({ database: { connected: false } }),
23
- config: () => {
24
- configCalls += 1;
25
- return {};
26
- },
27
- services: () => ({}),
28
- middlewares: () => [],
29
- routers: (ctx) => {
30
- ctx.app.get('/widgets', { schema: routeSchema }, () => []);
31
- },
32
- });
33
- const spec = (await generateOpenApi(server));
34
- expect(configCalls).toBe(0);
35
- expect(spec.openapi).toBe('3.0.3');
36
- expect(spec.info).toMatchObject({ title: 'OpenAPI Test', version: '1.2.3' });
37
- // 身份块只有 service / title / version:`info.description` 不再由框架填
38
- expect(spec.info?.description).toBeUndefined();
39
- expect(spec.servers).toEqual([{ url: '/' }]);
40
- expect(Object.keys(spec.paths ?? {})).toContain('/widgets');
41
- expect(Object.keys(spec.components?.securitySchemes ?? {})).toContain('bearerAuth');
42
- });
43
- it('跳过集:不跑 config / initLifecycleUnits(但跑 resources / services / 中间件注册 / routers)', async () => {
44
- const calls = [];
45
- const server = defineServer({
46
- app: { service: 'openapi-skip-set', title: 'OpenAPI Skip Set', version: '1.0.0' },
47
- resources: () => {
48
- calls.push('resources');
49
- return {};
50
- },
51
- config: () => {
52
- calls.push('config');
53
- return {};
54
- },
55
- services: () => {
56
- calls.push('services');
57
- return {
58
- a: {
59
- init: () => {
60
- calls.push('init:a');
61
- },
62
- },
63
- };
64
- },
65
- middlewares: () => [
66
- {
67
- name: 'probe',
68
- register: () => {
69
- calls.push('register:probe');
70
- },
71
- },
72
- ],
73
- routers: () => {
74
- calls.push('routers');
75
- },
76
- });
77
- await generateOpenApi(server);
78
- expect(calls).toEqual(['resources', 'services', 'register:probe', 'routers']);
79
- });
80
- it('REDIS_URL 存在时仍不建连(惰性包装)', async () => {
81
- process.env.REDIS_URL = 'redis://127.0.0.1:1/0';
82
- try {
83
- const server = defineServer({
84
- app: { service: 'openapi-redis-lazy', title: 'Redis Lazy', version: '1.0.0' },
85
- // resources 里照常构造 Redis 句柄(惰性包装:不建连、不 ping)
86
- resources: (ctx) => ({ redis: createRedis(ctx.env, ctx.log) }),
87
- config: () => ({}),
88
- services: () => ({}),
89
- middlewares: () => [],
90
- routers: () => undefined,
91
- });
92
- const spec = (await generateOpenApi(server));
93
- expect(spec.openapi).toBe('3.0.3');
94
- }
95
- finally {
96
- delete process.env.REDIS_URL;
97
- }
98
- });
99
- });
100
- describe('startServer 与 generateOpenApi 共用同一份定义', () => {
101
- it('startServer(server, { listen: false }) 返回原生实例、可 inject,且会跑 config', async () => {
102
- let configCalls = 0;
103
- const server = defineServer({
104
- app: { service: 'openapi-shared-definition', title: 'Shared Definition', version: '1.0.0' },
105
- resources: () => ({}),
106
- config: () => {
107
- configCalls += 1;
108
- return {};
109
- },
110
- services: () => ({}),
111
- middlewares: () => [],
112
- routers: (ctx) => {
113
- ctx.app.get('/ping', () => ({ pong: true }));
114
- },
115
- });
116
- const app = await startServer(server, { listen: false });
117
- expect(typeof app.inject).toBe('function');
118
- const response = await app.inject({ method: 'GET', url: '/ping' });
119
- expect(response.statusCode).toBe(200);
120
- expect(configCalls).toBe(1);
121
- await app.close();
122
- });
123
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=lifecycle-logging.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"lifecycle-logging.test.d.ts","sourceRoot":"","sources":["../../source/app/lifecycle-logging.test.ts"],"names":[],"mappings":""}