@zintrust/core 0.4.12 → 0.4.14

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 (34) hide show
  1. package/package.json +1 -1
  2. package/src/boot/registry/registerRoute.d.ts.map +1 -1
  3. package/src/boot/registry/registerRoute.js +59 -7
  4. package/src/cli/commands/StartCommand.d.ts.map +1 -1
  5. package/src/cli/commands/StartCommand.js +119 -9
  6. package/src/cli/scaffolding/ServiceScaffolder.d.ts.map +1 -1
  7. package/src/cli/scaffolding/ServiceScaffolder.js +4 -1
  8. package/src/cli/utils/EnvFileLoader.d.ts +2 -0
  9. package/src/cli/utils/EnvFileLoader.d.ts.map +1 -1
  10. package/src/cli/utils/EnvFileLoader.js +85 -24
  11. package/src/config/env.d.ts.map +1 -1
  12. package/src/config/env.js +13 -0
  13. package/src/functions/cloudflare.d.ts.map +1 -1
  14. package/src/functions/cloudflare.js +21 -1
  15. package/src/index.js +3 -3
  16. package/src/microservices/ServiceManifest.d.ts +1 -0
  17. package/src/microservices/ServiceManifest.d.ts.map +1 -1
  18. package/src/microservices/ServiceManifest.js +5 -0
  19. package/src/orm/Database.d.ts.map +1 -1
  20. package/src/orm/Database.js +5 -2
  21. package/src/orm/DatabaseConnectionRegistry.d.ts +8 -0
  22. package/src/orm/DatabaseConnectionRegistry.d.ts.map +1 -0
  23. package/src/orm/DatabaseConnectionRegistry.js +13 -0
  24. package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
  25. package/src/orm/DatabaseRuntimeRegistration.js +4 -2
  26. package/src/runtime/ProjectBootstrap.d.ts +6 -0
  27. package/src/runtime/ProjectBootstrap.d.ts.map +1 -0
  28. package/src/runtime/ProjectBootstrap.js +43 -0
  29. package/src/runtime/ProjectRuntime.d.ts.map +1 -1
  30. package/src/runtime/ProjectRuntime.js +5 -2
  31. package/src/runtime/adapters/CloudflareAdapter.d.ts.map +1 -1
  32. package/src/runtime/adapters/CloudflareAdapter.js +8 -2
  33. package/src/start.d.ts.map +1 -1
  34. package/src/start.js +65 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"registerRoute.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/registerRoute.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAW3D,eAAO,MAAM,kBAAkB,QAAO,OAKrC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,CAAC,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAMpF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,CAAC,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAOrF,CAAC;AA4FF,eAAO,MAAM,oBAAoB,GAC/B,kBAAkB,MAAM,EACxB,QAAQ,OAAO,KACd,OAAO,CAAC,IAAI,CAoBd,CAAC"}
1
+ {"version":3,"file":"registerRoute.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/registerRoute.ts"],"names":[],"mappings":"AAEA,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAc3D,eAAO,MAAM,kBAAkB,QAAO,OAKrC,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAU,CAAC,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAMpF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAU,CAAC,EAAE,YAAY,MAAM,KAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAOrF,CAAC;AAiKF,eAAO,MAAM,oBAAoB,GAC/B,kBAAkB,MAAM,EACxB,QAAQ,OAAO,KACd,OAAO,CAAC,IAAI,CAqBd,CAAC"}
@@ -1,8 +1,7 @@
1
1
  import { appConfig } from '../../config/index.js';
2
2
  import Logger from '../../config/logger.js';
3
3
  import { Router } from '../../routes/Router.js';
4
- import { isObject } from '../../helper/index.js';
5
- import { getServicePrefix } from '../../microservices/ServiceManifest.js';
4
+ import { isNonEmptyString, isObject } from '../../helper/index.js';
6
5
  import * as path from '../../node-singletons/path.js';
7
6
  import { pathToFileURL } from '../../node-singletons/url.js';
8
7
  import { detectRuntime } from '../../runtime/detectRuntime.js';
@@ -63,23 +62,75 @@ const registerAppRoutes = async (resolvedBasePath, router) => {
63
62
  mod.registerRoutes(router);
64
63
  }
65
64
  };
