@zintrust/core 0.4.15 → 0.4.16

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.15",
3
+ "version": "0.4.16",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -32,6 +32,9 @@ interface ControllerPromptAnswers {
32
32
  interface RoutesPromptAnswers {
33
33
  name: string;
34
34
  }
35
+ interface MiddlewarePromptAnswers {
36
+ name: string;
37
+ }
35
38
  interface FactoryPromptAnswers {
36
39
  name: string;
37
40
  model: string;
@@ -80,6 +83,7 @@ export declare const AddCommand: Readonly<{
80
83
  promptModelConfig: () => Promise<ModelPromptAnswers>;
81
84
  promptControllerConfig: () => Promise<ControllerPromptAnswers>;
82
85
  promptRoutesConfig: () => Promise<RoutesPromptAnswers>;
86
+ promptMiddlewareConfig: () => Promise<MiddlewarePromptAnswers>;
83
87
  promptFactoryConfig: () => Promise<FactoryPromptAnswers>;
84
88
  promptSeederConfig: () => Promise<SeederPromptAnswers>;
85
89
  promptRequestFactoryConfig: () => Promise<RequestFactoryPromptAnswers>;
@@ -1 +1 @@
1
- {"version":3,"file":"AddCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/AddCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAarE,OAAO,KAAK,EAEV,aAAa,EACd,MAAM,2CAA2C,CAAC;AAwCnD,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAChC,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;CAC7C;AAED,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,oBAAoB;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,2BAA2B;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,UAAU,4BAA4B;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CAClB;AAsmCD;;GAEG;AACH,eAAO,MAAM,UAAU;IACrB;;OAEG;cACO,YAAY;IAWtB;;;OAGG;;2CAnlC2C,MAAM,KAAG,OAAO,CAAC,oBAAoB,CAAC;mCA2EhD,OAAO,CAAC,oBAAoB,CAAC;qCA8D3B,OAAO,CAAC,sBAAsB,CAAC;iCAwGnC,OAAO,CAAC,kBAAkB,CAAC;sCA4DtB,OAAO,CAAC,uBAAuB,CAAC;kCAwDpC,OAAO,CAAC,mBAAmB,CAAC;mCAkE3B,OAAO,CAAC,oBAAoB,CAAC;kCA8I9B,OAAO,CAAC,mBAAmB,CAAC;0CAiHpB,OAAO,CAAC,2BAA2B,CAAC;yCAwFrC,OAAO,CAAC,MAAM,CAAC;2DAkBpC,MAAM,KAC1B,OAAO,CAAC,4BAA4B,CAAC;4CAoHzB,MAAM,KAClB,OAAO,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,GAAG,KAAK,CAAA;SAAE,CAAC;iDAlE5C,MAAM,KAAG,aAAa,EAAE;;EA+RtE,CAAC"}
1
+ {"version":3,"file":"AddCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/AddCommand.ts"],"names":[],"mappings":"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"}
@@ -24,8 +24,8 @@ import { PluginManager } from '../../runtime/PluginManager.js';
24
24
  import inquirer from 'inquirer';
25
25
  const addOptions = (command) => {
26
26
  command
27
- .argument('<type>', 'What to add: service, feature, migration, model, controller, routes, factory, seeder, requestfactory, responsefactory, workflow, or governance')
28
- .argument('[name]', 'Name of service/feature/migration/model/controller/factory/seeder/requestfactory/responsefactory/workflow (governance takes no name)')
27
+ .argument('<type>', 'What to add: service, feature, migration, model, controller, routes, middleware, factory, seeder, requestfactory, responsefactory, workflow, or governance')
28
+ .argument('[name]', 'Name of service/feature/migration/model/controller/middleware/factory/seeder/requestfactory/responsefactory/workflow (governance takes no name)')
29
29
  .option('--package-manager <pm>', 'Specify package manager to use when installing plugins (npm|yarn|pnpm)')
30
30
  .option('--domain <name>', 'Service domain (e.g., ecommerce, payments) - for services')
31
31
  .option('--database <type>', 'Database (shared|isolated) - for services')
@@ -369,6 +369,119 @@ const addRoutes = async (cmd, routeName, opts) => {
369
369
  cmd.info(`File: ${path.basename(result.routeFile)}`);
370
370
  cmd.info(`\nNext steps:\n • Add route definitions\n • Import controllers\n • Register in main router`);
371
371
  };
372
+ const promptMiddlewareConfig = async () => {
373
+ return inquirer.prompt([
374
+ {
375
+ type: 'input',
376
+ name: 'name',
377
+ message: 'Middleware name (PascalCase, e.g., AuthMiddleware):',
378
+ validate: (value) => {
379
+ return /^[A-Z][a-zA-Z\d]*Middleware$/.test(value)
380
+ ? true
381
+ : 'Must be PascalCase ending with "Middleware"';
382
+ },
383
+ },
384
+ ]);
385
+ };
386
+ const buildMiddlewareKey = (middlewareName) => {
387
+ const withoutSuffix = middlewareName.replace(/Middleware$/, '');
388
+ return `${CommonUtils.camelCase(withoutSuffix)}Middleware`;
389
+ };
390
+ const buildMiddlewareSource = (middlewareName) => {
391
+ return `import type { Middleware } from '../../index.js';
392
+
393
+ export const ${middlewareName}: Middleware = async (_req, _res, next) => {
394
+ await next();
395
+ };
396
+ `;
397
+ };
398
+ const registerMiddlewareImport = (configSource, middlewareName) => {
399
+ const importLine = `import { ${middlewareName} } from '@app/Middleware/${middlewareName}';`;
400
+ if (configSource.includes(importLine))
401
+ return configSource;
402
+ const lines = configSource.split('\n');
403
+ let insertAt = -1;
404
+ for (let index = 0; index < lines.length; index += 1) {
405
+ if (lines[index]?.startsWith('import '))
406
+ insertAt = index;
407
+ }
408
+ if (insertAt === -1) {
409
+ return `${importLine}\n${configSource}`;
410
+ }
411
+ lines.splice(insertAt + 1, 0, importLine);
412
+ return lines.join('\n');
413
+ };
414
+ const registerMiddlewareRouteKey = (configSource, middlewareName, middlewareKey) => {
415
+ if (configSource.includes(`${middlewareKey}: ${middlewareName}`)) {
416
+ return { content: configSource, updated: true };
417
+ }
418
+ const routeBlockPattern = /route:\s*\{([\s\S]*?)\n\s*\},/;
419
+ if (routeBlockPattern.test(configSource)) {
420
+ return {
421
+ content: configSource.replace(routeBlockPattern, (_match, inner) => {
422
+ const prefix = inner.trim() === '' ? '' : inner.replace(/\s*$/, '');
423
+ const nextInner = prefix === ''
424
+ ? `\n ${middlewareKey}: ${middlewareName},`
425
+ : `${prefix}\n ${middlewareKey}: ${middlewareName},`;
426
+ return `route: {${nextInner}\n },`;
427
+ }),
428
+ updated: true,
429
+ };
430
+ }
431
+ const closingPattern = /\n\} as MiddlewaresType;\s*$/;
432
+ if (!closingPattern.test(configSource)) {
433
+ return { content: configSource, updated: false };
434
+ }
435
+ const inserted = configSource.replace(closingPattern, `\n global: [],\n route: {\n ${middlewareKey}: ${middlewareName},\n },\n} as MiddlewaresType;\n`);
436
+ return { content: inserted, updated: true };
437
+ };
438
+ const printManualMiddlewareRegistrationSnippet = (cmd, middlewareName, middlewareKey) => {
439
+ cmd.warn('Could not update config/middleware.ts automatically. Add this manually:');
440
+ cmd.info(`import { ${middlewareName} } from '@app/Middleware/${middlewareName}';`);
441
+ cmd.info(`route: { ${middlewareKey}: ${middlewareName} }`);
442
+ cmd.info(`Route typing example: type AppMiddlewareKey = MiddlewareKey | '${middlewareKey}';`);
443
+ };
444
+ const addMiddleware = async (cmd, middlewareName, opts) => {
445
+ const projectRoot = process.cwd();
446
+ let name = middlewareName ?? '';
447
+ if (name === '' && opts.noInteractive !== true) {
448
+ const answers = await promptMiddlewareConfig();
449
+ name = answers.name;
450
+ }
451
+ else if (name === '') {
452
+ throw ErrorFactory.createValidationError('Middleware name is required');
453
+ }
454
+ if (!/^[A-Z][a-zA-Z\d]*Middleware$/.test(name)) {
455
+ throw ErrorFactory.createValidationError('Middleware name must be PascalCase ending with "Middleware"');
456
+ }
457
+ const middlewareDir = path.join(projectRoot, 'app', 'Middleware');
458
+ const middlewarePath = path.join(middlewareDir, `${name}.ts`);
459
+ const configPath = path.join(projectRoot, 'config', 'middleware.ts');
460
+ const middlewareKey = buildMiddlewareKey(name);
461
+ ensureDirectoryExists(middlewareDir);
462
+ const created = FileGenerator.writeFile(middlewarePath, buildMiddlewareSource(name), {
463
+ overwrite: false,
464
+ });
465
+ if (!FileGenerator.fileExists(configPath)) {
466
+ cmd.success(`Middleware '${name}' created successfully!`);
467
+ cmd.warn('config/middleware.ts was not found, so registration was skipped.');
468
+ printManualMiddlewareRegistrationSnippet(cmd, name, middlewareKey);
469
+ return;
470
+ }
471
+ const currentConfig = FileGenerator.readFile(configPath);
472
+ const withImport = registerMiddlewareImport(currentConfig, name);
473
+ const registered = registerMiddlewareRouteKey(withImport, name, middlewareKey);
474
+ if (!registered.updated) {
475
+ cmd.success(`Middleware '${name}' created successfully!`);
476
+ printManualMiddlewareRegistrationSnippet(cmd, name, middlewareKey);
477
+ return;
478
+ }
479
+ fs.writeFileSync(configPath, registered.content, 'utf-8');
480
+ cmd.success(`Middleware '${name}' ${created ? 'created' : 'updated'} successfully!`);
481
+ cmd.info(`File: ${path.basename(middlewarePath)}`);
482
+ cmd.info(`Registered route key: ${middlewareKey}`);
483
+ cmd.info(`\nNext steps:\n • Use '${middlewareKey}' in route metadata\n • If your route file uses MiddlewareKey, extend it locally: type AppMiddlewareKey = MiddlewareKey | '${middlewareKey}'`);
484
+ };
372
485
  const getFactoryInitialConfig = (factoryName, opts) => {
373
486
  return {
374
487
  name: factoryName ?? '',
@@ -833,6 +946,7 @@ const TYPE_HANDLERS = {
833
946
  model: addModel,
834
947
  controller: addController,
835
948
  routes: addRoutes,
949
+ middleware: addMiddleware,
836
950
  factory: addFactory,
837
951
  seeder: addSeeder,
838
952
  requestfactory: addRequestFactory,
@@ -845,7 +959,7 @@ const TYPE_HANDLERS = {
845
959
  const handleType = async (cmd, type, name, opts) => {
846
960
  const handler = TYPE_HANDLERS[type];
847
961
  if (handler === undefined) {
848
- throw ErrorFactory.createCliError(`Unknown type "${type}". Use: service, feature, migration, model, controller, routes, factory, seeder, requestfactory, responsefactory, workflow, or governance`);
962
+ throw ErrorFactory.createCliError(`Unknown type "${type}". Use: service, feature, migration, model, controller, routes, middleware, factory, seeder, requestfactory, responsefactory, workflow, or governance`);
849
963
  }
850
964
  await handler(cmd, name, opts);
851
965
  };
@@ -874,7 +988,7 @@ const executeAdd = async (cmd, options) => {
874
988
  const addOpts = { ...options, ...commandOpts };
875
989
  try {
876
990
  if (type === undefined || type === '') {
877
- throw ErrorFactory.createCliError('Please specify what to add: service, feature, migration, model, controller, routes, factory, or seeder');
991
+ throw ErrorFactory.createCliError('Please specify what to add: service, feature, migration, model, controller, routes, middleware, factory, or seeder');
878
992
  }
879
993
  // Planned modular adapter syntax: `zin add db:sqlite` (delegate to plugin installer)
880
994
  const normalizedType = type.toLowerCase();
@@ -921,6 +1035,7 @@ export const AddCommand = Object.freeze({
921
1035
  promptModelConfig,
922
1036
  promptControllerConfig,
923
1037
  promptRoutesConfig,
1038
+ promptMiddlewareConfig,
924
1039
  promptFactoryConfig,
925
1040
  promptSeederConfig,
926
1041
  promptRequestFactoryConfig,
@@ -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;AA21BvF,eAAO,MAAM,YAAY;cACb,YAAY;EAmCtB,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;AAs4BvF,eAAO,MAAM,YAAY;cACb,YAAY;EAmCtB,CAAC"}
@@ -2,9 +2,9 @@ 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';
6
5
  import { readEnvString } from '../../common/ExternalServiceUtils.js';
7
6
  import * as Common from '../../common/index.js';
7
+ import { generateUuid } from '../../common/utility.js';
8
8
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
9
9
  import { isNonEmptyString } from '../../helper/index.js';
10
10
  import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from '../../node-singletons/fs.js';
@@ -418,6 +418,7 @@ const executeWranglerStart = async (cmd, context, port, runtime, envName, wrangl
418
418
  if (configPath === undefined && entry === undefined) {
419
419
  throw ErrorFactory.createCliError("Error: wrangler config not found (wrangler.toml/json). Run 'wrangler init' first.");
420
420
  }
421
+ warnOnUnsafeWranglerBootstrap(cmd, context.cwd, entry);
421
422
  const wranglerArgs = ['dev'];
422
423
  if (normalizedConfig !== '') {
423
424
  wranglerArgs.push('--config', normalizedConfig);
@@ -434,14 +435,42 @@ const executeWranglerStart = async (cmd, context, port, runtime, envName, wrangl
434
435
  logMySqlProxyHint(cmd);
435
436
  cmd.info('Starting in Wrangler dev mode...');
436
437
  const exitCode = await withWranglerEnvSnapshot(context.cwd, envName, async () => {
438
+ const startEnv = {
439
+ ...buildStartEnv(context.projectRoot),
440
+ WORKER_ENABLED: 'false',
441
+ CLOUDFLARE_WORKER: 'true',
442
+ DOCKER_WORKER: 'false',
443
+ };
437
444
  return SpawnUtil.spawnAndWait({
438
445
  command: 'wrangler',
439
446
  args: wranglerArgs,
440
- env: buildStartEnv(context.projectRoot),
447
+ env: startEnv,
441
448
  });
442
449
  });
443
450
  process.exit(exitCode);
444
451
  };
452
+ const isUnsafeWranglerBootstrapSource = (source) => {
453
+ const getKernelIndex = source.indexOf('getKernel(');
454
+ const cloudflareFetchIndex = source.indexOf('cloudflareWorker.fetch');
455
+ return (getKernelIndex !== -1 && cloudflareFetchIndex !== -1 && getKernelIndex < cloudflareFetchIndex);
456
+ };
457
+ const warnOnUnsafeWranglerBootstrap = (cmd, cwd, entry) => {
458
+ if (entry === undefined)
459
+ return;
460
+ const entryPath = path.join(cwd, entry);
461
+ if (!existsSync(entryPath))
462
+ return;
463
+ try {
464
+ const source = readFileSync(entryPath, 'utf-8');
465
+ if (!isUnsafeWranglerBootstrapSource(source))
466
+ return;
467
+ cmd.warn(`Unsafe Worker bootstrap detected in ${entry}: getKernel() runs before the core Cloudflare handler initializes Worker bindings.`);
468
+ cmd.warn('Use `export { default } from "../../start.js"` and keep custom middleware registration in config/middleware.ts or route metadata.');
469
+ }
470
+ catch {
471
+ // Best-effort warning only.
472
+ }
473
+ };
445
474
  const ensureTmpRunnerFile = (cwd, filename, content) => {
446
475
  const tmpDir = path.join(cwd, 'tmp');
447
476
  try {
@@ -1,4 +1,5 @@
1
1
  import type { MiddlewareConfigType } from './type';
2
+ import type { Middleware } from '../middleware/MiddlewareStack';
2
3
  export declare const MiddlewareBody: {
3
4
  readonly email: "email";
4
5
  readonly password: "password";
@@ -6,7 +7,7 @@ export declare const MiddlewareBody: {
6
7
  readonly count: "count";
7
8
  };
8
9
  export type MiddlewaresType = {
9
- skipPaths: string[];
10
+ skipPaths: ReadonlyArray<string>;
10
11
  fillRateLimit: {
11
12
  windowMs: number;
12
13
  max: number;
@@ -22,6 +23,8 @@ export type MiddlewaresType = {
22
23
  max: number;
23
24
  message: string;
24
25
  };
26
+ global?: ReadonlyArray<Middleware>;
27
+ route?: Record<string, Middleware>;
25
28
  };
26
29
  export declare const MiddlewareKeys: Readonly<{
27
30
  log: true;
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAiEzD,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,qBAAqB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAkBuB,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,cAAc,CAAC;AA2MxD,wBAAgB,sBAAsB,IAAI,oBAAoB,CAkC7D;AAID;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,IAE7C,CAAC;AAwBF,eAAO,MAAM,gBAAgB,EAAE,oBAY7B,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAwD9D,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,qBAAqB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,MAAM,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAkBuB,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,cAAc,CAAC;AAoOxD,wBAAgB,sBAAsB,IAAI,oBAAoB,CAwC7D;AAID;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,IAE7C,CAAC;AAwBF,eAAO,MAAM,gBAAgB,EAAE,oBAY7B,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { Env } from './env.js';
2
+ import { isArray, isObject } from '../helper/index.js';
2
3
  import { bodyParsingMiddleware } from '../http/middleware/BodyParsingMiddleware.js';
3
4
  import { fileUploadMiddleware } from '../http/middleware/FileUploadMiddleware.js';
4
5
  import { AuthMiddleware } from '../middleware/AuthMiddleware.js';
@@ -157,6 +158,22 @@ function createSharedMiddlewares(loadMiddlewareConfig) {
157
158
  ...validations,
158
159
  });
159
160
  }
161
+ const resolveProjectGlobalMiddlewares = (loadMiddlewareConfig) => {
162
+ if (!isArray(loadMiddlewareConfig.global))
163
+ return [];
164
+ return loadMiddlewareConfig.global.filter((middleware) => {
165
+ return typeof middleware === 'function';
166
+ });
167
+ };
168
+ const resolveProjectRouteMiddlewares = (loadMiddlewareConfig) => {
169
+ if (!isObject(loadMiddlewareConfig.route))
170
+ return {};
171
+ const entries = Object.entries(loadMiddlewareConfig.route).filter((entry) => {
172
+ const [name, middleware] = entry;
173
+ return typeof name === 'string' && name.trim() !== '' && typeof middleware === 'function';
174
+ });
175
+ return Object.fromEntries(entries);
176
+ };
160
177
  export function createMiddlewareConfig() {
161
178
  const loadMiddlewareConfig = StartupConfigFileRegistry.get(StartupConfigFile.Middleware) ?? {};
162
179
  const skipPathsFromEnv = Env.get('CSRF_SKIP_PATHS', '')
@@ -170,6 +187,8 @@ export function createMiddlewareConfig() {
170
187
  : skipPathsFromEnv,
171
188
  };
172
189
  const shared = createSharedMiddlewares(effectiveMiddlewareConfig);
190
+ const projectGlobal = resolveProjectGlobalMiddlewares(effectiveMiddlewareConfig);
191
+ const projectRoute = resolveProjectRouteMiddlewares(effectiveMiddlewareConfig);
173
192
  const middlewareConfigObj = {
174
193
  global: [
175
194
  shared.log,
@@ -180,8 +199,12 @@ export function createMiddlewareConfig() {
180
199
  bodyParsingMiddleware,
181
200
  shared.csrf,
182
201
  shared.sanitizeBody,
202
+ ...projectGlobal,
183
203
  ],
184
- route: shared,
204
+ route: {
205
+ ...shared,
206
+ ...projectRoute,
207
+ },
185
208
  };
186
209
  return Object.freeze(middlewareConfigObj);
187
210
  }
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.15
2
+ * @zintrust/core v0.4.16
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-24T09:07:10.631Z
8
+ * Built: 2026-03-24T13:49:16.905Z
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-24T09:07:10.598Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-24T13:49:16.870Z'; // 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';
@@ -17,7 +17,7 @@ export interface CsrfOptions {
17
17
  * - `/api/*`
18
18
  * - `/api/v1/auth/login`
19
19
  */
20
- skipPaths?: string[];
20
+ skipPaths?: ReadonlyArray<string>;
21
21
  }
22
22
  export declare const CsrfMiddleware: Readonly<{
23
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"CsrfMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/CsrfMiddleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAK9D,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAkDD,eAAO,MAAM,cAAc;IACzB;;OAEG;qBACa,WAAW,GAAQ,UAAU;EA6D7C,CAAC"}
1
+ {"version":3,"file":"CsrfMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/CsrfMiddleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAK9D,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACnC;AAkDD,eAAO,MAAM,cAAc;IACzB;;OAEG;qBACa,WAAW,GAAQ,UAAU;EA6D7C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"WorkersModule.d.ts","sourceRoot":"","sources":["../../../src/runtime/WorkersModule.ts"],"names":[],"mappings":"AAOA,KAAK,aAAa,GAAG,cAAc,mBAAmB,CAAC,CAAC;AACxD,KAAK,kBAAkB,GAAG,cAAc,yBAAyB,CAAC,CAAC;AAmUnE,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,aAAa,CAwB/D,CAAC;AA4CF,eAAO,MAAM,sBAAsB,QAAa,OAAO,CAAC,kBAAkB,CAkBzE,CAAC"}
1
+ {"version":3,"file":"WorkersModule.d.ts","sourceRoot":"","sources":["../../../src/runtime/WorkersModule.ts"],"names":[],"mappings":"AAOA,KAAK,aAAa,GAAG,cAAc,mBAAmB,CAAC,CAAC;AACxD,KAAK,kBAAkB,GAAG,cAAc,yBAAyB,CAAC,CAAC;AA4WnE,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,aAAa,CAwB/D,CAAC;AA4CF,eAAO,MAAM,sBAAsB,QAAa,OAAO,CAAC,kBAAkB,CAkBzE,CAAC"}
@@ -237,12 +237,35 @@ const applyInitialPatches = () => {
237
237
  Logger.warn('Rewrote @zintrust/queue-monitor ESM specifiers before import', monitorPatch);
238
238
  }
239
239
  };
240
+ const getErrorChain = (error) => {
241
+ const chain = [];
242
+ const seen = new Set();
243
+ let current = error;
244
+ while (current !== null && current !== undefined && !seen.has(current)) {
245
+ seen.add(current);
246
+ const entry = current;
247
+ chain.push({
248
+ message: typeof entry.message === 'string' && entry.message !== '' ? entry.message : String(current),
249
+ code: typeof entry.code === 'string' ? entry.code : undefined,
250
+ });
251
+ current = entry.cause;
252
+ }
253
+ return chain;
254
+ };
240
255
  const shouldRetryAfterFailure = (error) => {
241
256
  if (patchAfterFailureAttempted)
242
257
  return false;
243
- const message = error instanceof Error ? error.message : String(error);
244
- const code = error?.code;
245
- return code === 'ERR_MODULE_NOT_FOUND' && message.includes('@zintrust/workers');
258
+ return getErrorChain(error).some(({ message, code }) => code === 'ERR_MODULE_NOT_FOUND' && message.includes('@zintrust/workers'));
259
+ };
260
+ const isMissingOptionalWorkersModuleError = (error) => {
261
+ return getErrorChain(error).some(({ message, code }) => {
262
+ if (!message.includes('@zintrust/workers'))
263
+ return false;
264
+ return (code === 'ERR_MODULE_NOT_FOUND' ||
265
+ message.includes('ERR_MODULE_NOT_FOUND') ||
266
+ message.includes('Cannot find package') ||
267
+ message.includes('No such module'));
268
+ });
246
269
  };
247
270
  const handleImportFailure = async (error) => {
248
271
  if (shouldRetryAfterFailure(error)) {
@@ -254,7 +277,12 @@ const handleImportFailure = async (error) => {
254
277
  replacements,
255
278
  });
256
279
  workersModulePromise = importOptionalPackage('@zintrust/workers');
257
- return workersModulePromise;
280
+ try {
281
+ return await workersModulePromise;
282
+ }
283
+ catch (retryError) {
284
+ error = retryError;
285
+ }
258
286
  }
259
287
  }
260
288
  const localFallback = await importLocalWorkersModule();
@@ -262,6 +290,11 @@ const handleImportFailure = async (error) => {
262
290
  workersModulePromise = Promise.resolve(localFallback);
263
291
  return localFallback;
264
292
  }
293
+ if (isMissingOptionalWorkersModuleError(error)) {
294
+ Logger.info('Optional @zintrust/workers package is unavailable; worker routes are disabled.');
295
+ workersModulePromise = Promise.resolve(createDisabledWorkersModule());
296
+ return workersModulePromise;
297
+ }
265
298
  throw error;
266
299
  };
267
300
  const tryLocalFallback = async () => {
@@ -1,14 +1,21 @@
1
1
  // @ts-ignore - config templates are excluded from the main TS project in this repo
2
+ import { Env } from '@zintrust/core';
3
+ import type { MiddlewaresType } from '@zintrust/core';
2
4
  /**
3
5
  * Middleware Configuration (template)
4
6
  *
5
- * Keep this file declarative:
6
- * - Core owns middleware construction / any runtime behavior.
7
- * - Projects can override by editing `middlewareConfigObj`.
7
+ * Full project middleware flow:
8
+ * 1. Create `app/Middleware/YourMiddleware.ts` and export a `Middleware` function.
9
+ * 2. Import it below.
10
+ * 3. Register route middleware under `route` or append global middleware under `global`.
11
+ * 4. Use the route key in `routes/*.ts`.
12
+ *
13
+ * For custom route keys, extend the framework type locally in your route file:
14
+ * `type AppMiddlewareKey = MiddlewareKey | 'yourMiddleware';`
8
15
  */
9
16
 
10
- import { Env } from '@zintrust/core';
11
- import type { MiddlewaresType } from '@zintrust/core';
17
+ // Example custom middleware import:
18
+ // import { AuthMiddleware } from '@app/Middleware/AuthMiddleware';
12
19
 
13
20
  export default {
14
21
  skipPaths: Env.get('CSRF_SKIP_PATHS', '')
@@ -30,4 +37,10 @@ export default {
30
37
  max: 20,
31
38
  message: 'Too many user mutation requests, please try again later.',
32
39
  },
40
+ global: [
41
+ // AuthMiddleware,
42
+ ],
43
+ route: {
44
+ // authMiddleware: AuthMiddleware,
45
+ },
33
46
  } as MiddlewaresType;