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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/output/app/index.d.ts +13 -15
  2. package/output/app/index.d.ts.map +1 -1
  3. package/output/app/index.js +256 -18
  4. package/output/auth/index.d.ts +72 -0
  5. package/output/auth/index.d.ts.map +1 -0
  6. package/output/auth/index.js +176 -0
  7. package/output/core/assemble.d.ts +64 -0
  8. package/output/core/assemble.d.ts.map +1 -0
  9. package/output/core/assemble.js +88 -0
  10. package/output/core/boot/context.d.ts +89 -0
  11. package/output/core/boot/context.d.ts.map +1 -0
  12. package/output/core/boot/context.js +106 -0
  13. package/output/core/boot/lifecycle.d.ts +94 -0
  14. package/output/core/boot/lifecycle.d.ts.map +1 -0
  15. package/output/{app → core/boot}/lifecycle.js +45 -14
  16. package/output/core/boot/shutdown.d.ts +30 -0
  17. package/output/core/boot/shutdown.d.ts.map +1 -0
  18. package/output/core/boot/shutdown.js +45 -0
  19. package/output/core/boot/signal.d.ts +13 -0
  20. package/output/core/boot/signal.d.ts.map +1 -0
  21. package/output/{app → core/boot}/signal.js +3 -2
  22. package/output/core/boot/steps.d.ts +107 -0
  23. package/output/core/boot/steps.d.ts.map +1 -0
  24. package/output/core/boot/steps.js +200 -0
  25. package/output/core/entries.d.ts +123 -0
  26. package/output/core/entries.d.ts.map +1 -0
  27. package/output/core/entries.js +193 -0
  28. package/output/core/entries.test.d.ts +2 -0
  29. package/output/core/entries.test.d.ts.map +1 -0
  30. package/output/core/entries.test.js +217 -0
  31. package/output/core/env.d.ts +62 -0
  32. package/output/core/env.d.ts.map +1 -0
  33. package/output/core/env.js +96 -0
  34. package/output/{app → core}/env.test.d.ts.map +1 -1
  35. package/output/core/env.test.js +105 -0
  36. package/output/core/modules/auth-provider/index.d.ts +10 -0
  37. package/output/core/modules/auth-provider/index.d.ts.map +1 -0
  38. package/output/core/modules/auth-provider/index.js +9 -0
  39. package/output/core/modules/auth-provider/types.d.ts +71 -0
  40. package/output/core/modules/auth-provider/types.d.ts.map +1 -0
  41. package/output/core/modules/context/index.d.ts +18 -0
  42. package/output/core/modules/context/index.d.ts.map +1 -0
  43. package/output/core/modules/context/index.js +23 -0
  44. package/output/core/modules/context/index.test.d.ts.map +1 -0
  45. package/output/core/modules/cookie/index.d.ts +21 -0
  46. package/output/core/modules/cookie/index.d.ts.map +1 -0
  47. package/output/{app/core/cookie.js → core/modules/cookie/index.js} +1 -6
  48. package/output/core/modules/identity/index.d.ts +4 -0
  49. package/output/core/modules/identity/index.d.ts.map +1 -0
  50. package/output/{app/core/identity.js → core/modules/identity/index.js} +1 -1
  51. package/output/core/modules/logging/index.d.ts +5 -0
  52. package/output/core/modules/logging/index.d.ts.map +1 -0
  53. package/output/{app/core/logging.js → core/modules/logging/index.js} +2 -2
  54. package/output/core/modules/openapi/index.d.ts +7 -0
  55. package/output/core/modules/openapi/index.d.ts.map +1 -0
  56. package/output/{app/core/openapi.js → core/modules/openapi/index.js} +11 -12
  57. package/output/core/modules/permission-operations/index.d.ts +26 -0
  58. package/output/core/modules/permission-operations/index.d.ts.map +1 -0
  59. package/output/core/modules/permission-operations/index.js +36 -0
  60. package/output/core/modules/permission-operations/index.test.d.ts +2 -0
  61. package/output/core/modules/permission-operations/index.test.d.ts.map +1 -0
  62. package/output/core/modules/permission-operations/index.test.js +45 -0
  63. package/output/{app/core/request-id.d.ts → core/modules/request-id/index.d.ts} +1 -4
  64. package/output/core/modules/request-id/index.d.ts.map +1 -0
  65. package/output/{app/core/request-id.js → core/modules/request-id/index.js} +2 -2
  66. package/output/core/modules/response/index.d.ts +22 -0
  67. package/output/core/modules/response/index.d.ts.map +1 -0
  68. package/output/{app/core/response.js → core/modules/response/index.js} +22 -11
  69. package/output/{schema/index.d.ts → core/modules/schema/builders.d.ts} +8 -1
  70. package/output/core/modules/schema/builders.d.ts.map +1 -0
  71. package/output/{schema/index.js → core/modules/schema/builders.js} +7 -4
  72. package/output/core/modules/schema/index.d.ts +34 -0
  73. package/output/core/modules/schema/index.d.ts.map +1 -0
  74. package/output/core/modules/schema/index.js +62 -0
  75. package/output/core/modules/schema/index.test.d.ts +2 -0
  76. package/output/core/modules/schema/index.test.d.ts.map +1 -0
  77. package/output/core/modules/schema/index.test.js +55 -0
  78. package/output/core/modules/system-routes/index.d.ts +15 -0
  79. package/output/core/modules/system-routes/index.d.ts.map +1 -0
  80. package/output/core/modules/system-routes/index.js +171 -0
  81. package/output/core/test-support/database.d.ts +96 -0
  82. package/output/core/test-support/database.d.ts.map +1 -0
  83. package/output/core/test-support/database.js +144 -0
  84. package/output/core/test-support/database.test.d.ts +2 -0
  85. package/output/core/test-support/database.test.d.ts.map +1 -0
  86. package/output/core/test-support/database.test.js +110 -0
  87. package/output/core/test-support/environment.d.ts +33 -0
  88. package/output/core/test-support/environment.d.ts.map +1 -0
  89. package/output/core/test-support/environment.js +42 -0
  90. package/output/core/test-support/test-server.d.ts +94 -0
  91. package/output/core/test-support/test-server.d.ts.map +1 -0
  92. package/output/core/test-support/test-server.js +60 -0
  93. package/output/core/test-support/test-server.test.d.ts +2 -0
  94. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  95. package/output/core/test-support/test-server.test.js +162 -0
  96. package/output/{app → core}/type-augment.d.ts +2 -2
  97. package/output/core/type-augment.d.ts.map +1 -0
  98. package/output/core/types.d.ts +338 -0
  99. package/output/core/types.d.ts.map +1 -0
  100. package/output/cors/index.d.ts +3 -0
  101. package/output/cors/index.d.ts.map +1 -0
  102. package/output/cors/index.js +27 -0
  103. package/output/index.d.ts +8 -4
  104. package/output/index.d.ts.map +1 -1
  105. package/output/index.js +24 -5
  106. package/output/rate-limit/index.d.ts +23 -0
  107. package/output/rate-limit/index.d.ts.map +1 -0
  108. package/output/rate-limit/index.js +42 -0
  109. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  110. package/output/rate-limit/limiter.d.ts.map +1 -0
  111. package/output/rate-limit/memory-store.d.ts.map +1 -0
  112. package/output/rate-limit/redis-store.d.ts.map +1 -0
  113. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  114. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  115. package/output/{rate-limiter → rate-limit}/types.d.ts +0 -1
  116. package/output/rate-limit/types.d.ts.map +1 -0
  117. package/output/rate-limit/types.js +1 -0
  118. package/output/{redis-cache/redis-cache.d.ts → redis/cache.d.ts} +1 -1
  119. package/output/redis/cache.d.ts.map +1 -0
  120. package/output/redis/cache.test.d.ts +2 -0
  121. package/output/redis/cache.test.d.ts.map +1 -0
  122. package/output/{redis-cache/redis-cache.test.js → redis/cache.test.js} +1 -1
  123. package/output/{app/redis.d.ts → redis/index.d.ts} +12 -7
  124. package/output/redis/index.d.ts.map +1 -0
  125. package/output/{app/redis.js → redis/index.js} +15 -5
  126. package/output/redis/index.test.d.ts +2 -0
  127. package/output/{context → redis}/index.test.d.ts.map +1 -1
  128. package/output/redis/index.test.js +55 -0
  129. package/output/redis-queue/index.js +1 -1
  130. package/output/redis-queue/test-helpers.d.ts +1 -1
  131. package/output/redis-queue/test-helpers.js +1 -1
  132. package/output/redis-queue/types.d.ts +3 -12
  133. package/output/redis-queue/types.d.ts.map +1 -1
  134. package/package.json +16 -15
  135. package/LICENSE +0 -21
  136. package/output/app/auth.test.d.ts +0 -2
  137. package/output/app/auth.test.d.ts.map +0 -1
  138. package/output/app/auth.test.js +0 -148
  139. package/output/app/boot.d.ts +0 -39
  140. package/output/app/boot.d.ts.map +0 -1
  141. package/output/app/boot.js +0 -140
  142. package/output/app/config.test.d.ts +0 -2
  143. package/output/app/config.test.d.ts.map +0 -1
  144. package/output/app/config.test.js +0 -132
  145. package/output/app/context.d.ts +0 -70
  146. package/output/app/context.d.ts.map +0 -1
  147. package/output/app/context.js +0 -73
  148. package/output/app/core/cookie.d.ts +0 -25
  149. package/output/app/core/cookie.d.ts.map +0 -1
  150. package/output/app/core/identity.d.ts +0 -12
  151. package/output/app/core/identity.d.ts.map +0 -1
  152. package/output/app/core/index.d.ts +0 -44
  153. package/output/app/core/index.d.ts.map +0 -1
  154. package/output/app/core/index.js +0 -57
  155. package/output/app/core/logging.d.ts +0 -13
  156. package/output/app/core/logging.d.ts.map +0 -1
  157. package/output/app/core/openapi.d.ts +0 -17
  158. package/output/app/core/openapi.d.ts.map +0 -1
  159. package/output/app/core/request-id.d.ts.map +0 -1
  160. package/output/app/core/response.d.ts +0 -41
  161. package/output/app/core/response.d.ts.map +0 -1
  162. package/output/app/define-server.negative-cases.d.ts +0 -40
  163. package/output/app/define-server.negative-cases.d.ts.map +0 -1
  164. package/output/app/define-server.negative-cases.js +0 -268
  165. package/output/app/define-server.test.d.ts +0 -2
  166. package/output/app/define-server.test.d.ts.map +0 -1
  167. package/output/app/define-server.test.js +0 -97
  168. package/output/app/entries.d.ts +0 -29
  169. package/output/app/entries.d.ts.map +0 -1
  170. package/output/app/entries.js +0 -64
  171. package/output/app/env.d.ts +0 -59
  172. package/output/app/env.d.ts.map +0 -1
  173. package/output/app/env.js +0 -66
  174. package/output/app/env.test.js +0 -124
  175. package/output/app/error-contract.test.d.ts +0 -2
  176. package/output/app/error-contract.test.d.ts.map +0 -1
  177. package/output/app/error-contract.test.js +0 -227
  178. package/output/app/errors.d.ts +0 -20
  179. package/output/app/errors.d.ts.map +0 -1
  180. package/output/app/errors.js +0 -51
  181. package/output/app/generate-openapi.test.d.ts +0 -2
  182. package/output/app/generate-openapi.test.d.ts.map +0 -1
  183. package/output/app/generate-openapi.test.js +0 -123
  184. package/output/app/lifecycle-logging.test.d.ts +0 -2
  185. package/output/app/lifecycle-logging.test.d.ts.map +0 -1
  186. package/output/app/lifecycle-logging.test.js +0 -155
  187. package/output/app/lifecycle.d.ts +0 -70
  188. package/output/app/lifecycle.d.ts.map +0 -1
  189. package/output/app/lifecycle.test.d.ts +0 -2
  190. package/output/app/lifecycle.test.d.ts.map +0 -1
  191. package/output/app/lifecycle.test.js +0 -152
  192. package/output/app/middleware/auth.d.ts +0 -71
  193. package/output/app/middleware/auth.d.ts.map +0 -1
  194. package/output/app/middleware/auth.js +0 -190
  195. package/output/app/middleware/context.d.ts +0 -13
  196. package/output/app/middleware/context.d.ts.map +0 -1
  197. package/output/app/middleware/context.js +0 -15
  198. package/output/app/middleware/cors.d.ts +0 -14
  199. package/output/app/middleware/cors.d.ts.map +0 -1
  200. package/output/app/middleware/cors.js +0 -30
  201. package/output/app/middleware/index.d.ts +0 -17
  202. package/output/app/middleware/index.d.ts.map +0 -1
  203. package/output/app/middleware/index.js +0 -16
  204. package/output/app/middleware/rate-limit.d.ts +0 -24
  205. package/output/app/middleware/rate-limit.d.ts.map +0 -1
  206. package/output/app/middleware/rate-limit.js +0 -46
  207. package/output/app/middleware-order.test.d.ts +0 -2
  208. package/output/app/middleware-order.test.d.ts.map +0 -1
  209. package/output/app/middleware-order.test.js +0 -148
  210. package/output/app/permission-operations.d.ts +0 -48
  211. package/output/app/permission-operations.d.ts.map +0 -1
  212. package/output/app/permission-operations.js +0 -77
  213. package/output/app/permission-operations.test.d.ts +0 -2
  214. package/output/app/permission-operations.test.d.ts.map +0 -1
  215. package/output/app/permission-operations.test.js +0 -72
  216. package/output/app/permission-registration.test.d.ts +0 -2
  217. package/output/app/permission-registration.test.d.ts.map +0 -1
  218. package/output/app/permission-registration.test.js +0 -138
  219. package/output/app/redis.d.ts.map +0 -1
  220. package/output/app/registered-operations.test.d.ts +0 -2
  221. package/output/app/registered-operations.test.d.ts.map +0 -1
  222. package/output/app/registered-operations.test.js +0 -57
  223. package/output/app/request-id.test.d.ts +0 -2
  224. package/output/app/request-id.test.d.ts.map +0 -1
  225. package/output/app/request-id.test.js +0 -130
  226. package/output/app/resources.test.d.ts +0 -2
  227. package/output/app/resources.test.d.ts.map +0 -1
  228. package/output/app/resources.test.js +0 -199
  229. package/output/app/shutdown.d.ts +0 -22
  230. package/output/app/shutdown.d.ts.map +0 -1
  231. package/output/app/shutdown.js +0 -30
  232. package/output/app/shutdown.test.d.ts +0 -2
  233. package/output/app/shutdown.test.d.ts.map +0 -1
  234. package/output/app/shutdown.test.js +0 -103
  235. package/output/app/signal.d.ts +0 -12
  236. package/output/app/signal.d.ts.map +0 -1
  237. package/output/app/smoke.test.d.ts +0 -2
  238. package/output/app/smoke.test.d.ts.map +0 -1
  239. package/output/app/smoke.test.js +0 -210
  240. package/output/app/test-support.d.ts +0 -21
  241. package/output/app/test-support.d.ts.map +0 -1
  242. package/output/app/test-support.js +0 -24
  243. package/output/app/type-augment.d.ts.map +0 -1
  244. package/output/app/types.d.ts +0 -228
  245. package/output/app/types.d.ts.map +0 -1
  246. package/output/context/index.d.ts +0 -6
  247. package/output/context/index.d.ts.map +0 -1
  248. package/output/context/index.js +0 -14
  249. package/output/rate-limiter/index.d.ts +0 -5
  250. package/output/rate-limiter/index.d.ts.map +0 -1
  251. package/output/rate-limiter/index.js +0 -4
  252. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  253. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  254. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  255. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  256. package/output/rate-limiter/types.d.ts.map +0 -1
  257. package/output/redis-cache/index.d.ts +0 -4
  258. package/output/redis-cache/index.d.ts.map +0 -1
  259. package/output/redis-cache/index.js +0 -1
  260. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  261. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  262. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  263. package/output/schema/index.d.ts.map +0 -1
  264. /package/output/{app → core}/env.test.d.ts +0 -0
  265. /package/output/{rate-limiter → core/modules/auth-provider}/types.js +0 -0
  266. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  267. /package/output/{context → core/modules/context}/index.test.js +0 -0
  268. /package/output/{app → core}/type-augment.js +0 -0
  269. /package/output/{app → core}/types.js +0 -0
  270. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  271. /package/output/{rate-limiter → rate-limit}/memory-store.d.ts +0 -0
  272. /package/output/{rate-limiter → rate-limit}/memory-store.js +0 -0
  273. /package/output/{rate-limiter → rate-limit}/redis-store.d.ts +0 -0
  274. /package/output/{rate-limiter → rate-limit}/redis-store.js +0 -0
  275. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  276. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -0,0 +1,21 @@