65
+ const getProjectRoot = () => {
66
+ const fromEnv = process.env?.['ZINTRUST_PROJECT_ROOT'] ?? '';
67
+ if (fromEnv.trim() !== '')
68
+ return fromEnv.trim();
69
+ return process.cwd();
70
+ };
71
+ const resolveManifestServiceEnvDir = (projectRoot, entry) => {
72
+ const configRoot = entry.configRoot;
73
+ if (isNonEmptyString(configRoot)) {
74
+ return path.dirname(path.join(projectRoot, configRoot));
75
+ }
76
+ return path.join(projectRoot, 'src', 'services', entry.domain, entry.name);
77
+ };
78
+ const resolveServicePrefix = (entry) => {
79
+ const prefix = entry.prefix;
80
+ if (isNonEmptyString(prefix)) {
81
+ const segments = prefix
82
+ .split('/')
83
+ .map((segment) => segment.trim())
84
+ .filter((segment) => segment !== '');
85
+ return segments.length === 0 ? '/' : `/${segments.join('/')}`;
86
+ }
87
+ return `/${entry.domain}/${entry.name}`;
88
+ };
89
+ const ensureManifestServiceEnvLoaded = async (entry) => {
90
+ if (isCloudflare)
91
+ return;
92
+ const { EnvFileLoader } = await import('../../cli/utils/EnvFileLoader.js');
93
+ const projectRoot = getProjectRoot();
94
+ const envPath = resolveManifestServiceEnvDir(projectRoot, entry);
95
+ EnvFileLoader.ensureLoaded({
96
+ cwd: projectRoot,
97
+ includeCwd: true,
98
+ envPaths: [envPath],
99
+ });
100
+ };
101
+ const registerLoadedRoutes = (router, entry, registerRoutes, activeService) => {
102
+ const servicePrefix = resolveServicePrefix(entry);
103
+ if (activeService?.id === entry.id) {
104
+ registerRoutes(router);
105
+ return;
106
+ }
107
+ Router.group(router, servicePrefix, (scopedRouter) => {
108
+ registerRoutes(scopedRouter);
109
+ });
110
+ };
66
111
  const registerManifestRoutes = async (router) => {
67
112
  await ProjectRuntime.tryLoadNodeRuntime();
68
113
  const serviceManifest = ProjectRuntime.getServiceManifest();
69
114
  if (serviceManifest.length === 0)
70
115
  return;
116
+ const activeService = ProjectRuntime.getActiveService();
117
+ if (activeService !== undefined && isCloudflare)
118
+ return;
71
119
  for (const entry of serviceManifest) {
72
120
  if (entry.monolithEnabled === false || typeof entry.loadRoutes !== 'function') {
73
121
  continue;
74
122
  }
123
+ if (activeService !== undefined && activeService.id !== entry.id) {
124
+ continue;
125
+ }
75
126
  try {
127
+ // eslint-disable-next-line no-await-in-loop
128
+ await ensureManifestServiceEnvLoaded(entry);
76
129
  // eslint-disable-next-line no-await-in-loop
77
130
  const mod = await entry.loadRoutes();
78
131
  const registerRoutes = isObject(mod) ? mod.registerRoutes : undefined;
79
132
  if (typeof registerRoutes === 'function') {
80
- Router.group(router, getServicePrefix(entry), (scopedRouter) => {
81
- registerRoutes(scopedRouter);
82
- });
133
+ registerLoadedRoutes(router, entry, registerRoutes, activeService);
83
134
  }
84
135
  }
85
136
  catch (error) {
@@ -104,14 +155,15 @@ const registerGlobalRoutes = (router) => {
104
155
  };
105
156
  export const registerMasterRoutes = async (resolvedBasePath, router) => {
106
157
  try {
158
+ const activeService = ProjectRuntime.getActiveService();
107
159
  if (isCloudflare) {
108
160
  registerGlobalRoutes(router);
109
161
  }
110
- if (!isCloudflare) {
162
+ if (!isCloudflare && activeService === undefined) {
111
163
  await registerAppRoutes(resolvedBasePath, router);
112
164
  }
113
165
  await registerManifestRoutes(router);
114
- if (router.routes.length === 0) {
166
+ if (router.routes.length === 0 && activeService === undefined) {
115
167
  await registerFrameworkRoutes(resolvedBasePath, router);
116
168
  }
117
169
  // Always register core framework routes (health, metrics, doc) after app routes
@@ -1 +1 @@
1
- {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgtBvF,eAAO,MAAM,YAAY;cACb,YAAY;EA6BtB,CAAC"}
1
+ {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA21BvF,eAAO,MAAM,YAAY;cACb,YAAY;EAmCtB,CAAC"}
@@ -2,11 +2,16 @@ import { BaseCommand } from '../BaseCommand.js';
2
2
  import { createDenoRunnerSource, createLambdaRunnerSource } from '../commands/runner/index.js';
3
3
  import { EnvFileLoader } from '../utils/EnvFileLoader.js';
4
4
  import { SpawnUtil } from '../utils/spawn.js';
5
+ import { generateUuid } from '../../common/utility.js';
5
6
  import { readEnvString } from '../../common/ExternalServiceUtils.js';
6
7
  import * as Common from '../../common/index.js';
7
8
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
8
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
9
+ import { isNonEmptyString } from '../../helper/index.js';
10
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from '../../node-singletons/fs.js';
9
11
  import * as path from '../../node-singletons/path.js';
12
+ import { ProjectRuntime } from '../../runtime/ProjectRuntime.js';
13
+ const isWranglerVarName = (value) => /^[A-Za-z_]\w*$/.test(value);
14
+ const isAbsolutePath = (value) => value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
10
15
  const resolveNpmPath = () => {
11
16
  try {
12
17
  return typeof Common.resolveNpmPath === 'function' ? Common.resolveNpmPath() : 'npm';
@@ -168,6 +173,26 @@ const resolveCacheEnabledPreference = (options) => {
168
173
  return options.cache;
169
174
  return undefined;
170
175
  };
176
+ const resolveRootEnvPreference = (options) => {
177
+ const hasRootEnv = hasFlag('--root-env');
178
+ const hasNoRootEnv = hasFlag('--no-root-env');
179
+ if (hasRootEnv && hasNoRootEnv) {
180
+ throw ErrorFactory.createCliError('Error: Cannot use both --root-env and --no-root-env.');
181
+ }
182
+ if (hasRootEnv)
183
+ return true;
184
+ if (hasNoRootEnv)
185
+ return false;
186
+ if (typeof options.rootEnv === 'boolean')
187
+ return options.rootEnv;
188
+ return true;
189
+ };
190
+ const resolveEnvPath = (options, projectRoot) => {
191
+ const raw = typeof options.envPath === 'string' ? options.envPath.trim() : '';
192
+ if (raw === '')
193
+ return undefined;
194
+ return isAbsolutePath(raw) ? raw : path.join(projectRoot, raw);
195
+ };
171
196
  const findNearestPackageJsonDir = (cwd) => {
172
197
  let current = path.resolve(cwd);
173
198
  while (true) {
@@ -210,9 +235,87 @@ const buildStartEnv = (projectRoot) => ({
210
235
  ...process.env,
211
236
  ZINTRUST_PROJECT_ROOT: projectRoot,
212
237
  });
213
- const ensureStartEnvLoaded = (context) => {
214
- const extraCwds = context.cwd === context.projectRoot ? [] : [context.cwd];
215
- EnvFileLoader.ensureLoaded({ cwd: context.projectRoot, extraCwds });
238
+ const buildWorkerDevVarsContent = () => {
239
+ return (Object.entries(process.env)
240
+ .filter((entry) => {
241
+ const [key, value] = entry;
242
+ return isWranglerVarName(key) && typeof value === 'string';
243
+ })
244
+ .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
245
+ .join('\n') + '\n');
246
+ };
247
+ async function withWranglerEnvSnapshot(cwd, envName, fn) {
248
+ const normalizedEnv = typeof envName === 'string' ? envName.trim() : '';
249
+ const targetName = normalizedEnv === '' ? '.dev.vars' : `.dev.vars.${normalizedEnv}`;
250
+ const targetPath = path.join(cwd, targetName);
251
+ const backupPath = existsSync(targetPath)
252
+ ? `${targetPath}.disabled-by-zin-${generateUuid()}`
253
+ : undefined;
254
+ if (backupPath !== undefined) {
255
+ renameSync(targetPath, backupPath);
256
+ }
257
+ try {
258
+ writeFileSync(targetPath, buildWorkerDevVarsContent(), 'utf-8');
259
+ return await fn();
260
+ }
261
+ finally {
262
+ try {
263
+ if (existsSync(targetPath))
264
+ unlinkSync(targetPath);
265
+ }
266
+ catch {
267
+ // noop
268
+ }
269
+ if (backupPath !== undefined) {
270
+ try {
271
+ if (existsSync(backupPath))
272
+ renameSync(backupPath, targetPath);
273
+ }
274
+ catch {
275
+ // noop
276
+ }
277
+ }
278
+ }
279
+ }
280
+ const resolveManifestServiceEnvDir = (projectRoot, entry) => {
281
+ const configRoot = entry.configRoot;
282
+ if (isNonEmptyString(configRoot)) {
283
+ return path.dirname(path.join(projectRoot, configRoot));
284
+ }
285
+ return path.join(projectRoot, 'src', 'services', entry.domain, entry.name);
286
+ };
287
+ const ensureStartEnvLoaded = (context, options) => {
288
+ const envPath = resolveEnvPath(options, context.projectRoot);
289
+ const rootEnv = resolveRootEnvPreference(options);
290
+ const extraCwds = envPath === undefined && context.cwd !== context.projectRoot ? [context.cwd] : [];
291
+ EnvFileLoader.ensureLoaded({
292
+ cwd: context.projectRoot,
293
+ includeCwd: rootEnv,
294
+ extraCwds,
295
+ ...(envPath === undefined ? {} : { envPaths: [envPath] }),
296
+ });
297
+ };
298
+ const preloadManifestServiceEnv = async (context, options) => {
299
+ if (context.cwd !== context.projectRoot)
300
+ return;
301
+ if (resolveEnvPath(options, context.projectRoot) !== undefined)
302
+ return;
303
+ process.env['ZINTRUST_PROJECT_ROOT'] = context.projectRoot;
304
+ ProjectRuntime.clear();
305
+ await ProjectRuntime.tryLoadNodeRuntime();
306
+ const manifest = ProjectRuntime.getServiceManifest().filter((entry) => entry.monolithEnabled !== false);
307
+ if (manifest.length === 0)
308
+ return;
309
+ const envPaths = manifest
310
+ .map((entry) => resolveManifestServiceEnvDir(context.projectRoot, entry))
311
+ .filter((value, index, items) => items.indexOf(value) === index);
312
+ if (envPaths.length === 0)
313
+ return;
314
+ EnvFileLoader.ensureLoaded({
315
+ cwd: context.projectRoot,
316
+ includeCwd: resolveRootEnvPreference(options),
317
+ envPaths,
318
+ });
216
319
  };
217
320
  const isFrameworkRepo = (packageJson) => packageJson.name === '@zintrust/core';
218
321
  const hasDevScript = (packageJson) => {
@@ -330,10 +433,12 @@ const executeWranglerStart = async (cmd, context, port, runtime, envName, wrangl
330
433
  }
331
434
  logMySqlProxyHint(cmd);
332
435
  cmd.info('Starting in Wrangler dev mode...');
333
- const exitCode = await SpawnUtil.spawnAndWait({
334
- command: 'wrangler',
335
- args: wranglerArgs,
336
- env: buildStartEnv(context.projectRoot),
436
+ const exitCode = await withWranglerEnvSnapshot(context.cwd, envName, async () => {
437
+ return SpawnUtil.spawnAndWait({
438
+ command: 'wrangler',
439
+ args: wranglerArgs,
440
+ env: buildStartEnv(context.projectRoot),
441
+ });
337
442
  });
338
443
  process.exit(exitCode);
339
444
  };
@@ -483,7 +588,9 @@ const executeSplitStart = async (cmd, context, _options) => {
483
588
  const executeStart = async (options, cmd) => {
484
589
  const cwd = process.cwd();
485
590
  const context = resolveStartContext(cwd);
486
- ensureStartEnvLoaded(context);
591
+ process.env['ZINTRUST_PROJECT_ROOT'] = context.projectRoot;
592
+ ensureStartEnvLoaded(context, options);
593
+ await preloadManifestServiceEnv(context, options);
487
594
  const mode = resolveMode(options);
488
595
  const port = resolvePort(options);
489
596
  const runtime = resolveRuntime(options);
@@ -541,8 +648,11 @@ export const StartCommand = Object.freeze({
541
648
  .option('--no-cache', 'Disable cache functionality')
542
649
  .option('--watch', 'Force watch mode (Node only)')
543
650
  .option('--no-watch', 'Disable watch mode (Node only)')
651
+ .option('--root-env', 'Load root project .env files for standalone service start')
652
+ .option('--no-root-env', 'Skip root project .env files for standalone service start')
544
653
  .option('--mode <development|production|testing>', 'Override app mode')
545
654
  .option('--env <name>', 'Wrangler environment name (Wrangler mode only)')
655
+ .option('--env-path <path>', 'Explicit env directory or .env file path for standalone service start')
546
656
  .option('--wrangler-config <path>', 'Wrangler config path (Wrangler mode only)')
547
657
  .option('--runtime <nodejs|cloudflare|lambda|deno|auto>', 'Set RUNTIME for spawned Node')
548
658
  .option('-p, --port <number>', 'Override server port');
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ServiceScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD;;GAEG;AAEH;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB7F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAGnF;AAED;;GAEG;AAEH,wBAAgB,QAAQ,CACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAiDhC;AAgcD,eAAO,MAAM,iBAAiB;;;;EAI5B,CAAC"}
1
+ {"version":3,"file":"ServiceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ServiceScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD;;GAEG;AAEH;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB7F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAGnF;AAED;;GAEG;AAEH,wBAAgB,QAAQ,CACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAiDhC;AAkcD,eAAO,MAAM,iBAAiB;;;;EAI5B,CAAC"}
@@ -9,6 +9,7 @@ const coreModuleSpecifier = ['@zintrust', 'core'].join('/');
9
9
  const coreStartModuleSpecifier = `${coreModuleSpecifier}/start`;
10
10
  const serviceManifestImportExpression = "import('./bootstrap/service-manifest.ts').catch(() => import('./bootstrap/service-manifest.js'))";
11
11
  const buildRouteImportExpression = (domain, serviceName) => `import('../services/${domain}/${serviceName}/routes/api.ts').catch(() => import('../services/${domain}/${serviceName}/routes/api.js'))`;
12
+ const getServiceConfigRoot = (domain, serviceName) => `src/services/${domain}/${serviceName}/config`;
12
13
  /**
13
14
  * ServiceScaffolder generates microservices with all necessary files
14
15
  */
@@ -107,6 +108,7 @@ export const serviceManifest: ReadonlyArray<ServiceManifestEntry> = [
107
108
  id: '${serviceId}',
108
109
  domain: '${domain}',
109
110
  name: '${options.name}',
111
+ configRoot: '${getServiceConfigRoot(domain, options.name)}',
110
112
  prefix: '${serviceId}',
111
113
  port: ${options.port ?? 3001},
112
114
  monolithEnabled: true,
@@ -148,6 +150,7 @@ function updateServiceManifest(projectRoot, options) {
148
150
  id: '${serviceId}',
149
151
  domain: '${domain}',
150
152
  name: '${options.name}',
153
+ configRoot: '${getServiceConfigRoot(domain, options.name)}',
151
154
  prefix: '${serviceId}',
152
155
  port: ${options.port ?? 3001},
153
156
  monolithEnabled: true,
@@ -236,7 +239,7 @@ function generateServiceConfig(options) {
236
239
  function generateServiceIndex(options) {
237
240
  const domain = options.domain ?? 'default';
238
241
  const serviceId = `${domain}/${options.name}`;
239
- const configRoot = `src/services/${domain}/${options.name}/config`;
242
+ const configRoot = getServiceConfigRoot(domain, options.name);
240
243
  return `/**
241
244
  * ${options.name} Service - Entry Point
242
245
  * Port: ${options.port ?? 3001}
@@ -1,7 +1,9 @@
1
1
  type node_env = 'development' | 'production' | 'testing';
2
2
  type LoadOptions = {
3
3
  cwd?: string;
4
+ includeCwd?: boolean;
4
5
  extraCwds?: string[];
6
+ envPaths?: string[];
5
7
  overrideExisting?: boolean;
6
8
  };
7
9
  type LoadState = {
@@ -1 +1 @@
1
- {"version":3,"file":"EnvFileLoader.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/EnvFileLoader.ts"],"names":[],"mappings":"AAQA,KAAK,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAAC;AAmIzD,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAkHF,eAAO,MAAM,aAAa;qBA7DH,WAAW,KAAQ,SAAS;6BAyBpB,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,KAAQ,SAAS;mCAG/C,YAAY,KAAG,IAAI;oBA+BpC,SAAS;EAO5B,CAAC"}
1
+ {"version":3,"file":"EnvFileLoader.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/EnvFileLoader.ts"],"names":[],"mappings":"AASA,KAAK,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAAC;AAmIzD,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAaF,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAgMF,eAAO,MAAM,aAAa;qBAjDH,WAAW,KAAQ,SAAS;6BAapB,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,KAAQ,SAAS;mCAG/C,YAAY,KAAG,IAAI;oBA+BpC,SAAS;EAO5B,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { Env } from '../../config/env.js';
2
+ import { isArray, isNonEmptyString } from '../../helper/index.js';
2
3
  import { existsSync, readFileSync } from '../../node-singletons/fs.js';
3
- import { join } from '../../node-singletons/path.js';
4
+ import * as path from '../../node-singletons/path.js';
4
5
  const safeEnvGet = (key, defaultValue = '') => {
5
6
  const envAny = Env;
6
7
  if (typeof envAny.get === 'function')
@@ -102,7 +103,7 @@ const applyToProcessEnv = (values, overrideExisting) => {
102
103
  }
103
104
  };
104
105
  const readEnvFileIfExists = (cwd, filename) => {
105
- const fullPath = join(cwd, filename);
106
+ const fullPath = path.join(cwd, filename);
106
107
  if (!existsSync(fullPath))
107
108
  return undefined;
108
109
  const raw = readFileSync(fullPath, 'utf-8');
@@ -120,20 +121,20 @@ const resolveAppMode = (cwd) => {
120
121
  };
121
122
  const filesLoader = (cwd, mode) => {
122
123
  const files = [];
123
- if (existsSync(join(cwd, '.env')))
124
+ if (existsSync(path.join(cwd, '.env')))
124
125
  files.push('.env');
125
126
  // Per your rule: production uses .env; dev uses .env.dev
126
127
  if (mode !== undefined && mode !== '' && mode !== 'production') {
127
128
  const modeFile = `.env.${mode}`;
128
- if (existsSync(join(cwd, modeFile)))
129
+ if (existsSync(path.join(cwd, modeFile)))
129
130
  files.push(modeFile);
130
131
  }
131
132
  const local = '.env.local';
132
- if (existsSync(join(cwd, local)))
133
+ if (existsSync(path.join(cwd, local)))
133
134
  files.push(local);
134
135
  if (mode !== undefined && mode !== '') {
135
136
  const modeLocal = `.env.${mode}.local`;
136
- if (existsSync(join(cwd, modeLocal)))
137
+ if (existsSync(path.join(cwd, modeLocal)))
137
138
  files.push(modeLocal);
138
139
  }
139
140
  return files;
@@ -161,25 +162,85 @@ const loadFromCwd = (cwd, overrideExisting) => {
161
162
  }
162
163
  return { loadedFiles: files, mode };
163
164
  };
164
- const load = (options = {}) => {
165
- if (cached !== undefined)
166
- return cached;
167
- const cwd = typeof options.cwd === 'string' && options.cwd !== '' ? options.cwd : process.cwd();
168
- const extraCwds = Array.isArray(options.extraCwds)
169
- ? options.extraCwds.filter((value) => typeof value === 'string' && value.trim() !== '')
170
- : [];
165
+ const loadFromFile = (filePath, overrideExisting) => {
166
+ if (!existsSync(filePath))
167
+ return { loadedFiles: [] };
168
+ const raw = readFileSync(filePath, 'utf-8');
169
+ const parsed = parseEnvFile(raw);
170
+ applyToProcessEnv(parsed, overrideExisting);
171
+ const rawMode = parsed['NODE_ENV'];
172
+ const mode = isNonEmptyString(rawMode) ? normalizeAppMode(rawMode) : undefined;
173
+ if (mode !== undefined) {
174
+ safeEnvSet('NODE_ENV', mode);
175
+ }
176
+ return { loadedFiles: [filePath], mode };
177
+ };
178
+ const normalizeCwdList = (value) => {
179
+ if (!isArray(value))
180
+ return [];
181
+ return value
182
+ .filter(isNonEmptyString)
183
+ .map((item) => item.trim())
184
+ .filter((item) => item !== '');
185
+ };
186
+ const normalizeEnvPathList = (value) => normalizeCwdList(value);
187
+ const createLoadPlan = (options) => {
188
+ const cwd = isNonEmptyString(options.cwd) ? options.cwd : process.cwd();
189
+ const includeCwd = options.includeCwd !== false;
190
+ const extraCwds = normalizeCwdList(options.extraCwds);
191
+ const envPaths = normalizeEnvPathList(options.envPaths);
171
192
  const overrideExisting = options.overrideExisting ?? true;
172
- const roots = [cwd, ...extraCwds].filter((value, index, items) => items.indexOf(value) === index);
173
- let mergedMode;
174
- const loadedFiles = [];
175
- for (let index = 0; index < roots.length; index += 1) {
176
- const root = roots[index];
177
- const state = loadFromCwd(root, index === 0 ? overrideExisting : true);
178
- if (mergedMode === undefined && state.mode !== undefined)
179
- mergedMode = state.mode;
180
- loadedFiles.push(...state.loadedFiles);
181
- }
182
- cached = { loadedFiles, mode: mergedMode };
193
+ const sources = [];
194
+ if (includeCwd) {
195
+ sources.push({
196
+ key: `cwd:${cwd}`,
197
+ path: cwd,
198
+ kind: 'cwd',
199
+ overrideExisting,
200
+ });
201
+ }
202
+ for (const extraCwd of extraCwds) {
203
+ sources.push({
204
+ key: `cwd:${extraCwd}`,
205
+ path: extraCwd,
206
+ kind: 'cwd',
207
+ overrideExisting: true,
208
+ });
209
+ }
210
+ for (const envPath of envPaths) {
211
+ const looksLikeFile = path.basename(envPath).startsWith('.env');
212
+ sources.push({
213
+ key: `${looksLikeFile ? 'file' : 'cwd'}:${envPath}`,
214
+ path: envPath,
215
+ kind: looksLikeFile ? 'file' : 'cwd',
216
+ overrideExisting: true,
217
+ });
218
+ }
219
+ return sources.filter((source, index, items) => items.findIndex((item) => item.key === source.key) === index);
220
+ };
221
+ const loadSource = (source) => {
222
+ if (source.kind === 'file')
223
+ return loadFromFile(source.path, source.overrideExisting);
224
+ return loadFromCwd(source.path, source.overrideExisting);
225
+ };
226
+ const mergeCachedState = (state, source, next) => {
227
+ state.loadedSourceKeys.push(source.key);
228
+ if (next.mode !== undefined && state.mode === undefined) {
229
+ state.mode = next.mode;
230
+ }
231
+ if (next.loadedFiles.length > 0) {
232
+ state.loadedFiles.push(...next.loadedFiles);
233
+ }
234
+ return state;
235
+ };
236
+ const load = (options = {}) => {
237
+ const plan = createLoadPlan(options);
238
+ cached ??= { loadedFiles: [], loadedSourceKeys: [] };
239
+ for (const source of plan) {
240
+ if (cached.loadedSourceKeys.includes(source.key))
241
+ continue;
242
+ mergeCachedState(cached, source, loadSource(source));
243
+ }
183
244
  return cached;
184
245
  };
185
246
  const ensureLoaded = (options = {}) => load({ ...options, overrideExisting: false });
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBlF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFxF,CAAC;AAEH,eAAO,MAAM,aAAa,QAAO,MAchC,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAsClF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFxF,CAAC;AAEH,eAAO,MAAM,aAAa,QAAO,MAchC,CAAC"}
package/src/config/env.js CHANGED
@@ -8,11 +8,24 @@
8
8
  // Cache process check once at module load time
9
9
  const processLike = typeof process === 'undefined' ? undefined : process;
10
10
  let externalEnvSource = null;
11
+ const getGlobalEnv = () => {
12
+ const env = globalThis.env;
13
+ if (env === undefined || env === null || typeof env !== 'object')
14
+ return undefined;
15
+ return env;
16
+ };
11
17
  const getEnvSource = () => {
12
18
  if (typeof externalEnvSource === 'function')
13
19
  return externalEnvSource();
14
20
  if (externalEnvSource !== null)
15
21
  return externalEnvSource;
22
+ const globalEnv = getGlobalEnv();
23
+ if (globalEnv !== undefined) {
24
+ return {
25
+ ...processLike?.env,
26
+ ...globalEnv,
27
+ };
28
+ }
16
29
  return processLike?.env ?? {};
17
30
  };
18
31
  const normalizeEnvValue = (value) => {
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/functions/cloudflare.ts"],"names":[],"mappings":";mBAmIuB,OAAO,QAAQ,OAAO,QAAQ,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;AADhF,wBAmCE"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/functions/cloudflare.ts"],"names":[],"mappings":";mBA0JuB,OAAO,QAAQ,OAAO,QAAQ,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;;AADhF,wBAoCE"}
@@ -101,6 +101,25 @@ const injectIoredisModule = async () => {
101
101
  }
102
102
  };
103
103
  let startupConfigOverridesPromise;
104
+ const WORKER_ENV_SNAPSHOT_KEY = 'ZINTRUST_WORKER_ENV_SNAPSHOT';
105
+ const resolveWorkersEnv = (env) => {
106
+ const bindings = typeof env === 'object' && env !== null ? { ...env } : {};
107
+ const rawSnapshot = bindings[WORKER_ENV_SNAPSHOT_KEY];
108
+ if (typeof rawSnapshot !== 'string' || rawSnapshot.trim() === '') {
109
+ return bindings;
110
+ }
111
+ try {
112
+ const parsed = JSON.parse(rawSnapshot);
113
+ Reflect.deleteProperty(bindings, WORKER_ENV_SNAPSHOT_KEY);
114
+ return {
115
+ ...parsed,
116
+ ...bindings,
117
+ };
118
+ }
119
+ catch {
120
+ return bindings;
121
+ }
122
+ };
104
123
  const ensureStartupConfigOverridesLoaded = async () => {
105
124
  startupConfigOverridesPromise ??= applyStartupConfigOverrides();
106
125
  await startupConfigOverridesPromise;
@@ -108,8 +127,9 @@ const ensureStartupConfigOverridesLoaded = async () => {
108
127
  export default {
109
128
  async fetch(request, _env, _ctx) {
110
129
  try {
130
+ const workersEnv = resolveWorkersEnv(_env);
111
131
  // Make bindings available to framework code in Workers
112
- globalThis.env = _env;
132
+ globalThis.env = workersEnv;
113
133
  const AppRoutes = (await import('@routes/' + 'api.ts'));
114
134
  if (AppRoutes !== undefined) {
115
135
  globalThis.__zintrustRoutes = AppRoutes;
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.12
2
+ * @zintrust/core v0.4.14
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-03-23T13:57:31.438Z
8
+ * Built: 2026-03-23T16:59:39.933Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.41';
24
- export const ZINTRUST_BUILD_DATE = '2026-03-23T13:57:31.366Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-23T16:59:39.858Z'; // Replaced during build
25
25
  export { Application } from './boot/Application.js';
26
26
  export { AwsSigV4 } from './common/index.js';
27
27
  export { SignedRequest } from './security/SignedRequest.js';
@@ -4,6 +4,7 @@ export interface ServiceManifestEntry {
4
4
  domain: string;
5
5
  name: string;
6
6
  prefix?: string;
7
+ configRoot?: string;
7
8
  version?: string;
8
9
  description?: string;
9
10
  port?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceManifest.d.ts","sourceRoot":"","sources":["../../../src/microservices/ServiceManifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ServiceManifest.d.ts","sourceRoot":"","sources":["../../../src/microservices/ServiceManifest.ts"],"names":[],"mappings":"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"}
@@ -46,6 +46,10 @@ export const isServiceManifestEntry = (value) => {
46
46
  if (prefix !== undefined && typeof prefix !== 'string') {
47
47
  return false;
48
48
  }
49
+ const configRoot = value['configRoot'];
50
+ if (configRoot !== undefined && typeof configRoot !== 'string') {
51
+ return false;
52
+ }
49
53
  const loadRoutes = value['loadRoutes'];
50
54
  if (loadRoutes !== undefined && !isFunction(loadRoutes)) {
51
55
  return false;
@@ -59,6 +63,7 @@ export const normalizeServiceManifest = (value) => {
59
63
  ...entry,
60
64
  id: toCanonicalServiceId(entry),
61
65
  prefix: getServicePrefix(entry),
66
+ ...(isNonEmptyString(entry.configRoot) ? { configRoot: entry.configRoot } : {}),
62
67
  monolithEnabled: entry.monolithEnabled !== false,
63
68
  }));
64
69
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAqhBD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAI1C,CAAC;AAIH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,cAAc,GAAG,SAAqB,EAC9C,uBAA0B,KACzB,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAMxC,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAoBtF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAWnD"}
1
+ {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAYxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAqhBD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAI1C,CAAC;AAIH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,cAAc,GAAG,SAAqB,EAC9C,uBAA0B,KACzB,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAMxC,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAsBtF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAYnD"}
@@ -12,6 +12,7 @@ import { D1Adapter } from './adapters/D1Adapter.js';
12
12
  import { D1RemoteAdapter } from './adapters/D1RemoteAdapter.js';
13
13
  import { MySQLAdapter } from './adapters/MySQLAdapter.js';
14
14
  import { MySQLProxyAdapter } from './adapters/MySQLProxyAdapter.js';
15
+ import { DatabaseConnectionRegistry } from './DatabaseConnectionRegistry.js';
15
16
  import { PostgreSQLAdapter } from './adapters/PostgreSQLAdapter.js';
16
17
  import { PostgreSQLProxyAdapter } from './adapters/PostgreSQLProxyAdapter.js';
17
18
  import { SQLiteAdapter } from './adapters/SQLiteAdapter.js';
@@ -432,14 +433,15 @@ export const useEnsureDbConnected = async (config = undefined, connectionName =
432
433
  };
433
434
  export function useDatabase(config, connection = 'default') {
434
435
  if (databaseInstances.has(connection) === false) {
435
- if (config === undefined) {
436
+ const resolvedConfig = config ?? DatabaseConnectionRegistry.get(connection);
437
+ if (resolvedConfig === undefined) {
436
438
  // Diagnostic logging
437
439
  Logger.error('[DEBUG] Database instances keys:', Array.from(databaseInstances.keys()));
438
440
  Logger.error('[DEBUG] Requesting connection:', connection);
439
441
  throw ErrorFactory.createConfigError(`Database connection '${connection}' is not registered. ` +
440
442
  `Call useDatabase(config, '${connection}') during startup to register it.`);
441
443
  }
442
- databaseInstances.set(connection, Database.create(config));
444
+ databaseInstances.set(connection, Database.create(resolvedConfig));
443
445
  }
444
446
  const instance = databaseInstances.get(connection);
445
447
  if (instance === undefined) {
@@ -459,4 +461,5 @@ export async function resetDatabase() {
459
461
  });
460
462
  await Promise.all(promises);
461
463
  databaseInstances.clear();
464
+ DatabaseConnectionRegistry.clear();
462
465
  }
@@ -0,0 +1,8 @@
1
+ import type { DatabaseConfig } from './DatabaseAdapter';
2
+ export declare const DatabaseConnectionRegistry: Readonly<{
3
+ clear(): void;
4
+ set(name: string, config: DatabaseConfig): void;
5
+ get(name: string): DatabaseConfig | undefined;
6
+ }>;
7
+ export default DatabaseConnectionRegistry;
8
+ //# sourceMappingURL=DatabaseConnectionRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DatabaseConnectionRegistry.d.ts","sourceRoot":"","sources":["../../../src/orm/DatabaseConnectionRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI3D,eAAO,MAAM,0BAA0B;aAC5B,IAAI;cAIH,MAAM,UAAU,cAAc,GAAG,IAAI;cAIrC,MAAM,GAAG,cAAc,GAAG,SAAS;EAG7C,CAAC;AAEH,eAAe,0BAA0B,CAAC"}
@@ -0,0 +1,13 @@
1
+ const state = new Map();
2
+ export const DatabaseConnectionRegistry = Object.freeze({
3
+ clear() {
4
+ state.clear();
5
+ },
6
+ set(name, config) {
7
+ state.set(name, config);
8
+ },
9
+ get(name) {
10
+ return state.get(name);
11
+ },
12
+ });
13
+ export default DatabaseConnectionRegistry;
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../src/orm/DatabaseRuntimeRegistration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,mBAAmB,EAGpB,MAAM,cAAc,CAAC;AA0DtB;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAYpF"}
1
+ {"version":3,"file":"DatabaseRuntimeRegistration.d.ts","sourceRoot":"","sources":["../../../src/orm/DatabaseRuntimeRegistration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,mBAAmB,EAGpB,MAAM,cAAc,CAAC;AA4DtB;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAapF"}
@@ -5,6 +5,7 @@
5
5
  * instances that can be selected via `useDatabase(undefined, name)`.
6
6
  */
7
7
  import { ErrorFactory } from '../exceptions/ZintrustError.js';
8
+ import { DatabaseConnectionRegistry } from './DatabaseConnectionRegistry.js';
8
9
  import { useDatabase } from './Database.js';
9
10
  const toOrmConfig = (cfg) => {
10
11
  switch (cfg.driver) {
@@ -50,9 +51,9 @@ const toOrmConfig = (cfg) => {
50
51
  }
51
52
  };
52
53
  const registerConnections = (connections) => {
54
+ DatabaseConnectionRegistry.clear();
53
55
  for (const [name, runtimeCfg] of Object.entries(connections)) {
54
- // Register instance by name; a later call without config may now resolve.
55
- useDatabase(toOrmConfig(runtimeCfg), name);
56
+ DatabaseConnectionRegistry.set(name, toOrmConfig(runtimeCfg));
56
57
  }
57
58
  };
58
59
  import { Logger } from '../config/logger.js';
@@ -71,5 +72,6 @@ export function registerDatabasesFromRuntimeConfig(config) {
71
72
  throw ErrorFactory.createConfigError(`Database default connection not configured: ${String(config.default ?? '')}`);
72
73
  }
73
74
  Logger.info(`✓ Registering default database connection: ${config.default}`);
75
+ useDatabase(toOrmConfig(defaultCfg), config.default);
74
76
  useDatabase(toOrmConfig(defaultCfg), 'default');
75
77
  }
@@ -0,0 +1,6 @@
1
+ export declare function loadProjectBootstrap(): Promise<void>;
2
+ declare const _default: Readonly<{
3
+ loadProjectBootstrap: typeof loadProjectBootstrap;
4
+ }>;
5
+ export default _default;
6
+ //# sourceMappingURL=ProjectBootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectBootstrap.d.ts","sourceRoot":"","sources":["../../../src/runtime/ProjectBootstrap.ts"],"names":[],"mappings":"AAgCA,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAU1D;;;;AAED,wBAEG"}
@@ -0,0 +1,43 @@
1
+ import { Logger } from '../config/logger.js';
2
+ import { existsSync } from '../node-singletons/fs.js';
3
+ import * as path from '../node-singletons/path.js';
4
+ import { pathToFileURL } from '../node-singletons/url.js';
5
+ const getProjectRoot = () => {
6
+ const fromEnv = process.env?.['ZINTRUST_PROJECT_ROOT'] ?? '';
7
+ if (fromEnv.trim() !== '')
8
+ return fromEnv.trim();
9
+ return process.cwd();
10
+ };
11
+ const getBootstrapCandidates = (projectRoot) => [
12
+ path.join(projectRoot, 'src', 'boot', 'bootstrap.ts'),
13
+ path.join(projectRoot, 'dist', 'src', 'boot', 'bootstrap.js'),
14
+ path.join(projectRoot, 'src', 'boot', 'bootstrap.js'),
15
+ ];
16
+ const tryImportBootstrapCandidate = async (candidate) => {
17
+ if (!existsSync(candidate))
18
+ return false;
19
+ try {
20
+ await import(pathToFileURL(candidate).href);
21
+ return true;
22
+ }
23
+ catch (error) {
24
+ Logger.warn('Failed to import project bootstrap candidate', {
25
+ candidate,
26
+ error: error instanceof Error ? error.message : String(error),
27
+ });
28
+ return false;
29
+ }
30
+ };
31
+ export async function loadProjectBootstrap() {
32
+ const projectRoot = getProjectRoot();
33
+ for (const candidate of getBootstrapCandidates(projectRoot)) {
34
+ // eslint-disable-next-line no-await-in-loop
35
+ const loaded = await tryImportBootstrapCandidate(candidate);
36
+ if (loaded)
37
+ return;
38
+ }
39
+ await import('../boot/bootstrap.js');
40
+ }
41
+ export default Object.freeze({
42
+ loadProjectBootstrap,
43
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/ProjectRuntime.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AA2ExC,eAAO,MAAM,cAAc;aAChB,IAAI;iBAIA,oBAAoB,GAAG,SAAS;gBAIjC,OAAO,GAAG,oBAAoB;oCAIV,OAAO,GAAG,oBAAoB,GAAG,SAAS;0BAM9C,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;4BAgBvC,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;0BAejD,aAAa,CAAC,oBAAoB,CAAC;wBAIrC,oBAAoB,GAAG,SAAS;EAGpD,CAAC;AAEH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"ProjectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/ProjectRuntime.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AA+ExC,eAAO,MAAM,cAAc;aAChB,IAAI;iBAIA,oBAAoB,GAAG,SAAS;gBAIjC,OAAO,GAAG,oBAAoB;oCAIV,OAAO,GAAG,oBAAoB,GAAG,SAAS;0BAM9C,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;4BAgBvC,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;0BAejD,aAAa,CAAC,oBAAoB,CAAC;wBAIrC,oBAAoB,GAAG,SAAS;EAGpD,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -33,6 +33,9 @@ const cacheProjectRuntime = (module) => {
33
33
  const getCachedProjectRuntime = () => {
34
34
  return getRuntimeGlobal().__zintrustProjectRuntime;
35
35
  };
36
+ const hasLoadedServiceManifest = (runtime) => {
37
+ return Array.isArray(runtime?.serviceManifest);
38
+ };
36
39
  const tryImportNodeRuntimeCandidate = async (candidate) => {
37
40
  if (!existsSync(candidate))
38
41
  return undefined;
@@ -76,7 +79,7 @@ export const ProjectRuntime = Object.freeze({
76
79
  },
77
80
  async tryLoadNodeRuntime() {
78
81
  const cached = getCachedProjectRuntime();
79
- if (cached !== undefined)
82
+ if (hasLoadedServiceManifest(cached))
80
83
  return cached;
81
84
  const projectRoot = getProjectRoot();
82
85
  const candidates = getNodeRuntimeCandidates(projectRoot);
@@ -90,7 +93,7 @@ export const ProjectRuntime = Object.freeze({
90
93
  },
91
94
  async tryLoadWorkerRuntime() {
92
95
  const cached = getCachedProjectRuntime();
93
- if (cached !== undefined)
96
+ if (hasLoadedServiceManifest(cached))
94
97
  return cached;
95
98
  const workerModuleIds = ['../' + 'zintrust.runtime.wg.js', '../' + 'zintrust.runtime.js'];
96
99
  for (const moduleId of workerModuleIds) {
@@ -1 +1 @@
1
- {"version":3,"file":"CloudflareAdapter.d.ts","sourceRoot":"","sources":["../../../../src/runtime/adapters/CloudflareAdapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,aAAa,EAGb,cAAc,EACf,MAAM,yBAAyB,CAAC;AAGjC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;IAC5B;;OAEG;mBACY,aAAa,GAAG,cAAc;IAwD7C;;;OAGG;qBACc,OAAO;IAKxB;;;OAGG;qBACc,MAAM,GAAG,OAAO;EAIjC,CAAC;AAiKH;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,OAAO;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACjC,EAAE,CAAC,EAAE;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH"}
1
+ {"version":3,"file":"CloudflareAdapter.d.ts","sourceRoot":"","sources":["../../../../src/runtime/adapters/CloudflareAdapter.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,aAAa,EAGb,cAAc,EACf,MAAM,yBAAyB,CAAC;AAGjC;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;IAC5B;;OAEG;mBACY,aAAa,GAAG,cAAc;IA8D7C;;;OAGG;qBACc,OAAO;IAKxB;;;OAGG;qBACc,MAAM,GAAG,OAAO;EAIjC,CAAC;AAiKH;;;;GAIG;AACH,MAAM,WAAW,iBAAkB,SAAQ,OAAO;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;IACjC,EAAE,CAAC,EAAE;QACH,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH"}
@@ -4,7 +4,7 @@
4
4
  import { appConfig } from '../../config/index.js';
5
5
  import { isUndefinedOrNull } from '../../helper/index.js';
6
6
  import { Cloudflare } from '../../config/cloudflare.js';
7
- import { Env } from '../../config/env.js';
7
+ import { Env, getProcessLike } from '../../config/env.js';
8
8
  import { Logger } from '../../config/logger.js';
9
9
  import { createMockHttpObjects, ErrorResponse, HttpResponse } from '../RuntimeAdapter.js';
10
10
  /**
@@ -19,7 +19,13 @@ export const CloudflareAdapter = Object.freeze({
19
19
  create(config) {
20
20
  const workersEnv = Cloudflare.getWorkersEnv();
21
21
  if (workersEnv !== null) {
22
- Env.setSource(() => workersEnv);
22
+ const processEnv = getProcessLike()?.env;
23
+ const resolveEnvSource = () => {
24
+ if (processEnv === undefined)
25
+ return workersEnv;
26
+ return { ...processEnv, ...workersEnv };
27
+ };
28
+ Env.setSource(resolveEnvSource);
23
29
  }
24
30
  const logger = config.logger ?? createDefaultLogger();
25
31
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/start.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AAexC,eAAO,MAAM,UAAU,GAAI,eAAe,MAAM,KAAG,OAYlD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,eAAe,OAAO,KAAG,oBASnE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,eAAe,OAAO,KACrB,OAAO,CAAC,oBAAoB,CAQ9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAO1C,CAAC;AAEF;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/start.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AAuBxC,eAAO,MAAM,UAAU,GAAI,eAAe,MAAM,KAAG,OAYlD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,eAAe,OAAO,KAAG,oBASnE,CAAC;AAqFF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,eAAe,OAAO,KACrB,OAAO,CAAC,oBAAoB,CAS9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAO1C,CAAC;AAEF;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
package/src/start.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { ErrorFactory } from './exceptions/ZintrustError.js';
2
+ import { isArray, isNonEmptyString, isObject } from './helper/index.js';
2
3
  import { ZintrustLang } from './lang/lang.js';
3
4
  import { normalizeActiveServiceRuntime, } from './microservices/ServiceManifest.js';
4
5
  import { ProjectRuntime } from './runtime/ProjectRuntime.js';
5
6
  import { isNodeRuntime } from './runtime/detectRuntime.js';
7
+ const isAbsolutePath = (value) => value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
6
8
  const fileUrlToPathLike = (value) => {
7
9
  if (!value.startsWith(ZintrustLang.FILE_PROTOCOL))
8
10
  return value;
@@ -34,7 +36,68 @@ export const configureStandaloneService = (activeService) => {
34
36
  }
35
37
  return ProjectRuntime.set({ activeService: normalized }).activeService ?? normalized;
36
38
  };
39
+ const normalizeStandaloneEnvPaths = (value) => {
40
+ if (isNonEmptyString(value)) {
41
+ const trimmed = value.trim();
42
+ return trimmed === '' ? [] : [trimmed];
43
+ }
44
+ if (!isArray(value))
45
+ return [];
46
+ return value
47
+ .filter(isNonEmptyString)
48
+ .map((item) => item.trim())
49
+ .filter((item) => item !== '');
50
+ };
51
+ const resolveStandaloneProjectRoot = async () => {
52
+ const configuredRoot = process.env?.['ZINTRUST_PROJECT_ROOT'] ?? '';
53
+ if (isNonEmptyString(configuredRoot))
54
+ return configuredRoot;
55
+ const { existsSync } = await import('./node-singletons/fs.js');
56
+ const path = await import('./node-singletons/path.js');
57
+ let current = process.cwd();
58
+ while (true) {
59
+ if (existsSync(path.join(current, 'package.json')))
60
+ return current;
61
+ const parent = path.dirname(current);
62
+ if (parent === current)
63
+ return process.cwd();
64
+ current = parent;
65
+ }
66
+ };
67
+ const resolveServiceEnvPath = async (importMetaUrl, activeService, projectRoot) => {
68
+ const path = await import('./node-singletons/path.js');
69
+ if (isObject(activeService) && isNonEmptyString(activeService['configRoot'])) {
70
+ return path.dirname(path.join(projectRoot, activeService['configRoot']));
71
+ }
72
+ const entryFile = fileUrlToPathLike(importMetaUrl);
73
+ const entryDir = path.dirname(entryFile);
74
+ return path.basename(entryDir) === 'src' ? path.dirname(entryDir) : entryDir;
75
+ };
76
+ const resolveConfiguredEnvPaths = async (projectRoot, activeService, importMetaUrl) => {
77
+ const path = await import('./node-singletons/path.js');
78
+ const configured = isObject(activeService)
79
+ ? normalizeStandaloneEnvPaths(activeService['envPath'])
80
+ : [];
81
+ if (configured.length > 0) {
82
+ return configured.map((value) => isAbsolutePath(value) ? value : path.join(projectRoot, value));
83
+ }
84
+ return [await resolveServiceEnvPath(importMetaUrl, activeService, projectRoot)];
85
+ };
86
+ const ensureStandaloneServiceEnv = async (importMetaUrl, activeService) => {
87
+ if (!isNodeRuntime())
88
+ return;
89
+ const { EnvFileLoader } = await import('./cli/utils/EnvFileLoader.js');
90
+ const projectRoot = await resolveStandaloneProjectRoot();
91
+ const envPaths = await resolveConfiguredEnvPaths(projectRoot, activeService, importMetaUrl);
92
+ const rootEnv = !isObject(activeService) || activeService['rootEnv'] !== false;
93
+ EnvFileLoader.ensureLoaded({
94
+ cwd: projectRoot,
95
+ includeCwd: rootEnv,
96
+ envPaths,
97
+ });
98
+ };
37
99
  export const bootStandaloneService = async (importMetaUrl, activeService) => {
100
+ await ensureStandaloneServiceEnv(importMetaUrl, activeService);
38
101
  const configuredService = configureStandaloneService(activeService);
39
102
  if (isNodeMain(importMetaUrl)) {
40
103
  await start();
@@ -49,10 +112,8 @@ export const bootStandaloneService = async (importMetaUrl, activeService) => {
49
112
  export const start = async () => {
50
113
  if (!isNodeRuntime())
51
114
  return;
52
- // Compiled output places bootstrap at `dist/src/boot/bootstrap.js`.
53
- // This file compiles to `dist/src/start.js`, so relative import is stable.
54
- // In unit tests, importing bootstrap has heavy side effects (starts server + exits).
55
- await import('./boot/bootstrap.js');
115
+ const projectBootstrapModule = (await import('./runtime/ProjectBootstrap.js'));
116
+ await projectBootstrapModule.loadProjectBootstrap();
56
117
  };
57
118
  /**
58
119
  * Cloudflare Workers entry (module worker style).