@taicode/common-server 3.8.0 → 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 (218) 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 -9
  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 +5 -4
  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/core/modules/cookie/index.js +17 -0
  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/core/modules/identity/index.js +14 -0
  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/core/modules/logging/index.js +29 -0
  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/core/modules/openapi/index.js +41 -0
  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/core/modules/request-id/index.d.ts +33 -0
  64. package/output/core/modules/request-id/index.d.ts.map +1 -0
  65. package/output/core/modules/request-id/index.js +66 -0
  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/core/modules/response/index.js +102 -0
  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.map +1 -0
  88. package/output/core/test-support/database.test.js +111 -0
  89. package/output/core/test-support/environment.d.ts +38 -0
  90. package/output/core/test-support/environment.d.ts.map +1 -0
  91. package/output/core/test-support/environment.js +45 -0
  92. package/output/core/test-support/service-context.d.ts +64 -0
  93. package/output/core/test-support/service-context.d.ts.map +1 -0
  94. package/output/core/test-support/service-context.js +63 -0
  95. package/output/core/test-support/service-context.test.d.ts +2 -0
  96. package/output/core/test-support/service-context.test.d.ts.map +1 -0
  97. package/output/core/test-support/service-context.test.js +55 -0
  98. package/output/core/test-support/test-server.d.ts +154 -0
  99. package/output/core/test-support/test-server.d.ts.map +1 -0
  100. package/output/core/test-support/test-server.js +114 -0
  101. package/output/core/test-support/test-server.test.d.ts +2 -0
  102. package/output/core/test-support/test-server.test.d.ts.map +1 -0
  103. package/output/core/test-support/test-server.test.js +296 -0
  104. package/output/core/test-support/testdata.d.ts +255 -0
  105. package/output/core/test-support/testdata.d.ts.map +1 -0
  106. package/output/core/test-support/testdata.js +271 -0
  107. package/output/core/test-support/testdata.test.d.ts +2 -0
  108. package/output/core/test-support/testdata.test.d.ts.map +1 -0
  109. package/output/core/test-support/testdata.test.js +334 -0
  110. package/output/core/type-augment.d.ts +34 -0
  111. package/output/core/type-augment.d.ts.map +1 -0
  112. package/output/core/type-augment.js +7 -0
  113. package/output/core/types.d.ts +582 -0
  114. package/output/core/types.d.ts.map +1 -0
  115. package/output/core/types.js +2 -0
  116. package/output/cors/index.d.ts +3 -0
  117. package/output/cors/index.d.ts.map +1 -0
  118. package/output/cors/index.js +28 -0
  119. package/output/index.d.ts +10 -4
  120. package/output/index.d.ts.map +1 -1
  121. package/output/index.js +25 -5
  122. package/output/rate-limit/index.d.ts +23 -0
  123. package/output/rate-limit/index.d.ts.map +1 -0
  124. package/output/rate-limit/index.js +43 -0
  125. package/output/{rate-limiter/rate-limiter.d.ts → rate-limit/limiter.d.ts} +1 -1
  126. package/output/rate-limit/limiter.d.ts.map +1 -0
  127. package/output/{rate-limiter → rate-limit}/memory-store.d.ts +6 -0
  128. package/output/rate-limit/memory-store.d.ts.map +1 -0
  129. package/output/{rate-limiter → rate-limit}/memory-store.js +8 -0
  130. package/output/{rate-limiter → rate-limit}/redis-store.d.ts +18 -0
  131. package/output/rate-limit/redis-store.d.ts.map +1 -0
  132. package/output/{rate-limiter → rate-limit}/redis-store.js +35 -0
  133. package/output/rate-limit/redis-store.test.d.ts.map +1 -0
  134. package/output/{rate-limiter → rate-limit}/redis-store.test.js +2 -2
  135. package/output/rate-limit/types.d.ts +45 -0
  136. package/output/rate-limit/types.d.ts.map +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/redis/index.d.ts +50 -0
  143. package/output/redis/index.d.ts.map +1 -0
  144. package/output/redis/index.js +71 -0
  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 +11 -7
  154. package/output/app/assemble.d.ts +0 -16
  155. package/output/app/assemble.d.ts.map +0 -1
  156. package/output/app/assemble.js +0 -124
  157. package/output/app/database.d.ts +0 -36
  158. package/output/app/database.d.ts.map +0 -1
  159. package/output/app/database.js +0 -40
  160. package/output/app/database.test.d.ts.map +0 -1
  161. package/output/app/database.test.js +0 -66
  162. package/output/app/default-modules.d.ts +0 -23
  163. package/output/app/default-modules.d.ts.map +0 -1
  164. package/output/app/default-modules.js +0 -40
  165. package/output/app/define-server.d.ts +0 -12
  166. package/output/app/define-server.d.ts.map +0 -1
  167. package/output/app/define-server.js +0 -289
  168. package/output/app/define-server.test.d.ts +0 -2
  169. package/output/app/define-server.test.d.ts.map +0 -1
  170. package/output/app/define-server.test.js +0 -299
  171. package/output/app/env.d.ts +0 -24
  172. package/output/app/env.d.ts.map +0 -1
  173. package/output/app/env.js +0 -38
  174. package/output/app/env.test.js +0 -78
  175. package/output/app/module.d.ts +0 -88
  176. package/output/app/module.d.ts.map +0 -1
  177. package/output/app/module.js +0 -34
  178. package/output/app/shutdown.d.ts +0 -9
  179. package/output/app/shutdown.d.ts.map +0 -1
  180. package/output/app/shutdown.js +0 -15
  181. package/output/app/shutdown.test.d.ts +0 -2
  182. package/output/app/shutdown.test.d.ts.map +0 -1
  183. package/output/app/shutdown.test.js +0 -149
  184. package/output/app/signal.d.ts +0 -12
  185. package/output/app/signal.d.ts.map +0 -1
  186. package/output/app/steps.d.ts +0 -17
  187. package/output/app/steps.d.ts.map +0 -1
  188. package/output/app/steps.js +0 -41
  189. package/output/app/types.d.ts +0 -149
  190. package/output/app/types.d.ts.map +0 -1
  191. package/output/context/index.d.ts +0 -6
  192. package/output/context/index.d.ts.map +0 -1
  193. package/output/context/index.js +0 -14
  194. package/output/rate-limiter/index.d.ts +0 -5
  195. package/output/rate-limiter/index.d.ts.map +0 -1
  196. package/output/rate-limiter/index.js +0 -4
  197. package/output/rate-limiter/memory-store.d.ts.map +0 -1
  198. package/output/rate-limiter/rate-limiter.d.ts.map +0 -1
  199. package/output/rate-limiter/redis-store.d.ts.map +0 -1
  200. package/output/rate-limiter/redis-store.test.d.ts.map +0 -1
  201. package/output/rate-limiter/types.d.ts +0 -27
  202. package/output/rate-limiter/types.d.ts.map +0 -1
  203. package/output/redis-cache/index.d.ts +0 -4
  204. package/output/redis-cache/index.d.ts.map +0 -1
  205. package/output/redis-cache/index.js +0 -1
  206. package/output/redis-cache/redis-cache.d.ts.map +0 -1
  207. package/output/redis-cache/redis-cache.test.d.ts +0 -2
  208. package/output/redis-cache/redis-cache.test.d.ts.map +0 -1
  209. package/output/schema/index.d.ts.map +0 -1
  210. /package/output/{app → core}/env.test.d.ts +0 -0
  211. /package/output/{app → core/modules/auth-provider}/types.js +0 -0
  212. /package/output/{context → core/modules/context}/index.test.d.ts +0 -0
  213. /package/output/{context → core/modules/context}/index.test.js +0 -0
  214. /package/output/{app → core/test-support}/database.test.d.ts +0 -0
  215. /package/output/{rate-limiter/rate-limiter.js → rate-limit/limiter.js} +0 -0
  216. /package/output/{rate-limiter → rate-limit}/redis-store.test.d.ts +0 -0
  217. /package/output/{rate-limiter → rate-limit}/types.js +0 -0
  218. /package/output/{redis-cache/redis-cache.js → redis/cache.js} +0 -0
