@zintrust/core 0.4.9 → 0.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
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":"RoutesCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/RoutesCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAsSvF,eAAO,MAAM,aAAa;cACd,YAAY;EAatB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"RoutesCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/RoutesCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuVvF,eAAO,MAAM,aAAa;cACd,YAAY;EAatB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -6,6 +6,8 @@ import { BaseCommand } from '../BaseCommand.js';
6
6
  import { Env } from '../../config/env.js';
7
7
  import { Router } from '../../routes/Router.js';
8
8
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
9
+ import { isObject } from '../../helper/index.js';
10
+ import { ProjectRuntime } from '../../runtime/ProjectRuntime.js';
9
11
  const parseGroupBy = (value) => {
10
12
  const raw = typeof value === 'string' ? value.trim().toLowerCase() : '';
11
13
  if (raw === '' || raw === 'group')
@@ -157,6 +159,37 @@ const renderTable = (rows) => {
157
159
  console.log(bottom);
158
160
  /* eslint-enable no-console */
159
161
  };
162
+ const annotateManifestRoutes = (router, beforeCount, serviceId) => {
163
+ const routes = Router.getRoutes(router);
164
+ for (const route of routes.slice(beforeCount)) {
165
+ route.__zintrustServiceId = serviceId;
166
+ }
167
+ };
168
+ const getRouteServiceId = (route) => {
169
+ return route.__zintrustServiceId ?? '';
170
+ };
171
+ const registerManifestRoutes = async (router) => {
172
+ await ProjectRuntime.tryLoadNodeRuntime();
173
+ const serviceManifest = ProjectRuntime.getServiceManifest();
174
+ if (serviceManifest.length === 0)
175
+ return;
176
+ for (const entry of serviceManifest) {
177
+ if (entry.monolithEnabled === false || typeof entry.loadRoutes !== 'function')
178
+ continue;
179
+ try {
180
+ const beforeCount = Router.getRoutes(router).length;
181
+ // eslint-disable-next-line no-await-in-loop
182
+ const mod = await entry.loadRoutes();
183
+ if (isObject(mod) && typeof mod.registerRoutes === 'function') {
184
+ mod.registerRoutes(router);
185
+ annotateManifestRoutes(router, beforeCount, entry.id);
186
+ }
187
+ }
188
+ catch {
189
+ // Ignore broken optional service routes and keep listing what is available.
190
+ }
191
+ }
192
+ };
160
193
  const buildRows = async (options) => {
161
194
  const groupBy = parseGroupBy(options.groupBy);
162
195
  const filterText = typeof options.filter === 'string' ? options.filter.trim().toLowerCase() : '';
@@ -174,12 +207,14 @@ const buildRows = async (options) => {
174
207
  catch {
175
208
  // routes/api.ts not found, continue with just core routes
176
209
  }
210
+ await registerManifestRoutes(router);
177
211
  const routes = Router.getRoutes(router);
178
212
  const rows = routes.map((route) => {
179
213
  const path = normalizePath(route.path);
214
+ const serviceId = getRouteServiceId(route);
180
215
  let group = '';
181
216
  if (groupBy === 'service') {
182
- group = deriveService(path);
217
+ group = serviceId === '' ? deriveService(path) : serviceId;
183
218
  }
184
219
  else if (groupBy === 'group') {
185
220
  group = deriveGroup(path);
@@ -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;AAspBvF,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;AA2sBvF,eAAO,MAAM,YAAY;cACb,YAAY;EA6BtB,CAAC"}
@@ -168,8 +168,19 @@ const resolveCacheEnabledPreference = (options) => {
168
168
  return options.cache;
169
169
  return undefined;
170
170
  };
171
- const readPackageJson = (cwd) => {
172
- const packagePath = path.join(cwd, 'package.json');
171
+ const findNearestPackageJsonDir = (cwd) => {
172
+ let current = path.resolve(cwd);
173
+ while (true) {
174
+ if (existsSync(path.join(current, 'package.json')))
175
+ return current;
176
+ const parent = path.dirname(current);
177
+ if (parent === current)
178
+ return undefined;
179
+ current = parent;
180
+ }
181
+ };
182
+ const readPackageJsonFromDir = (dir) => {
183
+ const packagePath = path.join(dir, 'package.json');
173
184
  if (!existsSync(packagePath)) {
174
185
  throw ErrorFactory.createCliError("Error: No ZinTrust app found. Run 'zin new <project>' or ensure package.json exists.");
175
186
  }
@@ -181,6 +192,24 @@ const readPackageJson = (cwd) => {
181
192
  throw ErrorFactory.createTryCatchError('Failed to read package.json', error);
182
193
  }
183
194
  };
195
+ const resolveStartContext = (cwd) => {
196
+ const projectRoot = findNearestPackageJsonDir(cwd) ?? cwd;
197
+ const packageDir = findNearestPackageJsonDir(cwd);
198
+ return {
199
+ cwd,
200
+ projectRoot,
201
+ ...(packageDir === undefined ? {} : { packageJson: readPackageJsonFromDir(packageDir) }),
202
+ };
203
+ };
204
+ const requirePackageJson = (context) => {
205
+ if (context.packageJson !== undefined)
206
+ return context.packageJson;
207
+ throw ErrorFactory.createCliError("Error: No ZinTrust app found. Run 'zin new <project>' or ensure package.json exists.");
208
+ };
209
+ const buildStartEnv = (projectRoot) => ({
210
+ ...process.env,
211
+ ZINTRUST_PROJECT_ROOT: projectRoot,
212
+ });
184
213
  const isFrameworkRepo = (packageJson) => packageJson.name === '@zintrust/core';
185
214
  const hasDevScript = (packageJson) => {
186
215
  const scripts = packageJson.scripts;
@@ -263,14 +292,14 @@ const resolveNodeProdCommand = (cwd) => {
263
292
  }
264
293
  return { command: 'node', args: [compiled] };
265
294
  };
266
- const executeWranglerStart = async (cmd, cwd, port, runtime, envName, wranglerConfig) => {
295
+ const executeWranglerStart = async (cmd, context, port, runtime, envName, wranglerConfig) => {
267
296
  if (runtime !== undefined) {
268
297
  throw ErrorFactory.createCliError('Error: --runtime is not supported with --wrangler (Wrangler controls Workers runtime).');
269
298
  }
270
299
  const normalizedConfig = typeof wranglerConfig === 'string' ? wranglerConfig.trim() : '';
271
- const explicitConfigFullPath = normalizedConfig.length > 0 ? path.join(cwd, normalizedConfig) : undefined;
272
- const configPath = explicitConfigFullPath ?? findWranglerConfig(cwd);
273
- const entry = resolveWranglerEntry(cwd);
300
+ const explicitConfigFullPath = normalizedConfig.length > 0 ? path.join(context.cwd, normalizedConfig) : undefined;
301
+ const configPath = explicitConfigFullPath ?? findWranglerConfig(context.cwd);
302
+ const entry = resolveWranglerEntry(context.cwd);
274
303
  if (explicitConfigFullPath !== undefined) {
275
304
  if (existsSync(explicitConfigFullPath)) {
276
305
  // ok
@@ -300,7 +329,7 @@ const executeWranglerStart = async (cmd, cwd, port, runtime, envName, wranglerCo
300
329
  const exitCode = await SpawnUtil.spawnAndWait({
301
330
  command: 'wrangler',
302
331
  args: wranglerArgs,
303
- env: process.env,
332
+ env: buildStartEnv(context.projectRoot),
304
333
  });
305
334
  process.exit(exitCode);
306
335
  };
@@ -321,92 +350,98 @@ const ensureTmpRunnerFile = (cwd, filename, content) => {
321
350
  }
322
351
  return fullPath;
323
352
  };
324
- const executeDenoStart = async (cmd, cwd, mode, watchEnabled, _port, runtime) => {
353
+ const executeDenoStart = async (cmd, context, mode, watchEnabled, _port, runtime) => {
325
354
  if (runtime !== undefined) {
326
355
  throw ErrorFactory.createCliError('Error: --runtime cannot be used with --deno.');
327
356
  }
328
357
  if (mode === 'testing') {
329
358
  throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use development or production.');
330
359
  }
331
- const startModuleSpecifier = resolveRuntimeStartModuleSpecifier(cwd);
332
- const denoRunner = ensureTmpRunnerFile(cwd, 'zin-start-deno.ts', createDenoRunnerSource(startModuleSpecifier));
360
+ const startModuleSpecifier = resolveRuntimeStartModuleSpecifier(context.cwd);
361
+ const denoRunner = ensureTmpRunnerFile(context.cwd, 'zin-start-deno.ts', createDenoRunnerSource(startModuleSpecifier));
333
362
  const args = [];
334
363
  if (mode === 'development' && watchEnabled)
335
364
  args.push('watch');
336
365
  args.push(denoRunner);
337
366
  cmd.info('Starting in Deno adapter mode...');
338
- const exitCode = await SpawnUtil.spawnAndWait({ command: 'tsx', args, env: process.env });
367
+ const exitCode = await SpawnUtil.spawnAndWait({
368
+ command: 'tsx',
369
+ args,
370
+ env: buildStartEnv(context.projectRoot),
371
+ });
339
372
  process.exit(exitCode);
340
373
  };
341
- const executeLambdaStart = async (cmd, cwd, mode, watchEnabled, _port, runtime) => {
374
+ const executeLambdaStart = async (cmd, context, mode, watchEnabled, _port, runtime) => {
342
375
  if (runtime !== undefined) {
343
376
  throw ErrorFactory.createCliError('Error: --runtime cannot be used with --lambda.');
344
377
  }
345
378
  if (mode === 'testing') {
346
379
  throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use development or production.');
347
380
  }
348
- const startModuleSpecifier = resolveRuntimeStartModuleSpecifier(cwd);
349
- const lambdaRunner = ensureTmpRunnerFile(cwd, 'zin-start-lambda.ts', createLambdaRunnerSource(startModuleSpecifier));
381
+ const startModuleSpecifier = resolveRuntimeStartModuleSpecifier(context.cwd);
382
+ const lambdaRunner = ensureTmpRunnerFile(context.cwd, 'zin-start-lambda.ts', createLambdaRunnerSource(startModuleSpecifier));
350
383
  const args = [];
351
384
  if (mode === 'development' && watchEnabled)
352
385
  args.push('watch');
353
386
  args.push(lambdaRunner);
354
387
  cmd.info('Starting in Lambda adapter mode...');
355
- const exitCode = await SpawnUtil.spawnAndWait({ command: 'tsx', args, env: process.env });
388
+ const exitCode = await SpawnUtil.spawnAndWait({
389
+ command: 'tsx',
390
+ args,
391
+ env: buildStartEnv(context.projectRoot),
392
+ });
356
393
  process.exit(exitCode);
357
394
  };
358
- const executeNodeStart = async (cmd, cwd, mode, watchEnabled, _port) => {
395
+ const executeNodeStart = async (cmd, context, mode, watchEnabled, _port) => {
359
396
  if (mode === 'testing') {
360
397
  throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use --force to override (not supported).');
361
398
  }
362
399
  if (mode === 'development') {
363
400
  if (!watchEnabled) {
364
401
  cmd.warn('Watch mode disabled; starting once.');
365
- const bootstrap = resolveBootstrapEntryTs(cwd);
402
+ const bootstrap = resolveBootstrapEntryTs(context.cwd);
366
403
  const args = bootstrap === undefined ? ['src/index.ts'] : [bootstrap];
367
404
  const exitCode = await SpawnUtil.spawnAndWait({
368
405
  command: 'tsx',
369
406
  args,
370
407
  forwardSignals: false,
371
- env: process.env,
408
+ env: buildStartEnv(context.projectRoot),
372
409
  });
373
410
  process.exit(exitCode);
374
411
  }
375
- const packageJson = readPackageJson(cwd);
376
- const dev = resolveNodeDevCommand(cwd, packageJson);
412
+ const dev = resolveNodeDevCommand(context.cwd, requirePackageJson(context));
377
413
  cmd.info('Starting in development mode (watch enabled)...');
378
414
  const exitCode = await SpawnUtil.spawnAndWait({
379
415
  command: dev.command,
380
416
  args: dev.args,
381
417
  forwardSignals: false,
382
- env: process.env,
418
+ env: buildStartEnv(context.projectRoot),
383
419
  });
384
420
  process.exit(exitCode);
385
421
  }
386
- const prod = resolveNodeProdCommand(cwd);
422
+ const prod = resolveNodeProdCommand(context.cwd);
387
423
  cmd.info('Starting in production mode...');
388
424
  const exitCode = await SpawnUtil.spawnAndWait({
389
425
  command: prod.command,
390
426
  args: prod.args,
391
427
  forwardSignals: false,
392
- env: process.env,
428
+ env: buildStartEnv(context.projectRoot),
393
429
  });
394
430
  process.exit(exitCode);
395
431
  };
396
- const executeSplitStart = async (cmd, cwd, _options) => {
432
+ const executeSplitStart = async (cmd, context, _options) => {
397
433
  cmd.info('🚀 Starting in split mode (Producer + Consumer)...');
398
- const packageJson = readPackageJson(cwd);
399
- const webDev = resolveNodeDevCommand(cwd, packageJson);
434
+ const webDev = resolveNodeDevCommand(context.cwd, requirePackageJson(context));
400
435
  // Producer Environment
401
436
  const producerEnv = {
402
- ...process.env,
437
+ ...buildStartEnv(context.projectRoot),
403
438
  WORKER_ENABLED: 'false',
404
439
  QUEUE_ENABLED: 'true',
405
440
  RUNTIME_MODE: 'node-server',
406
441
  };
407
442
  // Consumer Environment
408
443
  const consumerEnv = {
409
- ...process.env,
444
+ ...buildStartEnv(context.projectRoot),
410
445
  WORKER_ENABLED: 'true',
411
446
  QUEUE_ENABLED: 'true',
412
447
  RUNTIME_MODE: 'containers',
@@ -417,10 +452,10 @@ const executeSplitStart = async (cmd, cwd, _options) => {
417
452
  };
418
453
  // Resolve Consumer Command (zintrust worker:start-all)
419
454
  // We try to use tsx against the source bin if possible
420
- const workerArgs = existsSync(path.join(cwd, 'bin/zin.ts'))
455
+ const workerArgs = existsSync(path.join(context.projectRoot, 'bin/zin.ts'))
421
456
  ? ['bin/zin.ts', 'worker:start-all']
422
457
  : ['dist/bin/zin.js', 'worker:start-all'];
423
- const workerCommand = existsSync(path.join(cwd, 'bin/zin.ts')) ? 'tsx' : 'node';
458
+ const workerCommand = existsSync(path.join(context.projectRoot, 'bin/zin.ts')) ? 'tsx' : 'node';
424
459
  cmd.info('-------------------------------------------');
425
460
  cmd.info('🔹 [Producer] Web Server starting...');
426
461
  cmd.info('🔸 [Consumer] Worker Process starting...');
@@ -428,12 +463,14 @@ const executeSplitStart = async (cmd, cwd, _options) => {
428
463
  const pProducer = SpawnUtil.spawnAndWait({
429
464
  command: webDev.command,
430
465
  args: webDev.args,
466
+ cwd: context.cwd,
431
467
  env: producerEnv,
432
468
  forwardSignals: true,
433
469
  });
434
470
  const pConsumer = SpawnUtil.spawnAndWait({
435
471
  command: workerCommand,
436
472
  args: workerArgs,
473
+ cwd: context.projectRoot,
437
474
  env: consumerEnv,
438
475
  forwardSignals: true,
439
476
  });
@@ -441,6 +478,7 @@ const executeSplitStart = async (cmd, cwd, _options) => {
441
478
  };
442
479
  const executeStart = async (options, cmd) => {
443
480
  const cwd = process.cwd();
481
+ const context = resolveStartContext(cwd);
444
482
  EnvFileLoader.ensureLoaded();
445
483
  const mode = resolveMode(options);
446
484
  const port = resolvePort(options);
@@ -454,7 +492,7 @@ const executeStart = async (options, cmd) => {
454
492
  if (variant === 'lambda')
455
493
  effectiveRuntime = 'lambda';
456
494
  if (mode === 'split') {
457
- await executeSplitStart(cmd, cwd, options);
495
+ await executeSplitStart(cmd, context, options);
458
496
  return;
459
497
  }
460
498
  assertCompatibleStartVariant(variant, configuredRuntime);
@@ -469,7 +507,7 @@ const executeStart = async (options, cmd) => {
469
507
  const wranglerConfig = typeof options.wranglerConfig === 'string' && options.wranglerConfig.trim() !== ''
470
508
  ? options.wranglerConfig.trim()
471
509
  : undefined;
472
- await executeWranglerStart(cmd, cwd, port, runtime, envName === '' ? undefined : envName, wranglerConfig);
510
+ await executeWranglerStart(cmd, context, port, runtime, envName === '' ? undefined : envName, wranglerConfig);
473
511
  return;
474
512
  }
475
513
  if (envName !== '') {
@@ -477,14 +515,14 @@ const executeStart = async (options, cmd) => {
477
515
  }
478
516
  const watchEnabled = resolveWatchPreference(options, mode);
479
517
  if (variant === 'deno') {
480
- await executeDenoStart(cmd, cwd, mode, watchEnabled, port, runtime);
518
+ await executeDenoStart(cmd, context, mode, watchEnabled, port, runtime);
481
519
  return;
482
520
  }
483
521
  if (variant === 'lambda') {
484
- await executeLambdaStart(cmd, cwd, mode, watchEnabled, port, runtime);
522
+ await executeLambdaStart(cmd, context, mode, watchEnabled, port, runtime);
485
523
  return;
486
524
  }
487
- await executeNodeStart(cmd, cwd, mode, watchEnabled, port);
525
+ await executeNodeStart(cmd, context, mode, watchEnabled, port);
488
526
  };
489
527
  export const StartCommand = Object.freeze({
490
528
  create() {
@@ -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;AAED;;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;AAobD,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;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;AA8bD,eAAO,MAAM,iBAAiB;;;;EAI5B,CAAC"}
@@ -5,6 +5,10 @@
5
5
  import { FileGenerator } from '../scaffolding/FileGenerator.js';
6
6
  import { Logger } from '../../config/logger.js';
7
7
  import * as path from '../../node-singletons/path.js';
8
+ const coreModuleSpecifier = ['@zintrust', 'core'].join('/');
9
+ const coreStartModuleSpecifier = `${coreModuleSpecifier}/start`;
10
+ const serviceManifestImportExpression = "import('./bootstrap/service-manifest.ts').catch(() => import('./bootstrap/service-manifest.js'))";
11
+ const buildRouteImportExpression = (domain, serviceName) => `import('../services/${domain}/${serviceName}/routes/api.ts').catch(() => import('../services/${domain}/${serviceName}/routes/api.js'))`;
8
12
  /**
9
13
  * ServiceScaffolder generates microservices with all necessary files
10
14
  */
@@ -91,11 +95,12 @@ export function scaffold(projectRoot, options) {
91
95
  function ensureProjectRuntimeFiles(projectRoot, options) {
92
96
  const domain = options.domain ?? 'default';
93
97
  const serviceId = `${domain}/${options.name}`;
98
+ const routeImportExpression = buildRouteImportExpression(domain, options.name);
94
99
  const bootstrapDir = path.join(projectRoot, 'src', 'bootstrap');
95
100
  FileGenerator.createDirectory(bootstrapDir);
96
101
  const manifestPath = path.join(bootstrapDir, 'service-manifest.ts');
97
102
  if (!FileGenerator.fileExists(manifestPath)) {
98
- const initialManifest = `import type { ServiceManifestEntry } from '../../index.js';
103
+ const initialManifest = `import type { ServiceManifestEntry } from '${coreModuleSpecifier}';
99
104
 
100
105
  export const serviceManifest: ReadonlyArray<ServiceManifestEntry> = [
101
106
  {
@@ -104,7 +109,7 @@ export const serviceManifest: ReadonlyArray<ServiceManifestEntry> = [
104
109
  name: '${options.name}',
105
110
  port: ${options.port ?? 3001},
106
111
  monolithEnabled: true,
107
- loadRoutes: async () => import('../services/${domain}/${options.name}/routes/api'),
112
+ loadRoutes: async () => ${routeImportExpression},
108
113
  },
109
114
  ];
110
115
 
@@ -112,7 +117,9 @@ export default serviceManifest;
112
117
  `;
113
118
  FileGenerator.writeFile(manifestPath, initialManifest);
114
119
  }
115
- const runtimeModule = `import serviceManifest from './bootstrap/service-manifest';
120
+ const runtimeModule = `const serviceManifestModule = await ${serviceManifestImportExpression};
121
+
122
+ const serviceManifest = serviceManifestModule.default ?? serviceManifestModule.serviceManifest ?? [];
116
123
 
117
124
  export { serviceManifest };
118
125
 
@@ -128,6 +135,7 @@ export default Object.freeze({ serviceManifest });
128
135
  function updateServiceManifest(projectRoot, options) {
129
136
  const domain = options.domain ?? 'default';
130
137
  const serviceId = `${domain}/${options.name}`;
138
+ const routeImportExpression = buildRouteImportExpression(domain, options.name);
131
139
  const manifestPath = path.join(projectRoot, 'src', 'bootstrap', 'service-manifest.ts');
132
140
  if (!FileGenerator.fileExists(manifestPath))
133
141
  return;
@@ -141,7 +149,7 @@ function updateServiceManifest(projectRoot, options) {
141
149
  name: '${options.name}',
142
150
  port: ${options.port ?? 3001},
143
151
  monolithEnabled: true,
144
- loadRoutes: async () => import('../services/${domain}/${options.name}/routes/api'),
152
+ loadRoutes: async () => ${routeImportExpression},
145
153
  },
146
154
  `;
147
155
  const marker = '];';
@@ -234,7 +242,7 @@ function generateServiceIndex(options) {
234
242
  * Auth: ${options.auth ?? 'api-key'}
235
243
  */
236
244
 
237
- import { bootStandaloneService } from '../../start.js';
245
+ import { bootStandaloneService } from '${coreStartModuleSpecifier}';
238
246
 
239
247
  await bootStandaloneService(import.meta.url, {
240
248
  id: '${serviceId}',
@@ -244,7 +252,7 @@ await bootStandaloneService(import.meta.url, {
244
252
  });
245
253
 
246
254
  // Cloudflare Workers entry.
247
- export { default } from '../../start.js';
255
+ export { default } from '${coreStartModuleSpecifier}';
248
256
  `;
249
257
  }
250
258
  /**
@@ -255,7 +263,7 @@ function generateServiceRoutes(options) {
255
263
  * ${options.name} Service Routes
256
264
  */
257
265
 
258
- import { Router, type IRequest, type IResponse, type IRouter } from '../../index.js';
266
+ import { Router, type IRequest, type IResponse, type IRouter } from '${coreModuleSpecifier}';
259
267
 
260
268
  export function registerRoutes(router: IRouter): void {
261
269
  // Example route
@@ -326,8 +334,7 @@ function generateExampleController(options) {
326
334
  * Example Controller for ${options.name} Service
327
335
  */
328
336
 
329
- import { type IRequest, type IResponse, Controller } from '../../index.js';
330
-
337
+ import { type IRequest, type IResponse, Controller } from '${coreModuleSpecifier}';
331
338
  const controller = Object.freeze({
332
339
  ...Controller,
333
340
 
@@ -389,7 +396,7 @@ function generateExampleModel(options) {
389
396
  * Example Model for ${options.name} Service
390
397
  */
391
398
 
392
- import { Model } from '../../index.js';
399
+ import { Model } from '${coreModuleSpecifier}';
393
400
 
394
401
  export const Example = Model.define({
395
402
  table: '${options.name}',
@@ -447,6 +454,8 @@ function getServiceConfig(options) {
447
454
  */
448
455
  function generateServiceReadme(options) {
449
456
  const config = getServiceConfig(options);
457
+ const serviceDir = `src/services/${config.domain}/${options.name}`;
458
+ const serviceId = `${config.domain}/${options.name}`;
450
459
  return `# ${options.name} Service
451
460
 
452
461
  Microservice for ${config.domain} domain.
@@ -460,14 +469,19 @@ Microservice for ${config.domain} domain.
460
469
  ## Getting Started
461
470
 
462
471
  \`\`\`bash
463
- # Start service
464
- npm start
472
+ # Start this service from its service directory (Node)
473
+ cd ${serviceDir}
474
+ zin s
475
+
476
+ # Start this service with Cloudflare Workers dev
477
+ cd ${serviceDir}
478
+ zin s --wg
479
+
480
+ # List this service routes from the project root
481
+ MICROSERVICES=true SERVICES=${serviceId} zin routes
465
482
 
466
483
  # Run tests
467
484
  npm test
468
-
469
- # Run migrations
470
- npm run migrate
471
485
  \`\`\`
472
486
 
473
487
  ## Environment Variables
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.9
2
+ * @zintrust/core v0.4.10
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-23T11:19:34.232Z
8
+ * Built: 2026-03-23T12:53:44.665Z
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-23T11:19:34.198Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-23T12:53:44.591Z'; // 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';
@@ -1 +1 @@
1
- {"version":3,"file":"OverrideValueMerge.d.ts","sourceRoot":"","sources":["../../../src/runtime/OverrideValueMerge.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,GAAI,MAAM,OAAO,EAAE,UAAU,OAAO,KAAG,OAYtE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"OverrideValueMerge.d.ts","sourceRoot":"","sources":["../../../src/runtime/OverrideValueMerge.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,mBAAmB,GAAI,MAAM,OAAO,EAAE,UAAU,OAAO,KAAG,OAatE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -6,7 +6,8 @@ export const mergeOverrideValues = (base, override) => {
6
6
  const merged = { ...base };
7
7
  for (const [key, value] of Object.entries(override)) {
8
8
  const current = merged[key];
9
- merged[key] = isObject(current) && isObject(value) ? mergeOverrideValues(current, value) : value;
9
+ merged[key] =
10
+ isObject(current) && isObject(value) ? mergeOverrideValues(current, value) : value;
10
11
  }
11
12
  return merged;
12
13
  };
@@ -1,4 +1,8 @@
1
- import serviceManifest from './bootstrap/service-manifest';
1
+ const serviceManifestModule = await import('./bootstrap/service-manifest.ts').catch(() =>
2
+ import('./bootstrap/service-manifest.js')
3
+ );
4
+
5
+ const serviceManifest = serviceManifestModule.default ?? serviceManifestModule.serviceManifest ?? [];
2
6
 
3
7
  export { serviceManifest };
4
8
 
@@ -1,4 +1,8 @@
1
- import serviceManifest from './bootstrap/service-manifest';
1
+ const serviceManifestModule = await import('./bootstrap/service-manifest.ts').catch(() =>
2
+ import('./bootstrap/service-manifest.js')
3
+ );
4
+
5
+ const serviceManifest = serviceManifestModule.default ?? serviceManifestModule.serviceManifest ?? [];
2
6
 
3
7
  export { serviceManifest };
4
8