1
+ import type { ConfigEnv } from '../../env.js';
2
+ import type { FastifyInstance } from 'fastify';
3
+ /**
4
+ * core `cookie`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
5
+ *
6
+ * `@fastify/cookie`;cookie 的 domain 从 `ctx.readConfig('server')` 取(**请求期惰性现读**,刷新后即生效),
7
+ * cookie 名由调用方(`auth` 中间件)显式给出;显式设 `sameSite`(默认 `lax`)与 `secure`
8
+ * (默认 `ENV === 'production'`);注册在 `auth` 之前(框架内置序),故 `auth` 读 cookie 无需用户保证。
9
+ */
10
+ export interface AuthCookieOptions {
11
+ maxAge?: number;
12
+ path?: string;
13
+ domain?: string;
14
+ }
15
+ export interface CookieCoreOptions {
16
+ env: ConfigEnv;
17
+ /** 取最新配置中某个键的访问器(**每次调用现读**,不得缓存返回值) */
18
+ readConfig: (key: string) => unknown;
19
+ }
20
+ export declare function registerCookie(app: FastifyInstance, options: CookieCoreOptions): void;
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/cookie/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAA;AAE5D;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAMD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,SAAS,CAAA;IACd,wCAAwC;IACxC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CACrC;AAOD,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAerF"}
@@ -1,11 +1,6 @@
1
1
  import cookie from '@fastify/cookie'; // eslint-disable-line import/default -- @fastify/cookie 是 CJS `export =`,esModuleInterop 下默认导入即正确用法