@@ -1,289 +0,0 @@
1
- import { Disposer } from '@taicode/common-base';
2
- import Fastify, {} from 'fastify';
3
- import { assembleModules } from './assemble.js';
4
- import { attachSignalHandlers } from './signal.js';
5
- import { SHUTDOWN_ORDER } from './shutdown.js';
6
- import { SKIP_BY_MODE, STEP_ORDER } from './steps.js';
7
- const NOOP_LOGGER = {
8
- debug: () => undefined,
9
- info: () => undefined,
10
- warn: () => undefined,
11
- error: () => undefined,
12
- };
13
- function requireApp(state) {
14
- if (!state.app) {
15
- throw new Error('Fastify instance has not been created yet');
16
- }
17
- return state.app;
18
- }
19
- function resolveLog(options, state) {
20
- if (options.log) {
21
- return options.log;
22
- }
23
- if (state.app) {
24
- return state.app.log;
25
- }
26
- return NOOP_LOGGER;
27
- }
28
- function collectHooks(resolved) {
29
- const hooks = {};
30
- for (const name of resolved.order) {
31
- const module = resolved.byName[name];
32
- if (module?.hooks) {
33
- Object.assign(hooks, module.hooks);
34
- }
35
- }
36
- return hooks;
37
- }
38
- function collectPlugins(resolved, options) {
39
- const plugins = [];
40
- for (const name of resolved.order) {
41
- const module = resolved.byName[name];
42
- if (module?.plugins) {
43
- plugins.push(...module.plugins);
44
- }
45
- }
46
- if (options.extraPlugins) {
47
- plugins.push(...options.extraPlugins);
48
- }
49
- return plugins;
50
- }
51
- function buildStepImplementations(resolved, options, moduleHooks) {
52
- const implementations = {};
53
- // 1) 固定骨架步骤的内置实现(模块可覆盖既定槽位,但无法新增 / 重排步骤)
54
- implementations.createFastify = (ctx) => {
55
- ctx.state.app = Fastify({ logger: ctx.options.config.loadConfig().debug, trustProxy: true });
56
- };
57
- implementations.registerCorePlugins = async (ctx) => {
58
- const app = requireApp(ctx.state);
59
- for (const plugin of collectPlugins(resolved, ctx.options)) {
60
- await app.register(plugin);
61
- }
62
- };
63
- implementations.onClose = (ctx) => {
64
- const shutdownImplementations = buildShutdownImplementations(resolved, moduleHooks);
65
- requireApp(ctx.state).addHook('onClose', () => runShutdown(ctx, shutdownImplementations, moduleHooks));
66
- };
67
- implementations.ready = async (ctx) => {
68
- await requireApp(ctx.state).ready();
69
- };
70
- implementations.listen = async (ctx) => {
71
- const app = requireApp(ctx.state);
72
- const host = ctx.options.serverOptions?.host ?? '0.0.0.0';
73
- const port = ctx.options.serverOptions?.port ?? ctx.options.config.loadConfig().port;
74
- await app.listen({ port, host });
75
- if (ctx.options.signal !== false) {
76
- attachSignalHandlers(app, ctx.log);
77
- }
78
- };
79
- // 2) 数据层端口 → 第 3 步(database 模块的整体覆盖也在此生效)
80
- const database = resolved.database;
81
- if (database) {
82
- implementations['database.createClient'] = async (ctx) => {
83
- const loadPostgresConfig = ctx.options.config.loadPostgresConfig;
84
- const postgresConfig = loadPostgresConfig ? await loadPostgresConfig() : undefined;
85
- ctx.state.db = await database.createClient({ config: ctx.options.config, postgresConfig });
86
- };
87
- }
88
- // 3) 模块贡献的步骤实现(注册顺序 = MODULE_NAMES 顺序)
89
- for (const name of resolved.order) {
90
- const module = resolved.byName[name];
91
- if (module?.steps) {
92
- Object.assign(implementations, module.steps);
93
- }
94
- }
95
- // 4) 调用方槽位(调用方优先级最高)
96
- if (options.setupCompilers) {
97
- implementations.setupCompilers = (ctx) => {
98
- ctx.options.setupCompilers?.(requireApp(ctx.state));
99
- };
100
- }
101
- if (options.initGlobalConfig) {
102
- implementations.initGlobalConfig = (ctx) => {
103
- const globalConfigOptions = {
104
- redisUrl: ctx.options.config.resolveRedisUrl?.(),
105
- channelPrefix: ctx.options.config.resolveCachePrefix?.(),
106
- };
107
- const dispose = ctx.options.initGlobalConfig?.(ctx.state.db, globalConfigOptions);
108
- if (typeof dispose === 'function') {
109
- ctx.state.disposeConfig = dispose;
110
- }
111
- };
112
- }
113
- if (options.initClients) {
114
- implementations.initClients = async (ctx) => {
115
- const unsubscribe = await ctx.options.initClients?.({
116
- db: ctx.state.db,
117
- redis: ctx.state.redis?.client,
118
- globalConfig: ctx.state.globalConfig,
119
- });
120
- if (typeof unsubscribe === 'function') {
121
- ctx.state.unsubscribe = unsubscribe;
122
- }
123
- };
124
- }
125
- implementations.createServices = (ctx) => {
126
- ctx.state.services = ctx.options.createServices({
127
- db: ctx.state.db,
128
- redis: ctx.state.redis?.client,
129
- globalConfig: ctx.state.globalConfig,
130
- });
131
- };
132
- if (options.initServices || options.afterInit) {
133
- implementations.initServices = async (ctx) => {
134
- if (ctx.options.initServices) {
135
- await ctx.options.initServices({ services: ctx.state.services });
136
- }
137
- if (ctx.options.afterInit) {
138
- const disposer = await ctx.options.afterInit({
139
- db: ctx.state.db,
140
- redis: ctx.state.redis?.client,
141
- globalConfig: ctx.state.globalConfig,
142
- services: ctx.state.services,
143
- });
144
- if (typeof disposer === 'function') {
145
- ctx.state.disposer.addDisposer(disposer);
146
- }
147
- }
148
- };
149
- }
150
- if (options.backgroundWorkers) {
151
- implementations.backgroundWorkers = async (ctx) => {
152
- await ctx.options.backgroundWorkers?.({
153
- db: ctx.state.db,
154
- redis: ctx.state.redis?.client,
155
- globalConfig: ctx.state.globalConfig,
156
- services: ctx.state.services,
157
- disposer: ctx.state.disposer,
158
- });
159
- };
160
- }
161
- implementations.registerRouters = async (ctx) => {
162
- await ctx.options.registerRouters(requireApp(ctx.state), { services: ctx.state.services });
163
- };
164
- if (options.afterReady) {
165
- implementations.afterReady = async (ctx) => {
166
- await ctx.options.afterReady?.({ app: requireApp(ctx.state), services: ctx.state.services });
167
- };
168
- }
169
- return implementations;
170
- }
171
- async function disposeServiceMap(services) {
172
- if (typeof services !== 'object' || services === null) {
173
- return;
174
- }
175
- const record = services;
176
- const pending = Object.values(record).map((service) => {
177
- const candidate = service;
178
- return typeof candidate.dispose === 'function' ? candidate.dispose() : undefined;
179
- });
180
- await Promise.allSettled(pending);
181
- }
182
- function buildShutdownImplementations(resolved, moduleHooks) {
183
- const implementations = {
184
- stopProducers: async (ctx) => {
185
- await ctx.state.disposer.dispose();
186
- },
187
- unsubscribeConfig: async (ctx) => {
188
- await ctx.state.unsubscribe?.();
189
- },
190
- disposeConfig: async (ctx) => {
191
- await ctx.state.disposeConfig?.();
192
- },
193
- disposeServices: async (ctx) => {
194
- const disposeServices = ctx.options.disposeServices ?? moduleHooks.disposeServices;
195
- if (disposeServices) {
196
- await disposeServices({ services: ctx.state.services });
197
- }
198
- else {
199
- await disposeServiceMap(ctx.state.services);
200
- }
201
- },
202
- disposeClients: async (ctx) => {
203
- const disposeClients = ctx.options.disposeClients ?? moduleHooks.disposeClients;
204
- await disposeClients?.();
205
- },
206
- disposeRedis: async (ctx) => {
207
- await ctx.state.redis?.dispose();
208
- },
209
- disposeDb: async (ctx) => {
210
- await resolved.database?.dispose?.(ctx.state.db);
211
- },
212
- };
213
- for (const name of resolved.order) {
214
- const module = resolved.byName[name];
215
- if (module?.shutdown) {
216
- Object.assign(implementations, module.shutdown);
217
- }
218
- }
219
- return implementations;
220
- }
221
- async function runShutdown(ctx, implementations, moduleHooks) {
222
- const errors = [];
223
- const options = ctx.options;
224
- const shutdown = options.shutdown ?? moduleHooks.shutdown;
225
- const beforeShutdown = options.beforeShutdown ?? moduleHooks.beforeShutdown;
226
- if (shutdown) {
227
- try {
228
- await shutdown('onClose');
229
- }
230
- catch (error) {
231
- errors.push(error);
232
- }
233
- }
234
- else {
235
- if (beforeShutdown) {
236
- try {
237
- await beforeShutdown();
238
- }
239
- catch (error) {
240
- errors.push(error);
241
- }
242
- }
243
- for (const step of SHUTDOWN_ORDER) {
244
- const implementation = implementations[step];
245
- if (!implementation) {
246
- continue;
247
- }
248
- try {
249
- await implementation(ctx);
250
- }
251
- catch (error) {
252
- errors.push(error);
253
- }
254
- }
255
- }
256
- if (errors.length > 0) {
257
- ctx.log.error({ errors, count: errors.length }, '[shutdown] completed with errors');
258
- }
259
- }
260
- /**
261
- * 模块化装配 + 15 步固定编排(design §3 / §4 / §5;提案 §11)。
262
- *
263
- * 返回**原生 `FastifyInstance`**(不包 `fastify-plugin`、不返回包装对象)——调用方可直接
264
- * `app.swagger()` / `app.inject()` / `app.decorate()`。`mode: 'openapi-build'` 时不 listen,
265
- * 由调用方读 `app.swagger()`。
266
- *
267
- * `Db` 由 `modules.database`(`database` 模块的 `createClient` 返回类型)推断(决策 10-2)。
268
- */
269
- export async function defineServer(options) {
270
- const internal = options;
271
- const mode = internal.mode ?? 'production';
272
- const resolved = assembleModules(internal.modules);
273
- const state = { disposer: new Disposer() };
274
- const moduleHooks = collectHooks(resolved);
275
- const implementations = buildStepImplementations(resolved, internal, moduleHooks);
276
- const skipped = new Set(SKIP_BY_MODE[mode]);
277
- for (const step of STEP_ORDER) {
278
- if (skipped.has(step)) {
279
- continue;
280
- }
281
- const ctx = { options: internal, state, log: resolveLog(internal, state) };
282
- ctx.log.debug({ step }, '[boot]');
283
- const implementation = implementations[step];
284
- if (implementation) {
285
- await implementation(ctx);
286
- }
287
- }
288
- return requireApp(state);
289
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=define-server.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"define-server.test.d.ts","sourceRoot":"","sources":["../../source/app/define-server.test.ts"],"names":[],"mappings":""}
@@ -1,299 +0,0 @@
1
- import { describe, expect, expectTypeOf, it } from 'vitest';
2
- import { createPrismaDatabaseModule } from './database.js';
3
- import { defineServer } from './define-server.js';
4
- import { STEP_ORDER } from './steps.js';
5
- const P1_OPT_OUT = {
6
- context: false,
7
- response: false,
8
- cors: false,
9
- schema: false,
10
- 'rate-limit': false,
11
- auth: false,
12
- openapi: false,
13
- database: false,
14
- };
15
- const DUMMY_ERRORS = {
16
- UserError: class extends Error {
17
- },
18
- SystemError: class extends Error {
19
- },
20
- };
21
- const TEST_CONFIG = { loadConfig: () => ({ debug: false, port: 0 }) };
22
- const FAKE_PRISMA_NAMESPACE = {
23
- PrismaClientKnownRequestError: class extends Error {
24
- },
25
- PrismaClientUnknownRequestError: class extends Error {
26
- },
27
- PrismaClientValidationError: class extends Error {
28
- },
29
- PrismaClientRustPanicError: class extends Error {
30
- },
31
- PrismaClientInitializationError: class extends Error {
32
- },
33
- };
34
- class FakeClient {
35
- args;
36
- disconnected = false;
37
- constructor(args = []) {
38
- this.args = args;
39
- }
40
- async $disconnect() {
41
- this.disconnected = true;
42
- }
43
- }
44
- function createTestLog() {
45
- const steps = [];
46
- const errors = [];
47
- const log = {
48
- debug: (obj) => {
49
- const step = obj.step;
50
- if (typeof step === 'string') {
51
- steps.push(step);
52
- }
53
- },
54
- info: () => undefined,
55
- warn: () => undefined,
56
- error: (obj) => {
57
- errors.push(obj);
58
- },
59
- };
60
- return { log, steps, errors };
61
- }
62
- function without(list, drop) {
63
- return list.filter((step) => !drop.includes(step));
64
- }
65
- function createBaseOptions(overrides = {}) {
66
- return {
67
- serviceName: 'test',
68
- title: 'Test',
69
- description: 'Test service',
70
- errors: DUMMY_ERRORS,
71
- config: TEST_CONFIG,
72
- modules: { ...P1_OPT_OUT },
73
- serverOptions: { port: 0, host: '127.0.0.1' },
74
- signal: false,
75
- createServices: () => ({ dispose: () => undefined }),
76
- registerRouters: () => undefined,
77
- ...overrides,
78
- };
79
- }
80
- describe('defineServer 装配期校验', () => {
81
- it('未知模块名报错(文案逐字对齐 design §3.2)', async () => {
82
- const modules = { ...P1_OPT_OUT, foo: false };
83
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Unknown module "foo". Allowed: env, database, redis, context, response, cors, schema, rate-limit, auth, openapi');
84
- });
85
- it('重复注册报错', async () => {
86
- const modules = { ...P1_OPT_OUT, cors: { name: 'cors' }, redis: { name: 'cors' } };
87
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Module "cors" registered twice');
88
- });
89
- it('键名不符报错', async () => {
90
- const modules = { ...P1_OPT_OUT, redis: { name: 'mysql' } };
91
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Module key "redis" != module.name "mysql"');
92
- });
93
- it('dependsOn 指向被跳过的模块报错', async () => {
94
- const modules = {
95
- ...P1_OPT_OUT,
96
- database: false,
97
- redis: { name: 'redis', dependsOn: ['database'] },
98
- };
99
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Module "redis" depends on "database" which is disabled');
100
- });
101
- it('P1 未实现模块未显式 false 报错', async () => {
102
- const modules = {
103
- context: false,
104
- cors: false,
105
- schema: false,
106
- 'rate-limit': false,
107
- auth: false,
108
- openapi: false,
109
- database: false,
110
- };
111
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Module "response" is not implemented until P3; pass false to opt out');
112
- });
113
- it('过渡期报错按真实阶段号:openapi = P2、auth = P4', async () => {
114
- const modulesWithoutOpenapi = {
115
- context: false,
116
- response: false,
117
- cors: false,
118
- schema: false,
119
- 'rate-limit': false,
120
- auth: false,
121
- database: false,
122
- };
123
- await expect(defineServer(createBaseOptions({ modules: modulesWithoutOpenapi }))).rejects.toThrow('Module "openapi" is not implemented until P2; pass false to opt out');
124
- const modulesWithoutAuth = {
125
- context: false,
126
- response: false,
127
- cors: false,
128
- schema: false,
129
- 'rate-limit': false,
130
- openapi: false,
131
- database: false,
132
- };
133
- await expect(defineServer(createBaseOptions({ modules: modulesWithoutAuth }))).rejects.toThrow('Module "auth" is not implemented until P4; pass false to opt out');
134
- });
135
- it('未显式提供 database 报错(提示需 createPrismaDatabaseModule({...}) 或 false)', async () => {
136
- const modules = {
137
- context: false,
138
- response: false,
139
- cors: false,
140
- schema: false,
141
- 'rate-limit': false,
142
- auth: false,
143
- openapi: false,
144
- };
145
- await expect(defineServer(createBaseOptions({ modules }))).rejects.toThrow('Module "database" requires createPrismaDatabaseModule({...}) or false');
146
- });
147
- it('装配期报错发生在 promise resolve 之前(不拖到 listen)', async () => {
148
- const promise = defineServer(createBaseOptions({ modules: { ...P1_OPT_OUT, database: false, redis: { name: 'redis', dependsOn: ['database'] } } }));
149
- await expect(promise).rejects.toThrow();
150
- });
151
- });
152
- describe('defineServer 15 步骨架', () => {
153
- it('production 模式执行完整 15 步且顺序固定', async () => {
154
- const { log, steps } = createTestLog();
155
- const app = await defineServer(createBaseOptions({ log, mode: 'production' }));
156
- try {
157
- expect(steps).toEqual([...STEP_ORDER]);
158
- }
159
- finally {
160
- await app.close();
161
- }
162
- });
163
- it('test 模式跳过第 10 / 14 步', async () => {
164
- const { log, steps } = createTestLog();
165
- const app = await defineServer(createBaseOptions({ log, mode: 'test' }));
166
- try {
167
- expect(steps).toEqual(without(STEP_ORDER, ['backgroundWorkers', 'afterReady']));
168
- }
169
- finally {
170
- await app.close();
171
- }
172
- });
173
- it('openapi-build 模式跳过 [5,6,9,10,14,15],但保留第 3 / 4 步(决策 10-3)', async () => {
174
- const { log, steps } = createTestLog();
175
- const app = await defineServer(createBaseOptions({ log, mode: 'openapi-build' }));
176
- expect(steps).toEqual(without(STEP_ORDER, ['initGlobalConfig', 'initClients', 'initServices', 'backgroundWorkers', 'afterReady', 'listen']));
177
- expect(steps).toContain('database.createClient');
178
- expect(steps).toContain('createRedis');
179
- await app.close();
180
- });
181
- });
182
- describe('defineServer 跳过语义', () => {
183
- it('test 模式下 backgroundWorkers / afterReady 确实未执行', async () => {
184
- let workers = 0;
185
- let ready = 0;
186
- const app = await defineServer(createBaseOptions({
187
- mode: 'test',
188
- backgroundWorkers: async () => {
189
- workers += 1;
190
- },
191
- afterReady: async () => {
192
- ready += 1;
193
- },
194
- }));
195
- await app.close();
196
- expect(workers).toBe(0);
197
- expect(ready).toBe(0);
198
- });
199
- it('openapi-build 模式下连库/连 Redis 与集合类初始化确实未执行', async () => {
200
- const calls = { initGlobalConfig: 0, initClients: 0, initServices: 0, workers: 0, ready: 0, routers: 0 };
201
- const app = await defineServer(createBaseOptions({
202
- mode: 'openapi-build',
203
- initGlobalConfig: () => {
204
- calls.initGlobalConfig += 1;
205
- },
206
- initClients: async () => {
207
- calls.initClients += 1;
208
- },
209
- initServices: async () => {
210
- calls.initServices += 1;
211
- },
212
- backgroundWorkers: async () => {
213
- calls.workers += 1;
214
- },
215
- afterReady: async () => {
216
- calls.ready += 1;
217
- },
218
- registerRouters: () => {
219
- calls.routers += 1;
220
- },
221
- }));
222
- await app.close();
223
- expect(calls).toEqual({ initGlobalConfig: 0, initClients: 0, initServices: 0, workers: 0, ready: 0, routers: 1 });
224
- });
225
- it('production 模式下后台 worker / afterReady 都会执行', async () => {
226
- let workers = 0;
227
- let ready = 0;
228
- const app = await defineServer(createBaseOptions({
229
- mode: 'production',
230
- backgroundWorkers: async () => {
231
- workers += 1;
232
- },
233
- afterReady: async () => {
234
- ready += 1;
235
- },
236
- }));
237
- await app.close();
238
- expect(workers).toBe(1);
239
- expect(ready).toBe(1);
240
- });
241
- it('database: false 时第 3 步空转,createServices 收到 undefined db', async () => {
242
- let received = 'sentinel';
243
- const app = await defineServer(createBaseOptions({
244
- modules: { ...P1_OPT_OUT, database: false },
245
- createServices: (ctx) => {
246
- received = ctx.db;
247
- return {};
248
- },
249
- }));
250
- await app.close();
251
- expect(received).toBeUndefined();
252
- });
253
- });
254
- describe('defineServer 模块覆盖与 Db 推断', () => {
255
- it('Db 泛型从 database 模块推断(决策 10-2,无需强转)', async () => {
256
- const database = createPrismaDatabaseModule({
257
- PrismaClient: FakeClient,
258
- PrismaNamespace: FAKE_PRISMA_NAMESPACE,
259
- });
260
- const app = await defineServer({
261
- serviceName: 'test',
262
- title: 'Test',
263
- description: 'Test service',
264
- errors: DUMMY_ERRORS,
265
- config: TEST_CONFIG,
266
- modules: { ...P1_OPT_OUT, database },
267
- serverOptions: { port: 0, host: '127.0.0.1' },
268
- signal: false,
269
- createServices: (ctx) => {
270
- expectTypeOf(ctx.db).toEqualTypeOf();
271
- return {};
272
- },
273
- registerRouters: () => undefined,
274
- });
275
- await app.close();
276
- });
277
- it('整体覆盖 database 模块时使用覆盖实现的 createClient', async () => {
278
- let created = 0;
279
- const database = {
280
- name: 'database',
281
- createClient: () => {
282
- created += 1;
283
- return { tag: 'custom' };
284
- },
285
- classifyError: () => null,
286
- };
287
- let received;
288
- const app = await defineServer(createBaseOptions({
289
- modules: { ...P1_OPT_OUT, database: database },
290
- createServices: (ctx) => {
291
- received = ctx.db;
292
- return {};
293
- },
294
- }));
295
- await app.close();
296
- expect(created).toBe(1);
297
- expect(received).toEqual({ tag: 'custom' });
298
- });
299
- });
@@ -1,24 +0,0 @@
1
- /**
2
- * 默认 env 文件分层(低 → 高优先级,后者覆盖前者):
3
- *
4
- * .env.dev 已提交进仓库的 dev 默认值(克隆后 `pnpm run dev` 开箱即用)
5
- * .env 本机私有覆盖(已被 gitignore)
6
- * .env.local 本机更高优先级覆盖(已被 gitignore)
7
- *
8
- * 真实环境变量(进程/容器注入)优先级最高,任何 .env 文件都不会覆盖它们 —— 这条保证让 CI、
9
- * helm、`docker run -e` 等显式注入始终生效,也避免测试探针
10
- * (`check-test-database-isolation`)注入的连接串被仓库里的默认值顶掉。
11
- *
12
- * 解析器与 account / expense / notice / release 四家现状保持一致,统一使用 `dotenv`,
13
- * 以保证多行值、引号/转义、`export` 前缀等细节语义完全等价(design §6:与现状逐字一致)。
14
- */
15
- export declare const ENV_FILE_LAYERS: readonly [".env.dev", ".env", ".env.local"];
16
- /**
17
- * 按分层顺序加载 env 文件(design 决策 #5:独立导出,不塞进 `defineServer`)。
18
- *
19
- * 必须在任何 `config` 读取之前调用;测试 / 脚本同样复用本函数。
20
- *
21
- * @param extraLayers 追加在最高优先级的额外层,例如测试配置传 `['.env.test.local']`
22
- */
23
- export declare function loadEnvFiles(extraLayers?: readonly string[]): void;
24
- //# sourceMappingURL=env.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../source/app/env.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,eAAe,6CAA8C,CAAA;AAU1E;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,SAAS,MAAM,EAAO,GAAG,IAAI,CAUtE"}
package/output/app/env.js DELETED
@@ -1,38 +0,0 @@
1
- import { config } from 'dotenv';
2
- /**
3
- * 默认 env 文件分层(低 → 高优先级,后者覆盖前者):
4
- *
5
- * .env.dev 已提交进仓库的 dev 默认值(克隆后 `pnpm run dev` 开箱即用)
6
- * .env 本机私有覆盖(已被 gitignore)
7
- * .env.local 本机更高优先级覆盖(已被 gitignore)
8
- *
9
- * 真实环境变量(进程/容器注入)优先级最高,任何 .env 文件都不会覆盖它们 —— 这条保证让 CI、
10
- * helm、`docker run -e` 等显式注入始终生效,也避免测试探针
11
- * (`check-test-database-isolation`)注入的连接串被仓库里的默认值顶掉。
12
- *
13
- * 解析器与 account / expense / notice / release 四家现状保持一致,统一使用 `dotenv`,
14
- * 以保证多行值、引号/转义、`export` 前缀等细节语义完全等价(design §6:与现状逐字一致)。
15
- */
16
- export const ENV_FILE_LAYERS = ['.env.dev', '.env', '.env.local'];
17
- /**
18
- * 首次 import 时的真实环境变量快照(此时还没有任何 .env 文件被加载)。
19
- * 加载结束后原样恢复,实现「外部注入 > .env.local > .env > .env.dev」。
20
- */
21
- const EXTERNAL_ENV = new Map(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
22
- /**
23
- * 按分层顺序加载 env 文件(design 决策 #5:独立导出,不塞进 `defineServer`)。
24
- *
25
- * 必须在任何 `config` 读取之前调用;测试 / 脚本同样复用本函数。
26
- *
27
- * @param extraLayers 追加在最高优先级的额外层,例如测试配置传 `['.env.test.local']`
28
- */
29
- export function loadEnvFiles(extraLayers = []) {
30
- const layers = [...ENV_FILE_LAYERS, ...extraLayers];
31
- layers.forEach((path, index) => {
32
- // 第一层不覆盖已有值,后续层逐层覆盖前一层
33
- config({ path, override: index > 0 });
34
- });
35
- for (const [key, value] of EXTERNAL_ENV) {
36
- process.env[key] = value;
37
- }
38
- }