@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
@@ -0,0 +1,105 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+ import { loadEnvironment } from './env.js';
6
+ const ORIGINAL_ENV = { ...process.env };
7
+ const ORIGINAL_CWD = process.cwd();
8
+ const TEMP_ROOTS = [];
9
+ afterEach(() => {
10
+ process.env = { ...ORIGINAL_ENV };
11
+ process.chdir(ORIGINAL_CWD);
12
+ for (const root of TEMP_ROOTS.splice(0)) {
13
+ rmSync(root, { recursive: true, force: true });
14
+ }
15
+ vi.resetModules();
16
+ });
17
+ /** 在临时目录里起一个项目根并写文件,然后切过去(`dotenv` 按 `process.cwd()` 解析相对路径) */
18
+ function useTempProject(files) {
19
+ const root = mkdtempSync(join(tmpdir(), 'common-env-'));
20
+ TEMP_ROOTS.push(root);
21
+ for (const [name, content] of Object.entries(files)) {
22
+ writeFileSync(join(root, name), content);
23
+ }
24
+ process.chdir(root);
25
+ }
26
+ describe('loadEnvironment · runtime(工具进程 / startServer)', () => {
27
+ it('加载 .env.dev / .env / .env.local 三层,且逐层覆盖', () => {
28
+ useTempProject({
29
+ '.env.dev': 'PROBE_LAYER=dev\nPROBE_DEV_ONLY=yes\n',
30
+ '.env': 'PROBE_LAYER=env\n',
31
+ '.env.local': 'PROBE_LAYER=local\n',
32
+ });
33
+ delete process.env.PROBE_LAYER;
34
+ delete process.env.PROBE_DEV_ONLY;
35
+ loadEnvironment();
36
+ expect(process.env.PROBE_LAYER).toBe('local');
37
+ expect(process.env.PROBE_DEV_ONLY).toBe('yes');
38
+ });
39
+ it('不加载测试层(ENV 不是 test 时)', () => {
40
+ useTempProject({ '.env.test': 'PROBE_TEST_ONLY=yes\n' });
41
+ process.env.ENV = 'development';
42
+ delete process.env.PROBE_TEST_ONLY;
43
+ loadEnvironment();
44
+ expect(process.env.PROBE_TEST_ONLY).toBeUndefined();
45
+ });
46
+ it('ENV=test 时追加测试层', () => {
47
+ useTempProject({ '.env.test': 'PROBE_TEST_ONLY=yes\n' });
48
+ process.env.ENV = 'test';
49
+ delete process.env.PROBE_TEST_ONLY;
50
+ loadEnvironment();
51
+ expect(process.env.PROBE_TEST_ONLY).toBe('yes');
52
+ });
53
+ it('不给 ENV 时不兜底(运行期必须有明确身份)', () => {
54
+ useTempProject({});
55
+ delete process.env.ENV;
56
+ expect(loadEnvironment().ENV).toBe('');
57
+ });
58
+ });
59
+ describe('loadEnvironment · test(测试入口)', () => {
60
+ it('无条件加载测试层,不需要调用方先声明 ENV=test', () => {
61
+ useTempProject({ '.env.test': 'PROBE_TEST_ONLY=yes\n' });
62
+ process.env.ENV = 'development';
63
+ delete process.env.PROBE_TEST_ONLY;
64
+ loadEnvironment({ mode: 'test' });
65
+ expect(process.env.PROBE_TEST_ONLY).toBe('yes');
66
+ });
67
+ it('.env.test.local 覆盖 .env.test', () => {
68
+ useTempProject({
69
+ '.env.test': 'PROBE_LAYER=from-test\n',
70
+ '.env.test.local': 'PROBE_LAYER=from-local\n',
71
+ });
72
+ delete process.env.PROBE_LAYER;
73
+ loadEnvironment({ mode: 'test' });
74
+ expect(process.env.PROBE_LAYER).toBe('from-local');
75
+ });
76
+ it('没有任何 ENV 时兜底成 test', () => {
77
+ useTempProject({});
78
+ delete process.env.ENV;
79
+ expect(loadEnvironment({ mode: 'test' }).ENV).toBe('test');
80
+ });
81
+ it('已声明 ENV 时不被兜底改写', () => {
82
+ useTempProject({});
83
+ process.env.ENV = 'development';
84
+ expect(loadEnvironment({ mode: 'test' }).ENV).toBe('development');
85
+ });
86
+ });
87
+ describe('外部注入优先级(两种模式都必须成立)', () => {
88
+ it('runtime:.env 不覆盖进程已注入的值', async () => {
89
+ useTempProject({ '.env': 'PROBE_LAYER=from-file\n' });
90
+ // 同上:外部注入的快照在模块 import 时建立,重新 import 才能模拟真实注入时机
91
+ vi.resetModules();
92
+ process.env.PROBE_LAYER = 'from-process';
93
+ const fresh = await import('./env.js');
94
+ fresh.loadEnvironment();
95
+ expect(process.env.PROBE_LAYER).toBe('from-process');
96
+ });
97
+ it('test:线上注入的 ENV=production 不被 .env.test 顶掉(环境闸门因此仍会拒绝)', async () => {
98
+ useTempProject({ '.env.test': 'ENV=test\n' });
99
+ // EXTERNAL_ENV 是模块首次 import 时的快照,重新 import 才能模拟「进程启动前注入」
100
+ vi.resetModules();
101
+ process.env.ENV = 'production';
102
+ const fresh = await import('./env.js');
103
+ expect(fresh.loadEnvironment({ mode: 'test' }).ENV).toBe('production');
104
+ });
105
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `core/modules/auth-provider` 的出口:**只有类型,没有实现、没有地址**。
3
+ *
4
+ * 实现由业务服务提供(`createAuthMiddleware({ client })`):框架不建 HTTP 客户端、
5
+ * 不认任何地址、不做签名、不管超时——见 `types.ts` 文件头与设计 §5.2.2。
6
+ *
7
+ * 提供方是**可替换**的:account 只是当前的主要实现,不是框架的预设。
8
+ */
9
+ export type { AuthClient, AuthVerifyMetadata, AuthVerifyPayload, AuthVerifyResult, RegisterPermissionOperationsPayload, } from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/auth-provider/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,mCAAmC,GACpC,MAAM,YAAY,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `core/modules/auth-provider` 的出口:**只有类型,没有实现、没有地址**。
3
+ *
4
+ * 实现由业务服务提供(`createAuthMiddleware({ client })`):框架不建 HTTP 客户端、
5
+ * 不认任何地址、不做签名、不管超时——见 `types.ts` 文件头与设计 §5.2.2。
6
+ *
7
+ * 提供方是**可替换**的:account 只是当前的主要实现,不是框架的预设。
8
+ */
9
+ export {};
@@ -0,0 +1,71 @@
1
+ import type { PermissionOperation } from '../permission-operations/index.js';
2
+ import type { AccessControl } from '../../type-augment.js';
3
+ /**
4
+ * core `auth-provider` 集成层:**只有最小接口约束,没有实现、没有地址**。
5
+ *
6
+ * 鉴权提供方(Auth Provider)是**可替换**的——account 只是当前的主要实现之一,
7
+ * 任何能「校验凭证 + 接收权限目录」的系统都可以填进来(自建 IAM、OIDC 网关、
8
+ * 单点登录服务、甚至进程内的静态校验)。
9
+ *
10
+ * 为什么只留契约(设计 §5.2.2):
11
+ *
12
+ * - 客户端的**构造**依赖业务侧才知道的东西——`serviceUrl`(来自配置 / 服务发现 /
13
+ * 私有网络)、端点路径(属于提供方的业务契约,不在 framework)、服务凭证、
14
+ * 以及项目自己的 `fetch` / 拦截器 / 超时 / 重试策略。框架猜不出来,也不该猜。
15
+ * - 所以框架只声明「**调用鉴权提供方需要什么形状**」,由每个服务在自己 `config` /
16
+ * `resources` 槽位里实现 `AuthClient`,注入给 `auth` 的 `client` 选项。
17
+ *
18
+ * 契约本身是冻结的(框架只用到两个能力:校验凭证、注册权限操作):改形状等于改契约。
19
+ * **地址与路径不进 framework**——它们随部署环境变化,属于业务配置。
20
+ */
21
+ /** 校验请求附带的请求元信息(仅供下游审计 / 日志;不参与鉴权判定) */
22
+ export interface AuthVerifyMetadata {
23
+ ip?: string;
24
+ ips?: string[];
25
+ url?: string;
26
+ method?: string;
27
+ referrer?: string;
28
+ requestId?: string;
29
+ userAgent?: string;
30
+ }
31
+ /** `verify` 的请求体:调鉴权提供方的校验能力时提交什么 */
32
+ export interface AuthVerifyPayload {
33
+ token?: string;
34
+ apiKey?: string;
35
+ /** 调用方服务名(`AppIdentity.service`) */
36
+ service: string;
37
+ /** 操作标识(路由 `schema.operationId`,缺省回落 url) */
38
+ operation?: string;
39
+ /** 路由参数(`request.params`),如 `:id` 这类路径参数 */
40
+ resource?: Record<string, unknown>;
41
+ accessControls?: AccessControl[];
42
+ metadata?: AuthVerifyMetadata;
43
+ }
44
+ /** `verify` 的响应数据 */
45
+ export interface AuthVerifyResult {
46
+ user?: unknown;
47
+ token?: unknown;
48
+ }
49
+ /** 权限操作自注册的请求体:调鉴权提供方的权限目录端点时提交什么 */
50
+ export interface RegisterPermissionOperationsPayload {
51
+ service: string;
52
+ version: string;
53
+ operations: PermissionOperation[];
54
+ }
55
+ /**
56
+ * 鉴权客户端契约:框架只需要**两个**方法。
57
+ *
58
+ * 实现归业务服务(`createAuthMiddleware` 的 `client` 选项):**URL / 路径 / 签名 /
59
+ * 协议(HTTP、gRPC、进程内调用)/ 超时 / 重试全在业务侧**,框架不认识任何地址。
60
+ * account 只是其中一种实现,不是唯一一种。
61
+ *
62
+ * 失败语义:
63
+ * - `verify` 抛错 = 校验不通过或提供方不可用;提供方应**自己抛** `UserError` /
64
+ * `SystemError`(框架原样透出),其余错误统一归类为 `EXTERNAL_SERVICE_ERROR`;
65
+ * - `registerPermissionOperations` 抛错**不致命**(`auth` 只记日志,不中断启动)。
66
+ */
67
+ export interface AuthClient {
68
+ verify(payload: AuthVerifyPayload): Promise<AuthVerifyResult>;
69
+ registerPermissionOperations(payload: RegisterPermissionOperationsPayload): Promise<void>;
70
+ }
71
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/auth-provider/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAA;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAE1D;;;;;;;;;;;;;;;;;GAiBG;AAEH,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,EAAE,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,qCAAqC;AACrC,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,oCAAoC;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;IAChC,QAAQ,CAAC,EAAE,kBAAkB,CAAA;CAC9B;AAED,qBAAqB;AACrB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qCAAqC;AACrC,MAAM,WAAW,mCAAmC;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,mBAAmB,EAAE,CAAA;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC7D,4BAA4B,CAAC,OAAO,EAAE,mCAAmC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1F"}
@@ -0,0 +1,18 @@
1
+ import type { FastifyBaseLogger, FastifyInstance, FastifyRequest } from 'fastify';
2
+ /**
3
+ * core `context`(设计 §5.1)——**框架内置、锁定、不可替换/修改**。
4
+ *
5
+ * 两部分:
6
+ * 1. `RequestContext` / `createContext`:请求级上下文的形状与构造;
7
+ * 2. `registerRequestContext`:把上下文装配到 `request.ctx`(`decorateRequest` + `onRequest`)。
8
+ *
9
+ * `request.ctx` 是 `type-augment` 对外承诺的类型契约,装配必须由框架保证——不能交由
10
+ * 各仓决定「装不装 / 装成什么名字」,否则类型存在而运行时缺失。装饰名固定 `ctx`,无选项。
11
+ */
12
+ export interface RequestContext {
13
+ log: FastifyBaseLogger;
14
+ }
15
+ export declare function createContext(request: FastifyRequest): RequestContext;
16
+ /** 装配 `request.ctx`(框架固定,先于任何路由 / 业务中间件注册) */
17
+ export declare function registerRequestContext(app: FastifyInstance): void;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAEjF;;;;;;;;;GASG;AAEH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,iBAAiB,CAAA;CACvB;AAcD,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAErE;AAED,8CAA8C;AAC9C,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAOjE"}
@@ -0,0 +1,23 @@
1
+ const fallbackLogger = {
2
+ trace: () => { },
3
+ debug: () => { },
4
+ info: () => { },
5
+ warn: () => { },
6
+ error: () => { },
7
+ fatal: () => { },
8
+ child: () => fallbackLogger,
9
+ level: 'info',
10
+ silent: () => { },
11
+ };
12
+ export function createContext(request) {
13
+ return { log: request.log || fallbackLogger };
14
+ }
15
+ /** 装配 `request.ctx`(框架固定,先于任何路由 / 业务中间件注册) */
16
+ export function registerRequestContext(app) {
17
+ // 先声明属性(保持 hidden class 稳定),再在 `onRequest` 逐个请求写入
18
+ app.decorateRequest('ctx', null);
19
+ app.addHook('onRequest', (request, _reply, done) => {
20
+ request.ctx = createContext(request);
21
+ done();
22
+ });
23
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../../../source/core/modules/context/index.test.ts"],"names":[],"mappings":""}
@@ -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,50 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { ErrorClassifier } from '../../types.js';
3
+ declare const SUCCESS_STATUS = "SUCCESS";
4
+ /** 成功信封的固定 `message`(错误信封的 `message` 是业务文案,不固定) */
5
+ declare const SUCCESS_MESSAGE = "ok";
6
+ /**
7
+ * 响应信封(成功与错误同一形状,只有 `data` 与 `status` 的含义不同)。
8
+ *
9
+ * ## `status` 为什么是字面量 `'SUCCESS'` 而不是 `string`
10
+ *
11
+ * 类型必须**不宽于**运行时 schema,否则它就不是一个可用的类型:
12
+ * 框架的 `responseSchema()` 把成功响应声明为 `z.enum(['SUCCESS'])`(即 `'SUCCESS'`
13
+ * 字面量)。如果这里写 `status: string`,路由 handler 返回 `createSuccessResponse(x)`
14
+ * 就**不满足自己声明的响应 schema**——类型检查要么报错、要么被上游 `as` 掉。
15
+ *
16
+ * 各仓遇到这个矛盾时会自己再写一份「更窄的同名函数」(account 一直有一份),
17
+ * 结果同一个信封在四个仓里有四个实现,而差异只在一个类型宽度上。宽度对齐后
18
+ * 那份副本就没有存在理由了。
19
+ *
20
+ * 错误信封用 {@link createErrorResponse},它的 `status` 是错误码(`string`)。
21
+ * 两种信封共用一个接口,所以 `ResponseEnvelope.status` 的类型是
22
+ * `'SUCCESS' | string`——对它求值为 `string`,但 `createSuccessResponse` 的返回类型
23
+ * 单独收窄为 {@link SuccessResponse},路由 handler 拿到的就是字面量。
24
+ */
25
+ export interface ResponseEnvelope<T> {
26
+ status: string;
27
+ message: string;
28
+ data: T;
29
+ }
30
+ /** 成功信封:`status` / `message` 都是固定字面量(与 `responseSchema()` 一致) */
31
+ export interface SuccessResponse<T> {
32
+ status: typeof SUCCESS_STATUS;
33
+ message: typeof SUCCESS_MESSAGE;
34
+ data: T;
35
+ }
36
+ export declare function createSuccessResponse<T>(data: T): SuccessResponse<T>;
37
+ export declare function createErrorResponse(status: string, message: string): ResponseEnvelope<null>;
38
+ /**
39
+ * 资源错误分类器(单一来源:`types.ts`)。
40
+ *
41
+ * **每个资源注册自己的分类器**(一个资源一个,各管各的错误域);由服务仓在 `resources(ctx)` 里
42
+ * 通过 `ctx.registerClassifyError` 注册;未注册时错误落 `UNKNOWN_ERROR` 兜底。
43
+ */
44
+ export type { ErrorClassifier };
45
+ export interface ResponseCoreOptions {
46
+ /** 资源错误分类(各资源经 `ctx.registerClassifyError` 注册;缺省 = 不做资源错误分类) */
47
+ classifyResourceError?: ErrorClassifier;
48
+ }
49
+ export declare function registerResponse(app: FastifyInstance, options?: ResponseCoreOptions): void;
50
+ //# 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;AA2BrD,QAAA,MAAM,cAAc,YAAY,CAAA;AAShC,mDAAmD;AACnD,QAAA,MAAM,eAAe,OAAO,CAAA;AAE5B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAA;CACR;AAED,iEAAiE;AACjE,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,MAAM,EAAE,OAAO,cAAc,CAAA;IAC7B,OAAO,EAAE,OAAO,eAAe,CAAA;IAC/B,IAAI,EAAE,CAAC,CAAA;CACR;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAEpE;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"}