2
- /** 从 `readConfig('server')` 的返回值读取 cookie 名列表(缺省 `['token']`,对齐四家现状) */
3
- export function resolveCookieNames(server) {
4
- const names = server?.cookieNames;
5
- return Array.isArray(names) && names.length > 0 ? names : ['token'];
6
- }
7
2
  /** 从 `readConfig('server')` 的返回值读取 cookie domain */
8
- export function resolveCookieDomain(server) {
3
+ function resolveCookieDomain(server) {
9
4
  return server?.domain;
10
5
  }
11
6
  export function registerCookie(app, options) {
@@ -0,0 +1,4 @@
1
+ import type { AppIdentity } from '../../types.js';
2
+ export declare function attachServerIdentity(app: unknown, identity: AppIdentity): void;
3
+ export declare function readServerIdentity(app: unknown): AppIdentity | undefined;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/identity/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAWjD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,GAAG,IAAI,CAE9E;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAExE"}
@@ -5,7 +5,7 @@
5
5
  * (可见性按需收窄),框架内置单元(`auth` 权限自注册 / `rateLimit` 前缀)需要 `service` /
6
6
  * `version` 时从这里读。
7
7
  */
8
- export const SERVER_IDENTITY = Symbol.for('taicode.server/identity');
8
+ const SERVER_IDENTITY = Symbol.for('taicode.server/identity');
9
9
  export function attachServerIdentity(app, identity) {
10
10
  Object.defineProperty(app, SERVER_IDENTITY, { value: identity, enumerable: false, configurable: true });
11
11
  }
@@ -0,0 +1,5 @@
1
+ import type { ConfigEnv } from '../../env.js';
2
+ import type { FastifyServerOptions } from 'fastify';
3
+ /** 构造 Fastify 的 logger 选项 */
4
+ export declare function createLoggerOptions(env: ConfigEnv): FastifyServerOptions['logger'];
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/logging/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAsBnD,6BAA6B;AAC7B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAQlF"}
@@ -1,4 +1,4 @@
1
- import { DEFAULT_LOG_LEVEL } from '../env.js';
1
+ import { DEFAULT_LOG_LEVEL } from '../../env.js';
2
2
  /**
3
3
  * core `logging`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
4
4
  *
@@ -6,7 +6,7 @@ import { DEFAULT_LOG_LEVEL } from '../env.js';
6
6
  * 开关随 `CoreOptions` 一并删除,日志始终开启,不再由 `debug` 控制。
7
7
  */
8
8
  /** 默认 `redact` 路径(设计 §5.1) */
9
- export const LOG_REDACT_PATHS = [
9
+ const LOG_REDACT_PATHS = [
10
10
  'authorization',
11
11
  'cookie',
12
12
  'set-cookie',
@@ -0,0 +1,7 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ export interface OpenApiCoreOptions {
3
+ title: string;
4
+ version: string;
5
+ }
6
+ export declare function registerOpenApi(app: FastifyInstance, options: OpenApiCoreOptions): Promise<void>;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/openapi/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAgB9C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BtG"}
@@ -1,18 +1,19 @@
1
- import scalar from '@scalar/fastify-api-reference';
2
1
  import swagger from '@fastify/swagger';
3
2
  import { createJsonSchemaTransform } from 'fastify-type-provider-zod';
4
3
  /**
5
4
  * core `openapi`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
6
5
  *
7
- * `@fastify/swagger` + `@scalar/fastify-api-reference`;`/reference` 与 `/reference/openapi.json`;
8
- * `servers: [{ url: '/' }]`(构建期不依赖 env / DB);`version` 取 `app.version`(必填,无默认);
9
- * `securitySchemes.bearerAuth`;`onListen` 打印实际地址。
6
+ * 只做**文档生成**:`@fastify/swagger` 动态模式(不注册任何 HTTP 路由),`servers: [{ url: '/' }]`
7
+ * (构建期不依赖 env / DB)、`version` 取 `app.version`(必填,无默认)、`securitySchemes.bearerAuth`。
8
+ *
9
+ * 文档的**出口**在系统路由里(`GET /system/openapi`,见 `core/modules/system-routes`):
10
+ * 文档是服务的一部分元信息,不该单独占一个顶层前缀;也不再需要 UI——
11
+ * `@scalar/fastify-api-reference` 已移除,各仓 console 用自己的 api-gen 消费 spec。
10
12
  */
11
- export const DOC_ROUTE_PREFIX = '/reference';
12
- export const DOC_SPEC_ROUTE = '/reference/openapi.json';
13
- export const OPENAPI_SPEC_VERSION = '3.0.3';
13
+ /** OpenAPI 规范版本(固定,不可配置) */
14
+ const OPENAPI_SPEC_VERSION = '3.0.3';
14
15
  export async function registerOpenApi(app, options) {
15
- app.register(swagger, {
16
+ await app.register(swagger, {
16
17
  openapi: {
17
18
  openapi: OPENAPI_SPEC_VERSION,
18
19
  info: {
@@ -30,13 +31,11 @@ export async function registerOpenApi(app, options) {
30
31
  },
31
32
  transform: createJsonSchemaTransform({ skipList: [] }),
32
33
  });
33
- app.register(scalar, { routePrefix: DOC_ROUTE_PREFIX });
34
- // 确定性冲刷插件队列:让 swagger / scalar 的插件体在**任何路由注册之前**执行完——
35
- // @fastify/swagger 经 fastify-plugin 在 root 上挂 `onRoute` 钩子,只有它先就位,
34
+ // 确定性冲刷插件队列:让 swagger 的 root `onRoute` 钩子在**任何路由注册之前**就位——
36
35
  // 后续 `routers` 阶段注册的路由才会被采集进文档。不得依赖「恰好有 cors 等其它
37
36
  // `app.register` 顺带冲刷队列」这种隐式巧合(设计 §5.2 / §10-18)。
38
37
  await app.after();
39
38
  app.addHook('onListen', () => {
40
- app.log.info({ docPath: DOC_SPEC_ROUTE }, '[openapi] api reference ready');
39
+ app.log.info({ docPath: '/system/openapi' }, '[openapi] spec ready');
41
40
  });
42
41
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 权限操作提取(设计 §5.2)——**框架内置**。
3
+ *
4
+ * **唯一来源是 OpenAPI 文档**:`openapi` 已是 core 内置能力(§5.1),文档里的 `paths` 就是
5
+ * 本服务对外的完整操作面。权限目录直接从文档提取,**不再自建第二份路由注册表**——两套来源
6
+ * 必然漂移(`HEAD` / 前缀 / 插件路由的处理只要有一处不同就会对不上)。
7
+ *
8
+ * 文档里有什么就是什么:框架不给任何路由开「不进文档」的口子,因此也不存在
9
+ * 「路由活着、权限系统里却没有」的隐式状态。
10
+ *
11
+ * 目录元素形状(`{ operation, summary?, description?, tags? }`)与各仓原自写实现一致:
12
+ *
13
+ * - 缺 `operationId` 的条目(如 `parameters` / vendor 扩展)**跳过**;
14
+ * - 缺 `paths` / 空文档 → 返回 `[]`;
15
+ * - `summary` / `description` / `tags` 缺省分别归一为 `''` / `''` / `[]`。
16
+ * (正常路径下不会发生:core 的 schema 守卫已在注册期强制四项齐全。)
17
+ */
18
+ /** 单条权限操作(对齐 account `POST /permission/operation/register` 的 `operations` 元素) */
19
+ export interface PermissionOperation {
20
+ operation: string;
21
+ summary: string;
22
+ description: string;
23
+ tags: string[];
24
+ }
25
+ export declare function extractPermissionOperations(openApiSpec: unknown): PermissionOperation[];
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/permission-operations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAeD,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,OAAO,GAAG,mBAAmB,EAAE,CAoBvF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 权限操作提取(设计 §5.2)——**框架内置**。
3
+ *
4
+ * **唯一来源是 OpenAPI 文档**:`openapi` 已是 core 内置能力(§5.1),文档里的 `paths` 就是
5
+ * 本服务对外的完整操作面。权限目录直接从文档提取,**不再自建第二份路由注册表**——两套来源
6
+ * 必然漂移(`HEAD` / 前缀 / 插件路由的处理只要有一处不同就会对不上)。
7
+ *
8
+ * 文档里有什么就是什么:框架不给任何路由开「不进文档」的口子,因此也不存在
9
+ * 「路由活着、权限系统里却没有」的隐式状态。
10
+ *
11
+ * 目录元素形状(`{ operation, summary?, description?, tags? }`)与各仓原自写实现一致:
12
+ *
13
+ * - 缺 `operationId` 的条目(如 `parameters` / vendor 扩展)**跳过**;
14
+ * - 缺 `paths` / 空文档 → 返回 `[]`;
15
+ * - `summary` / `description` / `tags` 缺省分别归一为 `''` / `''` / `[]`。
16
+ * (正常路径下不会发生:core 的 schema 守卫已在注册期强制四项齐全。)
17
+ */
18
+ export function extractPermissionOperations(openApiSpec) {
19
+ // 出 spec 路径拿到的文档是未知形态;只按结构取字段,缺什么就跳过什么
20
+ const spec = (openApiSpec ?? {});
21
+ const operations = [];
22
+ for (const pathItem of Object.values(spec.paths ?? {})) {
23
+ for (const operation of Object.values(pathItem ?? {})) {
24
+ if (!operation?.operationId) {
25
+ continue;
26
+ }
27
+ operations.push({
28
+ operation: operation.operationId,
29
+ summary: operation.summary ?? '',
30
+ description: operation.description ?? '',
31
+ tags: operation.tags ?? [],
32
+ });
33
+ }
34
+ }
35
+ return operations;
36
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/permission-operations/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { extractPermissionOperations } from './index.js';
3
+ describe('extractPermissionOperations', () => {
4
+ it('从 OpenAPI 文档的 paths 提取操作', () => {
5
+ const spec = {
6
+ paths: {
7
+ '/user': {
8
+ get: { operationId: 'listUser', summary: '列表', description: '分页列出用户', tags: ['用户'] },
9
+ post: { operationId: 'createUser', summary: '创建', description: '创建用户', tags: ['用户'] },
10
+ },
11
+ },
12
+ };
13
+ expect(extractPermissionOperations(spec)).toEqual([
14
+ { operation: 'listUser', summary: '列表', description: '分页列出用户', tags: ['用户'] },
15
+ { operation: 'createUser', summary: '创建', description: '创建用户', tags: ['用户'] },
16
+ ]);
17
+ });
18
+ it('跳过没有 operationId 的条目(parameters / vendor 扩展 / 未声明 schema 的路由)', () => {
19
+ const spec = {
20
+ paths: {
21
+ '/user': {
22
+ parameters: [{ name: 'id', in: 'path' }],
23
+ get: { operationId: 'listUser' },
24
+ },
25
+ },
26
+ };
27
+ expect(extractPermissionOperations(spec).map((item) => item.operation)).toEqual(['listUser']);
28
+ });
29
+ it('summary / description / tags 缺省归一为空值', () => {
30
+ const spec = { paths: { '/user': { get: { operationId: 'listUser' } } } };
31
+ expect(extractPermissionOperations(spec)).toEqual([
32
+ { operation: 'listUser', summary: '', description: '', tags: [] },
33
+ ]);
34
+ });
35
+ it('文档缺失或没有 paths 时返回空数组', () => {
36
+ expect(extractPermissionOperations(undefined)).toEqual([]);
37
+ expect(extractPermissionOperations(null)).toEqual([]);
38
+ expect(extractPermissionOperations({})).toEqual([]);
39
+ expect(extractPermissionOperations({ paths: {} })).toEqual([]);
40
+ });
41
+ it('只按 paths 取,文档里没有的条目不会凭空出现', () => {
42
+ const spec = { paths: { '/user': { get: { operationId: 'listUser' } } } };
43
+ expect(extractPermissionOperations(spec).map((item) => item.operation)).toEqual(['listUser']);
44
+ });
45
+ });
@@ -12,9 +12,6 @@ import type { FastifyInstance } from 'fastify';
12
12
  * `onRequest` 钩子在链路最前补齐归一,并同步 `request.id` / 日志字段 / 响应 header。
13
13
  */
14
14
  export declare const REQUEST_ID_HEADER = "x-request-id";
15
- export declare const REQUEST_ID_LOG_LABEL = "requestId";
16
- /** 归一化入站 header 值(设计 §5.1):数组取首个;**trim 后为空视为缺失**(返回 `undefined`) */
17
- export declare function normalizeInboundRequestId(inbound: string | string[] | undefined): string | undefined;
18
15
  /** 传给 `Fastify()` 的 requestId 相关选项 */
19
16
  export declare function createRequestIdOptions(): {
20
17
  requestIdHeader: string;
@@ -33,4 +30,4 @@ export declare function createRequestIdOptions(): {
33
30
  * 入口 `request.id`、日志字段 `requestId`、响应 header 三处一致。
34
31
  */
35
32
  export declare function registerRequestId(app: FastifyInstance): void;
36
- //# sourceMappingURL=request-id.d.ts.map
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/request-id/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAE9C;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,iBAAiB,iBAAiB,CAAA;AAa/C,sCAAsC;AACtC,wBAAgB,sBAAsB,IAAI;IACxC,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,EAAE,CAAC,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAA;KAAE,KAAK,MAAM,CAAA;CAC1F,CAOA;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAwB5D"}
@@ -12,9 +12,9 @@ import { randomUUID } from 'node:crypto';
12
12
  * `onRequest` 钩子在链路最前补齐归一,并同步 `request.id` / 日志字段 / 响应 header。
13
13
  */
14
14
  export const REQUEST_ID_HEADER = 'x-request-id';
15
- export const REQUEST_ID_LOG_LABEL = 'requestId';
15
+ const REQUEST_ID_LOG_LABEL = 'requestId';
16
16
  /** 归一化入站 header 值(设计 §5.1):数组取首个;**trim 后为空视为缺失**(返回 `undefined`) */
17
- export function normalizeInboundRequestId(inbound) {
17
+ function normalizeInboundRequestId(inbound) {
18
18
  const value = Array.isArray(inbound) ? inbound[0] : inbound;
19
19
  if (typeof value !== 'string') {
20
20
  return undefined;
@@ -0,0 +1,22 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ErrorClassifier } from '../../types.js';
3
+ export interface ResponseEnvelope<T> {
4
+ status: string;
5
+ message: string;
6
+ data: T;
7
+ }
8
+ export declare function createSuccessResponse<T>(data: T): ResponseEnvelope<T>;
9
+ export declare function createErrorResponse(status: string, message: string): ResponseEnvelope<null>;
10
+ /**
11
+ * 资源错误分类器(单一来源:`types.ts`)。
12
+ *
13
+ * **每个资源注册自己的分类器**(一个资源一个,各管各的错误域);由服务仓在 `resources(ctx)` 里
14
+ * 通过 `ctx.registerClassifyError` 注册;未注册时错误落 `UNKNOWN_ERROR` 兜底。
15
+ */
16
+ export type { ErrorClassifier };
17
+ export interface ResponseCoreOptions {
18
+ /** 资源错误分类(各资源经 `ctx.registerClassifyError` 注册;缺省 = 不做资源错误分类) */
19
+ classifyResourceError?: ErrorClassifier;
20
+ }
21
+ export declare function registerResponse(app: FastifyInstance, options?: ResponseCoreOptions): void;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/response/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAoCrD,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAA;CACR;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAErE;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAE3F;AAsBD;;;;;GAKG;AACH,YAAY,EAAE,eAAe,EAAE,CAAA;AAE/B,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,qBAAqB,CAAC,EAAE,eAAe,CAAA;CACxC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,GAAE,mBAAwB,GAAG,IAAI,CAyD9F"}
@@ -1,24 +1,35 @@
1
+ import { SystemError, UserError } from '@taicode/common-base';
1
2
  import { hasZodFastifySchemaValidationErrors, isResponseSerializationError } from 'fastify-type-provider-zod';
2
- import { SystemError, UserError } from '../errors.js';
3
3
  /**
4
4
  * core `response`(设计 §5.1 / §8,错误契约 D7)——**框架内置、锁定、不可替换/修改**。
5
5
  *
6
6
  * - 成功信封 `{ status: 'SUCCESS', message: 'ok', data }`(由 `preSerialization` 统一包裹)。
7
7
  * - 错误信封 `{ status: <错误码字符串>, message, data: null }`。
8
- * - `setErrorHandler` 固定分支:`UserError`→400(`error.status` 字面量)/ `SystemError`→500 /
8
+ * - `setErrorHandler` 固定分支:`UserError`→400 / `SystemError`→500 /
9
9
  * zod→400 / serde→500 / 资源(各资源 `ctx.registerClassifyError` 注册的分类器)→500 / 未知→500;**禁止按 `error.type` 细分**。
10
10
  * - `setNotFoundHandler` → 404 + `NOT_IMPLEMENTED`(落 `warn`,文案对齐 expense/notice)。
11
11
  * - 错误日志分级:`UserError` / zod → `warn`,其余 → `error`(带 stack)。
12
12
  * - **错误只给稳定码**(`status` 放错误码字符串),文案不归框架(由前端 / i18n 决定,§10-6)。
13
+ *
14
+ * 错误类型本身**不在这里定义**:`UserError` / `SystemError` 复用 `@taicode/common-base` 的
15
+ * 单一来源(连 `Symbol.for` 品牌都同名,故 base 抛出的同类错误同样被识别),框架只负责
16
+ * **状态码映射**与响应信封,不重复实现错误类。
17
+ */
18
+ /**
19
+ * 固定状态码映射(D7):`UserError`→400 / `SystemError`→500。
20
+ *
21
+ * 映射写在框架侧而非错误类上(base 的类不带 `status`)。判定一律走 `is()` 品牌守卫,
22
+ * **不用 `instanceof`**——跨包 / 编译后 / 原型链丢失都仍然可靠;**禁止按 `error.type` 细分**。
13
23
  */
14
- export const SUCCESS_STATUS = 'SUCCESS';
15
- export const NOT_IMPLEMENTED_STATUS = 'NOT_IMPLEMENTED';
16
- export const INVALID_INPUT_STATUS = 'INVALID_INPUT';
17
- export const SERIALIZATION_ERROR_STATUS = 'RESPONSE_SERIALIZATION_ERROR';
18
- export const UNKNOWN_ERROR_STATUS = 'UNKNOWN_ERROR';
24
+ const USER_ERROR_STATUS = 400;
25
+ const SYSTEM_ERROR_STATUS = 500;
26
+ const SUCCESS_STATUS = 'SUCCESS';
27
+ const NOT_IMPLEMENTED_STATUS = 'NOT_IMPLEMENTED';
28
+ const INVALID_INPUT_STATUS = 'INVALID_INPUT';
29
+ const SERIALIZATION_ERROR_STATUS = 'RESPONSE_SERIALIZATION_ERROR';
30
+ const UNKNOWN_ERROR_STATUS = 'UNKNOWN_ERROR';
19
31
  /** 通用兜底文案(仅在 `error.message` 为空时使用) */
20
- export const DEFAULT_USER_MESSAGE = '请求失败';
21
- export const DEFAULT_SYSTEM_MESSAGE = '系统错误';
32
+ const DEFAULT_SYSTEM_MESSAGE = '系统错误';
22
33
  export function createSuccessResponse(data) {
23
34
  return { status: SUCCESS_STATUS, message: 'ok', data };
24
35
  }
@@ -55,12 +66,12 @@ export function registerResponse(app, options = {}) {
55
66
  // ① UserError → 400
56
67
  if (UserError.is(error)) {
57
68
  request.log.warn({ err: error }, '[response] user error');
58
- return reply.code(error.status).send(createErrorResponse(error.type, resolveMessage(error)));
69
+ return reply.code(USER_ERROR_STATUS).send(createErrorResponse(error.type, resolveMessage(error)));
59
70
  }
60
71
  // ② SystemError → 500
61
72
  if (SystemError.is(error)) {
62
73
  request.log.error({ err: error }, '[response] system error');
63
- return reply.code(error.status).send(createErrorResponse(error.type, resolveMessage(error)));
74
+ return reply.code(SYSTEM_ERROR_STATUS).send(createErrorResponse(error.type, resolveMessage(error)));
64
75
  }
65
76
  // ③ zod 校验失败 → 400
66
77
  if (hasZodFastifySchemaValidationErrors(error)) {
@@ -172,6 +172,13 @@ interface RouterSchema extends FastifySchema {
172
172
  operationId: string;
173
173
  description: string;
174
174
  }
175
+ /**
176
+ * 路由 schema 工厂:**四项元信息全部必填**,不留默认值。
177
+ *
178
+ * `operationId` / `summary` / `description` / `tags` 是权限目录与文档可读性的下限,
179
+ * core 的 `installRouteSchemaGuard` 会对运行时缺失逐项报错;这里在类型层面就要求写全,
180
+ * 避免「先给个默认值让它跑起来,回头再补」——补是不会发生的。
181
+ */
175
182
  export declare function routerSchema<T extends RouterSchema>(schema: T & RouterSchema): T & RouterSchema;
176
183
  export {};
177
- //# sourceMappingURL=index.d.ts.map
184
+ //# sourceMappingURL=builders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builders.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/schema/builders.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACzC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AA2B5C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;;;;;;;;;;;;;kBAMlF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;kBAKxD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,kCAE/B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;kBAKvD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;;;;;;;;;;kBAMvF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;mBAK7D;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;;;;;;;;;kBAMjE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;mBAKjE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;;;;;;;;;;;;;kBAMtF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;;;kBAK5D;AAGD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;;;;kBAMlF;AAED,wBAAgB,cAAc,CAAC,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;;;;;;;;;;;;;;;;;;EAMvG;AAED,UAAU,YAAa,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,YAAY,GAAG,CAAC,GAAG,YAAY,CAO/F"}
@@ -167,14 +167,17 @@ export function responseSchema(t, u, e) {
167
167
  '500': responseDataSchema(z.null(), e),
168
168
  };
169
169
  }
170
+ /**
171
+ * 路由 schema 工厂:**四项元信息全部必填**,不留默认值。
172
+ *
173
+ * `operationId` / `summary` / `description` / `tags` 是权限目录与文档可读性的下限,
174
+ * core 的 `installRouteSchemaGuard` 会对运行时缺失逐项报错;这里在类型层面就要求写全,
175
+ * 避免「先给个默认值让它跑起来,回头再补」——补是不会发生的。
176
+ */
170
177
  export function routerSchema(schema) {
171
178
  // 验证必需字段
172
179
  if (!schema.operationId) {
173
180
  throw new Error('routerSchema requires operationId');
174
181
  }
175
- // 如果没有提供 tags,设置默认值
176
- if (!schema.tags) {
177
- schema.tags = ['默认'];
178
- }
179
182
  return { ...schema };
180
183
  }
@@ -0,0 +1,34 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ /**
3
+ * core `schema`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
4
+ *
5
+ * 三部分:
6
+ * 1. `registerSchema`:装配 `fastify-type-provider-zod` 的 validator / serializer compiler;
7
+ * 2. `installRouteSchemaGuard`:**强制每条路由声明 schema**(`operationId` / `summary` /
8
+ * `description` / `tags` 缺一即启动失败);
9
+ * 3. `builders`:业务侧构造路由 schema 的约定工厂(`responseSchema` / `routerSchema` / 分页等)。
10
+ */
11
+ export declare function registerSchema(app: FastifyInstance): void;
12
+ /**
13
+ * 强制所有业务路由提供 schema(**注册处即失败**,不给「先跑起来再补」的口子)。
14
+ *
15
+ * 为什么必须硬性:
16
+ *
17
+ * - OpenAPI 文档是**权限目录的唯一来源**(`auth` 的权限自注册直接读文档)。一条路由没
18
+ * `operationId`,它就在权限系统里**不存在**——表现为「接口永远判不过」,且没有任何报错。
19
+ * 这种失败必须发生在启动期,不能留到线上。
20
+ * - `summary` / `description` / `tags` 是文档可读性的下限;没有它们的文档等于没有文档。
21
+ *
22
+ * ## 为什么没有「声明一个标记就不检查」的入口
23
+ *
24
+ * 守卫要防的就是「路由活着、权限系统里却没有」。任何**按路由声明**的布尔值能放宽它,
25
+ * 都等于把这个洞重新打开,而且是**显式地**打开——比遗漏更危险,因为它看起来像设计。
26
+ *
27
+ * 所以只有两类被排除,且全是方法级的结构事实,路由无法参与决定:
28
+ * `HEAD`(GET 自动派生)与 `OPTIONS`(CORS 预检)。
29
+ *
30
+ * 也没有「不进文档」的隐藏开关:文档就是权限目录,两者不能有分歧。
31
+ */
32
+ export declare function installRouteSchemaGuard(app: FastifyInstance): void;
33
+ export * from './builders.js';
34
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/schema/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,SAAS,CAAA;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAMzD;AAeD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAqBlE;AAED,cAAc,eAAe,CAAA"}
@@ -0,0 +1,62 @@
1
+ import { serializerCompiler, validatorCompiler } from 'fastify-type-provider-zod';
2
+ /**
3
+ * core `schema`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
4
+ *
5
+ * 三部分:
6
+ * 1. `registerSchema`:装配 `fastify-type-provider-zod` 的 validator / serializer compiler;
7
+ * 2. `installRouteSchemaGuard`:**强制每条路由声明 schema**(`operationId` / `summary` /
8
+ * `description` / `tags` 缺一即启动失败);
9
+ * 3. `builders`:业务侧构造路由 schema 的约定工厂(`responseSchema` / `routerSchema` / 分页等)。
10
+ */
11
+ export function registerSchema(app) {
12
+ app.setValidatorCompiler(validatorCompiler);
13
+ app.setSerializerCompiler(serializerCompiler);
14
+ }
15
+ /** 每条路由必须声明的 schema 字段(与权限目录同源:OpenAPI 文档就是权限目录的唯一来源) */
16
+ const REQUIRED_SCHEMA_FIELDS = ['operationId', 'summary', 'description', 'tags'];
17
+ /**
18
+ * 被排除在外的**方法**(结构事实,不是路由声明)。
19
+ *
20
+ * - `HEAD`:Fastify 为 GET 自动派生,schema 与 GET 完全相同,重复校验无意义;
21
+ * - `OPTIONS`:CORS 预检(`@fastify/cors` 注册的 `OPTIONS *`),永远不是业务操作。
22
+ */
23
+ const NON_OPERATION_METHODS = new Set(['HEAD', 'OPTIONS']);
24
+ /**
25
+ * 强制所有业务路由提供 schema(**注册处即失败**,不给「先跑起来再补」的口子)。
26
+ *
27
+ * 为什么必须硬性:
28
+ *
29
+ * - OpenAPI 文档是**权限目录的唯一来源**(`auth` 的权限自注册直接读文档)。一条路由没
30
+ * `operationId`,它就在权限系统里**不存在**——表现为「接口永远判不过」,且没有任何报错。
31
+ * 这种失败必须发生在启动期,不能留到线上。
32
+ * - `summary` / `description` / `tags` 是文档可读性的下限;没有它们的文档等于没有文档。
33
+ *
34
+ * ## 为什么没有「声明一个标记就不检查」的入口
35
+ *
36
+ * 守卫要防的就是「路由活着、权限系统里却没有」。任何**按路由声明**的布尔值能放宽它,
37
+ * 都等于把这个洞重新打开,而且是**显式地**打开——比遗漏更危险,因为它看起来像设计。
38
+ *
39
+ * 所以只有两类被排除,且全是方法级的结构事实,路由无法参与决定:
40
+ * `HEAD`(GET 自动派生)与 `OPTIONS`(CORS 预检)。
41
+ *
42
+ * 也没有「不进文档」的隐藏开关:文档就是权限目录,两者不能有分歧。
43
+ */
44
+ export function installRouteSchemaGuard(app) {
45
+ app.addHook('onRoute', (routeOptions) => {
46
+ const methods = Array.isArray(routeOptions.method) ? routeOptions.method : [routeOptions.method];
47
+ if (methods.every((method) => NON_OPERATION_METHODS.has(method))) {
48
+ return;
49
+ }
50
+ const schema = routeOptions.schema;
51
+ const missing = REQUIRED_SCHEMA_FIELDS.filter((field) => {
52
+ const value = schema?.[field];
53
+ return value === undefined || value === null || (Array.isArray(value) ? value.length === 0 : value === '');
54
+ });
55
+ if (missing.length > 0) {
56
+ throw new Error(`[schema] 路由 ${methods.join(',')} ${routeOptions.url} 缺少 schema.${missing.join(' / schema.')}:` +
57
+ '所有业务路由都必须用 `routerSchema({ operationId, summary, description, tags, ... })` 声明,' +
58
+ '权限目录直接由 OpenAPI 文档生成,未声明的路由在权限系统里不存在');
59
+ }
60
+ });
61
+ }
62
+ export * from './builders.js';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/schema/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import Fastify from 'fastify';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { installRouteSchemaGuard, registerSchema } from './index.js';
4
+ const FULL_SCHEMA = {
5
+ operationId: 'listUser',
6
+ summary: '列表',
7
+ description: '分页列出用户',
8
+ tags: ['用户'],
9
+ };
10
+ function buildApp() {
11
+ const app = Fastify();
12
+ registerSchema(app);
13
+ installRouteSchemaGuard(app);
14
+ return app;
15
+ }
16
+ describe('installRouteSchemaGuard', () => {
17
+ it('声明完整 schema 的路由通过(含 Fastify 自动派生的 HEAD)', async () => {
18
+ const app = buildApp();
19
+ app.get('/user', { schema: FULL_SCHEMA }, async () => ({ ok: true }));
20
+ await expect(app.ready()).resolves.toBeDefined();
21
+ await app.close();
22
+ });
23
+ it('缺少 schema 时在注册处即抛错(不等启动)', async () => {
24
+ const app = buildApp();
25
+ expect(() => app.get('/user', async () => ({ ok: true }))).toThrow(/GET \/user 缺少 schema\.operationId/);
26
+ await app.close();
27
+ });
28
+ it('缺少 summary / description / tags 时逐个列出', async () => {
29
+ const app = buildApp();
30
+ expect(() => app.post('/user', { schema: { operationId: 'createUser' } }, async () => ({ ok: true }))).toThrow(/schema\.summary \/ schema\.description \/ schema\.tags/);
31
+ await app.close();
32
+ });
33
+ it('OPTIONS 按方法豁免(CORS 预检不是业务操作)', async () => {
34
+ const app = buildApp();
35
+ app.options('/user', async () => ({}));
36
+ await expect(app.ready()).resolves.toBeDefined();
37
+ await app.close();
38
+ });
39
+ it('插件内注册的路由同样受约束(错误冒泡成启动失败)', async () => {
40
+ const app = buildApp();
41
+ let caught;
42
+ try {
43
+ await app.register(async (instance) => {
44
+ instance.get('/nested', async () => ({ ok: true }));
45
+ });
46
+ await app.ready();
47
+ }
48
+ catch (error) {
49
+ caught = error;
50
+ }
51
+ expect(caught).toBeInstanceOf(Error);
52
+ expect(caught.message).toMatch(/GET \/nested 缺少 schema/);
53
+ await app.close();
54
+ });
55
+ });
@@ -0,0 +1,15 @@
1
+ import type { AppIdentity, AppInstance } from '../../types.js';
2
+ export interface SystemRoutesOptions {
3
+ /** 身份块(`service` / `title` / `version`):`GET /` 与 `/system/info` 从这里派生 */
4
+ identity: AppIdentity;
5
+ /** 已构造的 services 实体(`ctx.services`):健康检查遍历其可选 `health()` */
6
+ services: Record<string, unknown>;
7
+ /** 取**已加载**配置中某个键的访问器(请求期惰性现读;出 spec 路径不得调用) */
8
+ readConfig: (key: string) => unknown;
9
+ }
10
+ /**
11
+ * 注册框架内置系统路由(5 个端点)。由 `registerSystemRoutesStep` 在 `server.routers(...)` **之前**调用,
12
+ * 保证框架路由先就位。
13
+ */
14
+ export declare function registerSystemRoutes(app: AppInstance, options: SystemRoutesOptions): void;
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/system-routes/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAmB,MAAM,gBAAgB,CAAA;AA+K/E,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,QAAQ,EAAE,WAAW,CAAA;IACrB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,gDAAgD;IAChD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CACrC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAwDzF"}