@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 +1 -1
- package/src/cli/commands/AddCommand.d.ts +4 -0
- package/src/cli/commands/AddCommand.d.ts.map +1 -1
- package/src/cli/commands/AddCommand.js +119 -4
- package/src/cli/commands/StartCommand.d.ts.map +1 -1
- package/src/cli/commands/StartCommand.js +31 -2
- package/src/config/middleware.d.ts +4 -1
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +24 -1
- package/src/index.js +3 -3
- package/src/middleware/CsrfMiddleware.d.ts +1 -1
- package/src/middleware/CsrfMiddleware.d.ts.map +1 -1
- package/src/runtime/WorkersModule.d.ts.map +1 -1
- package/src/runtime/WorkersModule.js +37 -4
- package/src/templates/project/basic/config/middleware.ts.tpl +18 -5
package/package.json
CHANGED
|
@@ -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;
|
|
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;
|
|
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:
|
|
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;
|
|
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"}
|
package/src/config/middleware.js
CHANGED
|
@@ -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:
|
|
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.
|
|
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-
|
|
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-
|
|
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';
|
|
@@ -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,
|
|
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;
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
|
|
11
|
-
import
|
|
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;
|