@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,77 +0,0 @@
1
- /** 路由注册表在 `app` 上的存放键(Symbol,不进公共契约) */
2
- const REGISTERED_OPERATIONS = Symbol.for('@taicode/common-server/registered-operations');
3
- /**
4
- * 在 root 挂 `onRoute` 钩子,收集框架自身可见的**路由注册表**(设计 §5.2)。
5
- *
6
- * 必须在**路由注册之前**(core 装配期)调用:钩子挂在 root,随后 `routers` 阶段注册的路由逐条被采集。
7
- * 该表与 swagger 解耦:swagger 只负责出文档,`auth` 的权限自注册只读这张表。
8
- */
9
- export function installRegisteredOperations(app) {
10
- const collected = [];
11
- Object.defineProperty(app, REGISTERED_OPERATIONS, {
12
- value: collected,
13
- enumerable: false,
14
- configurable: true,
15
- });
16
- app.addHook('onRoute', (routeOptions) => {
17
- const routeConfig = (routeOptions.config ?? {});
18
- // 与 `@fastify/swagger` 同口径:Fastify 默认为 GET 自动生成 HEAD 路由;未显式
19
- // `config.swagger.exposeHeadRoute = true` 时跳过,避免与 GET 重复(权限目录保持同源)。
20
- if (routeOptions.method === 'HEAD' && routeConfig.swagger?.exposeHeadRoute !== true) {
21
- return;
22
- }
23
- const schema = (routeOptions.schema ?? {});
24
- const methods = Array.isArray(routeOptions.method)
25
- ? routeOptions.method
26
- : [routeOptions.method];
27
- for (const method of methods) {
28
- collected.push({
29
- method,
30
- url: routeOptions.url,
31
- operationId: schema.operationId,
32
- summary: schema.summary,
33
- description: schema.description,
34
- tags: schema.tags,
35
- });
36
- }
37
- });
38
- }
39
- /** 读取 root `onRoute` 采集的路由注册表;未安装(未走框架装配)时返回空数组。 */
40
- export function readRegisteredOperations(app) {
41
- return (app[REGISTERED_OPERATIONS] ?? []);
42
- }
43
- /** 从路由注册表筛出声明了 `operationId` 的项,归一为权限操作目录元素 */
44
- export function toPermissionOperations(operations) {
45
- const result = [];
46
- for (const entry of operations) {
47
- if (!entry.operationId) {
48
- continue;
49
- }
50
- result.push({
51
- operation: entry.operationId,
52
- summary: entry.summary ?? '',
53
- description: entry.description ?? '',
54
- tags: entry.tags ?? [],
55
- });
56
- }
57
- return result;
58
- }
59
- export function extractPermissionOperations(openApiSpec) {
60
- // 出 spec 路径拿到的文档是未知形态;只按结构取字段,缺什么就跳过什么
61
- const spec = (openApiSpec ?? {});
62
- const operations = [];
63
- for (const pathItem of Object.values(spec.paths ?? {})) {
64
- for (const operation of Object.values(pathItem ?? {})) {
65
- if (!operation?.operationId) {
66
- continue;
67
- }
68
- operations.push({
69
- operation: operation.operationId,
70
- summary: operation.summary ?? '',
71
- description: operation.description ?? '',
72
- tags: operation.tags ?? [],
73
- });
74
- }
75
- }
76
- return operations;
77
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=permission-operations.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"permission-operations.test.d.ts","sourceRoot":"","sources":["../../source/app/permission-operations.test.ts"],"names":[],"mappings":""}
@@ -1,72 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { extractPermissionOperations } from './permission-operations.js';
3
- /**
4
- * `extractPermissionOperations`(设计 §5.2,框架内置)契约。
5
- *
6
- * 口径与各仓原自写实现一致:逐 path → 逐 method,缺 `operationId` 跳过;缺 `paths` / 空文档 → `[]`;
7
- * `summary` / `description` / `tags` 缺省归一为 `''` / `''` / `[]`。
8
- */
9
- describe('extractPermissionOperations(设计 §5.2)', () => {
10
- it('提取 operationId 与可选元信息(summary / description / tags)', () => {
11
- expect(extractPermissionOperations({
12
- paths: {
13
- '/release/list': {
14
- post: {
15
- operationId: 'listRelease',
16
- summary: 'List releases',
17
- description: 'Returns available releases.',
18
- tags: ['Release'],
19
- },
20
- },
21
- },
22
- })).toEqual([
23
- {
24
- operation: 'listRelease',
25
- summary: 'List releases',
26
- description: 'Returns available releases.',
27
- tags: ['Release'],
28
- },
29
- ]);
30
- });
31
- it('可选元信息缺省归一,且跳过无 operationId 的条目', () => {
32
- expect(extractPermissionOperations({
33
- paths: {
34
- '/release/create': {
35
- post: { operationId: 'createRelease' },
36
- parameters: { description: 'Shared parameters' },
37
- },
38
- },
39
- })).toEqual([
40
- {
41
- operation: 'createRelease',
42
- summary: '',
43
- description: '',
44
- tags: [],
45
- },
46
- ]);
47
- });
48
- it('缺 paths 时返回空数组', () => {
49
- expect(extractPermissionOperations({})).toEqual([]);
50
- });
51
- it('空文档 / 非对象入参返回空数组', () => {
52
- expect(extractPermissionOperations({ paths: {} })).toEqual([]);
53
- expect(extractPermissionOperations(undefined)).toEqual([]);
54
- });
55
- it('多路径 + 多方法:按 paths → methods 的遍历序全量提取', () => {
56
- expect(extractPermissionOperations({
57
- paths: {
58
- '/widget/list': {
59
- get: { operationId: 'listWidget', tags: ['Widget'] },
60
- post: { operationId: 'queryWidget' },
61
- },
62
- '/widget/create': {
63
- post: { operationId: 'createWidget', summary: 'Create widget' },
64
- },
65
- },
66
- })).toEqual([
67
- { operation: 'listWidget', summary: '', description: '', tags: ['Widget'] },
68
- { operation: 'queryWidget', summary: '', description: '', tags: [] },
69
- { operation: 'createWidget', summary: 'Create widget', description: '', tags: [] },
70
- ]);
71
- });
72
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=permission-registration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"permission-registration.test.d.ts","sourceRoot":"","sources":["../../source/app/permission-registration.test.ts"],"names":[],"mappings":""}
@@ -1,138 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import { defineServer, generateOpenApi, startServer } from './entries.js';
3
- import { defaultMiddlewares } from './middleware/index.js';
4
- import { createAuthMiddleware } from './middleware/auth.js';
5
- /** 注入假 fetch:记录请求,并按 `responder` 返回最小 Response 形状 */
6
- function stubFetch(responder) {
7
- const requests = [];
8
- const fetchStub = vi.fn(async (input, init) => {
9
- const url = typeof input === 'string' ? input : input.toString();
10
- requests.push({
11
- url,
12
- method: init?.method,
13
- body: init?.body ? JSON.parse(String(init.body)) : undefined,
14
- });
15
- const { status, payload } = responder(url);
16
- return {
17
- ok: status >= 200 && status < 300,
18
- status,
19
- json: async () => payload,
20
- };
21
- });
22
- vi.stubGlobal('fetch', fetchStub);
23
- return { requests, fetchStub };
24
- }
25
- const listWidgetSchema = {
26
- operationId: 'listWidget',
27
- summary: 'List widgets',
28
- tags: ['Widget'],
29
- };
30
- const createWidgetSchema = {
31
- operationId: 'createWidget',
32
- };
33
- function createServer(configuration, authOptions) {
34
- return defineServer({
35
- app: { service: 'widgets', title: 'Widgets Service', version: '2.3.4' },
36
- resources: () => ({}),
37
- config: () => configuration,
38
- // 默认预设去掉 auth,换上带选项的 auth(设计 §5.2 预设只是内容)
39
- middlewares: (ctx) => [...defaultMiddlewares(ctx).slice(0, -1), createAuthMiddleware(ctx, authOptions)],
40
- services: () => ({}),
41
- routers: (ctx) => {
42
- // 公开路由(不走 verify);本用例只关心 onReady 的自注册
43
- ctx.app.get('/widget/list', { schema: listWidgetSchema, config: { accessControls: ['NoLoginRequired'] } }, () => ({ items: [] }));
44
- ctx.app.post('/widget/create', { schema: createWidgetSchema, config: { accessControls: ['NoLoginRequired'] } }, () => ({ id: 'w-1' }));
45
- },
46
- });
47
- }
48
- afterEach(() => {
49
- vi.unstubAllGlobals();
50
- });
51
- describe('权限操作自注册(设计 §5.2 / §10-11)', () => {
52
- it('onReady 阶段向 /permission/operation/register POST 正确请求体(service / version / operations)', async () => {
53
- const { requests, fetchStub } = stubFetch(() => ({ status: 200, payload: { status: 'SUCCESS', data: {} } }));
54
- const app = await startServer(createServer({ account: { enabled: true, serviceUrl: 'http://account.local' } }), { listen: false });
55
- try {
56
- expect(fetchStub).toHaveBeenCalledTimes(1);
57
- expect(requests).toHaveLength(1);
58
- expect(requests[0].url).toBe('http://account.local/permission/operation/register');
59
- expect(requests[0].method).toBe('POST');
60
- expect(requests[0].body).toEqual({
61
- service: 'widgets',
62
- version: '2.3.4',
63
- operations: [
64
- { operation: 'listWidget', summary: 'List widgets', description: '', tags: ['Widget'] },
65
- { operation: 'createWidget', summary: '', description: '', tags: [] },
66
- ],
67
- });
68
- }
69
- finally {
70
- await app.close();
71
- }
72
- });
73
- it('account 无 serviceUrl 时不注册(自然排除 account 自身)', async () => {
74
- const { fetchStub } = stubFetch(() => ({ status: 200, payload: { status: 'SUCCESS' } }));
75
- const app = await startServer(createServer({ account: { enabled: false } }), { listen: false });
76
- try {
77
- expect(fetchStub).not.toHaveBeenCalled();
78
- }
79
- finally {
80
- await app.close();
81
- }
82
- });
83
- it('registerPermissions: false 时不注册', async () => {
84
- const { fetchStub } = stubFetch(() => ({ status: 200, payload: { status: 'SUCCESS' } }));
85
- const app = await startServer(createServer({ account: { serviceUrl: 'http://account.local' } }, { registerPermissions: false }), { listen: false });
86
- try {
87
- expect(fetchStub).not.toHaveBeenCalled();
88
- }
89
- finally {
90
- await app.close();
91
- }
92
- });
93
- it('注册失败(HTTP 非 2xx)不致命:startServer 仍 resolve、服务可用', async () => {
94
- const { fetchStub } = stubFetch(() => ({ status: 500, payload: { status: 'UNKNOWN_ERROR', message: 'boom' } }));
95
- const app = await startServer(createServer({ account: { serviceUrl: 'http://account.local' } }), { listen: false });
96
- try {
97
- expect(fetchStub).toHaveBeenCalledTimes(1);
98
- const response = await app.inject({ method: 'GET', url: '/widget/list' });
99
- expect(response.statusCode).toBe(200);
100
- }
101
- finally {
102
- await app.close();
103
- }
104
- });
105
- it('HTTP ok 但信封非 SUCCESS 也不致命(成功判定与 /gateway/verify 同口径)', async () => {
106
- const { fetchStub } = stubFetch(() => ({ status: 200, payload: { status: 'INVALID_INPUT', message: 'bad' } }));
107
- const app = await startServer(createServer({ account: { serviceUrl: 'http://account.local' } }), { listen: false });
108
- try {
109
- expect(fetchStub).toHaveBeenCalledTimes(1);
110
- const response = await app.inject({ method: 'GET', url: '/widget/list' });
111
- expect(response.statusCode).toBe(200);
112
- }
113
- finally {
114
- await app.close();
115
- }
116
- });
117
- it('fetch 抛错(网络不可达)也不致命', async () => {
118
- const fetchStub = vi.fn(async () => {
119
- throw new Error('network unreachable');
120
- });
121
- vi.stubGlobal('fetch', fetchStub);
122
- const app = await startServer(createServer({ account: { serviceUrl: 'http://account.local' } }), { listen: false });
123
- try {
124
- expect(fetchStub).toHaveBeenCalledTimes(1);
125
- const response = await app.inject({ method: 'GET', url: '/widget/list' });
126
- expect(response.statusCode).toBe(200);
127
- }
128
- finally {
129
- await app.close();
130
- }
131
- });
132
- it('出 spec 路径(generateOpenApi 不调 loadConfig)不注册', async () => {
133
- const { fetchStub } = stubFetch(() => ({ status: 200, payload: { status: 'SUCCESS' } }));
134
- const spec = (await generateOpenApi(createServer({ account: { serviceUrl: 'http://account.local' } })));
135
- expect(fetchStub).not.toHaveBeenCalled();
136
- expect(Object.keys(spec.paths ?? {})).toEqual(expect.arrayContaining(['/widget/list', '/widget/create']));
137
- });
138
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../source/app/redis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,UAAU,EAAE,MAAM,yBAAyB,CAAA;AAEvE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEzD;;;;;;;;;;;;;;GAcG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,+BAA+B,MAAM,CAAA;AAElD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,SAAS,CAmBrF;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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=registered-operations.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registered-operations.test.d.ts","sourceRoot":"","sources":["../../source/app/registered-operations.test.ts"],"names":[],"mappings":""}
@@ -1,57 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { defineServer, generateOpenApi, startServer } from './entries.js';
3
- import { createContextMiddleware } from './middleware/context.js';
4
- import { readRegisteredOperations, toPermissionOperations } from './permission-operations.js';
5
- /**
6
- * 路由注册表 + swagger 时序。
7
- *
8
- * 关键点:自定义 `middlewares`(**不含 cors**)时,swagger 插件体也必须在任何路由注册之前执行完
9
- * (`registerOpenApi` 内 `await app.after()`),路由注册表同样非空——两者都不依赖「恰好有 cors
10
- * 顺带冲刷队列」这种隐式巧合。
11
- */
12
- const listWidgetSchema = {
13
- operationId: 'listWidget',
14
- summary: 'List widgets',
15
- tags: ['Widget'],
16
- };
17
- /** 未声明 `operationId` 的路由(回落 url,且不进权限目录) */
18
- const plainSchema = {};
19
- function createServer() {
20
- return defineServer({
21
- app: { service: 'registry', title: 'Registry', version: '1.0.0' },
22
- resources: () => ({}),
23
- config: () => ({}),
24
- services: () => ({}),
25
- // 自定义中间件:只有 context,**没有 cors**(不得靠 cors 冲刷 swagger 队列)
26
- middlewares: () => [createContextMiddleware()],
27
- routers: (ctx) => {
28
- ctx.app.get('/widget/list', { schema: listWidgetSchema }, () => ({ items: [] }));
29
- ctx.app.post('/widget/plain', { schema: plainSchema }, () => ({ ok: true }));
30
- },
31
- });
32
- }
33
- describe('路由注册表', () => {
34
- it('自定义 middlewares(无 cors)时,注册表非空且能拿到 operationId', async () => {
35
- const app = await startServer(createServer(), { listen: false });
36
- try {
37
- const operations = readRegisteredOperations(app);
38
- expect(operations.length).toBeGreaterThan(0);
39
- const listWidget = operations.find((entry) => entry.url === '/widget/list');
40
- expect(listWidget).toMatchObject({ method: 'GET', url: '/widget/list', operationId: 'listWidget' });
41
- // Fastify 为 GET 自动生成的 HEAD 路由不进注册表(与 swagger 同口径)
42
- expect(operations.some((entry) => entry.method === 'HEAD')).toBe(false);
43
- // 未声明 operationId 的路由不进权限目录
44
- expect(toPermissionOperations(operations)).toEqual([
45
- { operation: 'listWidget', summary: 'List widgets', description: '', tags: ['Widget'] },
46
- ]);
47
- }
48
- finally {
49
- await app.close();
50
- }
51
- });
52
- it('自定义 middlewares(无 cors)时,swagger 文档仍包含已注册路由(确定性冲刷)', async () => {
53
- const spec = (await generateOpenApi(createServer()));
54
- const paths = spec.paths ?? {};
55
- expect(Object.keys(paths)).toEqual(expect.arrayContaining(['/widget/list', '/widget/plain']));
56
- });
57
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=request-id.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"request-id.test.d.ts","sourceRoot":"","sources":["../../source/app/request-id.test.ts"],"names":[],"mappings":""}
@@ -1,130 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { createRequestIdOptions, normalizeInboundRequestId, REQUEST_ID_HEADER, REQUEST_ID_LOG_LABEL, } from './core/request-id.js';
3
- import { defineServer, startServer } from './entries.js';
4
- /**
5
- * `requestId` 契约(设计 §5.1 / §8)——**框架内置、锁定**。
6
- *
7
- * 入站 `x-request-id` 存在且非空 → 复用,否则 `randomUUID`;响应 header 统一回吐
8
- * `x-request-id`;**不进响应体**;日志字段键名统一为全拼 `requestId`。
9
- */
10
- const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11
- function createRequestIdServer() {
12
- return defineServer({
13
- app: { service: 'request-id-test', title: 'Request Id Test', version: '1.0.0' },
14
- resources: () => ({}),
15
- config: () => ({}),
16
- services: () => ({}),
17
- middlewares: () => [],
18
- routers: (ctx) => {
19
- ctx.app.get('/echo', () => ({ ok: true }));
20
- },
21
- });
22
- }
23
- /** 启动一个可读取请求日志 bindings 的 app(`x-request-id` 归一化断言用) */
24
- async function startBindingApp() {
25
- let bindings;
26
- const server = defineServer({
27
- app: { service: 'request-id-normalize', title: 'Request Id Normalize', version: '1.0.0' },
28
- resources: () => ({}),
29
- config: () => ({}),
30
- services: () => ({}),
31
- middlewares: () => [],
32
- routers: (ctx) => {
33
- ctx.app.addHook('onRequest', (request, _reply, done) => {
34
- bindings = request.log.bindings();
35
- done();
36
- });
37
- ctx.app.get('/echo', () => ({ ok: true }));
38
- },
39
- });
40
- const app = await startServer(server, { listen: false });
41
- return { app, getBindings: () => bindings };
42
- }
43
- describe('requestId(设计 §5.1 / §8)', () => {
44
- it('固定 header 名与日志字段名', () => {
45
- expect(REQUEST_ID_HEADER).toBe('x-request-id');
46
- expect(REQUEST_ID_LOG_LABEL).toBe('requestId');
47
- const options = createRequestIdOptions();
48
- expect(options.requestIdHeader).toBe('x-request-id');
49
- expect(options.requestIdLogLabel).toBe('requestId');
50
- });
51
- it('genReqId:入站值非空则复用,否则生成 UUID', () => {
52
- const { genReqId } = createRequestIdOptions();
53
- expect(genReqId({ headers: { 'x-request-id': 'abc' } })).toBe('abc');
54
- expect(genReqId({ headers: { 'x-request-id': ['abc', 'def'] } })).toBe('abc');
55
- expect(genReqId({ headers: {} })).toMatch(UUID_PATTERN);
56
- expect(genReqId({ headers: { 'x-request-id': ' ' } })).toMatch(UUID_PATTERN);
57
- });
58
- it('normalizeInboundRequestId:trim 后为空视为缺失(undefined),非空返回 trim 值', () => {
59
- expect(normalizeInboundRequestId(undefined)).toBeUndefined();
60
- expect(normalizeInboundRequestId('')).toBeUndefined();
61
- expect(normalizeInboundRequestId(' ')).toBeUndefined();
62
- expect(normalizeInboundRequestId('trace-abc')).toBe('trace-abc');
63
- expect(normalizeInboundRequestId(' trace-abc ')).toBe('trace-abc');
64
- expect(normalizeInboundRequestId(['abc', 'def'])).toBe('abc');
65
- });
66
- it('空白入站 header → 视为缺失,重新生成 UUID(request.id / 日志字段 / 响应 header 一致)', async () => {
67
- const { app, getBindings } = await startBindingApp();
68
- const response = await app.inject({ method: 'GET', url: '/echo', headers: { [REQUEST_ID_HEADER]: ' ' } });
69
- expect(response.statusCode).toBe(200);
70
- const requestId = String(response.headers[REQUEST_ID_HEADER]);
71
- expect(requestId).toMatch(UUID_PATTERN);
72
- expect(getBindings()).toEqual({ [REQUEST_ID_LOG_LABEL]: requestId });
73
- await app.close();
74
- });
75
- it('带空格的入站 header → trim 后复用(request.id / 日志字段 / 响应 header 一致)', async () => {
76
- const { app, getBindings } = await startBindingApp();
77
- const response = await app.inject({
78
- method: 'GET',
79
- url: '/echo',
80
- headers: { [REQUEST_ID_HEADER]: ' trace-trim ' },
81
- });
82
- expect(response.statusCode).toBe(200);
83
- expect(response.headers[REQUEST_ID_HEADER]).toBe('trace-trim');
84
- expect(getBindings()).toEqual({ [REQUEST_ID_LOG_LABEL]: 'trace-trim' });
85
- await app.close();
86
- });
87
- it('缺失入站 header → 生成 UUID(request.id / 日志字段 / 响应 header 一致)', async () => {
88
- const { app, getBindings } = await startBindingApp();
89
- const response = await app.inject({ method: 'GET', url: '/echo' });
90
- expect(response.statusCode).toBe(200);
91
- const requestId = String(response.headers[REQUEST_ID_HEADER]);
92
- expect(requestId).toMatch(UUID_PATTERN);
93
- expect(getBindings()).toEqual({ [REQUEST_ID_LOG_LABEL]: requestId });
94
- await app.close();
95
- });
96
- it('复用入站 header 并回吐到响应 header;日志绑定字段为 requestId;不进响应体', async () => {
97
- let bindings;
98
- const server = defineServer({
99
- app: { service: 'request-id-integration', title: 'Request Id Integration', version: '1.0.0' },
100
- resources: () => ({}),
101
- config: () => ({}),
102
- services: () => ({}),
103
- middlewares: () => [],
104
- routers: (ctx) => {
105
- ctx.app.addHook('onRequest', (request, _reply, done) => {
106
- bindings = request.log.bindings();
107
- done();
108
- });
109
- ctx.app.get('/echo', () => ({ ok: true }));
110
- },
111
- });
112
- const app = await startServer(server, { listen: false });
113
- const reused = await app.inject({ method: 'GET', url: '/echo', headers: { 'x-request-id': 'trace-abc' } });
114
- expect(reused.statusCode).toBe(200);
115
- expect(reused.headers[REQUEST_ID_HEADER]).toBe('trace-abc');
116
- expect(bindings).toEqual({ requestId: 'trace-abc' });
117
- expect(reused.body).not.toContain('trace-abc');
118
- const generated = await app.inject({ method: 'GET', url: '/echo' });
119
- expect(String(generated.headers[REQUEST_ID_HEADER])).toMatch(UUID_PATTERN);
120
- expect(bindings).toHaveProperty(REQUEST_ID_LOG_LABEL);
121
- await app.close();
122
- });
123
- it('startServer 冒烟:定义可跑、路由可用', async () => {
124
- const app = await startServer(createRequestIdServer(), { listen: false });
125
- const response = await app.inject({ method: 'GET', url: '/echo' });
126
- expect(response.statusCode).toBe(200);
127
- expect(response.json()).toEqual({ status: 'SUCCESS', message: 'ok', data: { ok: true } });
128
- await app.close();
129
- });
130
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=resources.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resources.test.d.ts","sourceRoot":"","sources":["../../source/app/resources.test.ts"],"names":[],"mappings":""}