@zintrust/core 1.5.2 → 1.5.4

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 (39) hide show
  1. package/bin/z.js +35 -2
  2. package/bin/zin.js +35 -2
  3. package/bin/zintrust-main.d.ts.map +1 -1
  4. package/bin/zintrust-main.js +32 -6
  5. package/bin/zintrust.d.ts.map +1 -1
  6. package/bin/zintrust.js +35 -2
  7. package/bin/zt.js +22 -2
  8. package/package.json +1 -1
  9. package/src/boot/bootstrap.d.ts +1 -1
  10. package/src/boot/bootstrap.d.ts.map +1 -1
  11. package/src/boot/bootstrap.js +14 -9
  12. package/src/cli/commands/StartCommand.d.ts.map +1 -1
  13. package/src/cli/commands/StartCommand.js +1 -0
  14. package/src/cli/utils/spawn.d.ts +1 -0
  15. package/src/cli/utils/spawn.d.ts.map +1 -1
  16. package/src/cli/utils/spawn.js +74 -10
  17. package/src/config/index.d.ts +2 -1
  18. package/src/config/index.d.ts.map +1 -1
  19. package/src/config/index.js +3 -3
  20. package/src/config/queue.d.ts.map +1 -1
  21. package/src/config/queue.js +32 -3
  22. package/src/http/RequestContext.d.ts.map +1 -1
  23. package/src/http/RequestContext.js +6 -3
  24. package/src/index.js +3 -3
  25. package/src/migrations/schema/Schema.d.ts.map +1 -1
  26. package/src/migrations/schema/Schema.js +122 -5
  27. package/src/runtime/PluginManager.d.ts.map +1 -1
  28. package/src/runtime/PluginManager.js +9 -3
  29. package/src/runtime/WorkerAdapterImports.d.ts +2 -2
  30. package/src/runtime/WorkerAdapterImports.js +1 -1
  31. package/src/runtime/plugins/trace-runtime.d.ts.map +1 -1
  32. package/src/runtime/plugins/trace-runtime.js +2 -1
  33. package/src/runtime/plugins/trace.d.ts +1 -0
  34. package/src/runtime/plugins/trace.d.ts.map +1 -1
  35. package/src/runtime/plugins/trace.js +7 -5
  36. package/src/templates/project/basic/config/middleware.ts.tpl +3 -3
  37. package/src/zintrust.plugins.d.ts +6 -3
  38. package/src/zintrust.plugins.d.ts.map +1 -1
  39. package/src/zintrust.plugins.js +6 -3
package/bin/z.js CHANGED
@@ -3,5 +3,38 @@
3
3
  * ZinTrust CLI Shortcut - 'z'
4
4
  * Mirrors bin/zintrust.ts for convenience
5
5
  */
6
- import { run } from './zintrust-main.js';
7
- await run();
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ const here = path.dirname(fileURLToPath(import.meta.url));
12
+ const require = createRequire(import.meta.url);
13
+ const tsTarget = path.join(here, 'zintrust-main.ts');
14
+ const jsTarget = path.join(here, 'zintrust-main.js');
15
+ const target = existsSync(tsTarget) ? tsTarget : jsTarget;
16
+ const tsxImportPath = require.resolve('tsx');
17
+ const nodeArgs = target.endsWith('.ts')
18
+ ? ['--import', tsxImportPath, target, ...process.argv.slice(2)]
19
+ : [target, ...process.argv.slice(2)];
20
+ const child = spawn(process.execPath, nodeArgs, {
21
+ stdio: 'inherit',
22
+ env: process.env,
23
+ });
24
+ const result = await new Promise((resolve, reject) => {
25
+ let settled = false;
26
+ const finalize = (exitCode, signal) => {
27
+ if (settled) {
28
+ return;
29
+ }
30
+ settled = true;
31
+ child.off?.('error', reject);
32
+ child.off?.('exit', finalize);
33
+ child.off?.('close', finalize);
34
+ resolve({ exitCode, signal });
35
+ };
36
+ child.once('error', reject);
37
+ child.once('exit', finalize);
38
+ child.once('close', finalize);
39
+ });
40
+ process.exit(result.exitCode ?? (result.signal === 'SIGINT' || result.signal === 'SIGTERM' ? 0 : 1));
package/bin/zin.js CHANGED
@@ -3,5 +3,38 @@
3
3
  * ZinTrust CLI Shortcut - 'zin'
4
4
  * Mirrors bin/zintrust.ts for convenience
5
5
  */
6
- import { run } from './zintrust-main.js';
7
- await run();
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { createRequire } from 'node:module';
9
+ import path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ const here = path.dirname(fileURLToPath(import.meta.url));
12
+ const require = createRequire(import.meta.url);
13
+ const tsTarget = path.join(here, 'zintrust-main.ts');
14
+ const jsTarget = path.join(here, 'zintrust-main.js');
15
+ const target = existsSync(tsTarget) ? tsTarget : jsTarget;
16
+ const tsxImportPath = require.resolve('tsx');
17
+ const nodeArgs = target.endsWith('.ts')
18
+ ? ['--import', tsxImportPath, target, ...process.argv.slice(2)]
19
+ : [target, ...process.argv.slice(2)];
20
+ const child = spawn(process.execPath, nodeArgs, {
21
+ stdio: 'inherit',
22
+ env: process.env,
23
+ });
24
+ const result = await new Promise((resolve, reject) => {
25
+ let settled = false;
26
+ const finalize = (exitCode, signal) => {
27
+ if (settled) {
28
+ return;
29
+ }
30
+ settled = true;
31
+ child.off?.('error', reject);
32
+ child.off?.('exit', finalize);
33
+ child.off?.('close', finalize);
34
+ resolve({ exitCode, signal });
35
+ };
36
+ child.once('error', reject);
37
+ child.once('exit', finalize);
38
+ child.once('close', finalize);
39
+ });
40
+ process.exit(result.exitCode ?? (result.signal === 'SIGINT' || result.signal === 'SIGTERM' ? 0 : 1));
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust-main.d.ts","sourceRoot":"","sources":["../../bin/zintrust-main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AA0WF,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzC;AAED,eAAO,MAAM,mBAAmB;;qCAzUQ,MAAM,KAAG,qBAAqB,GAAG,SAAS;mCA6C5C,MAAM,GAAG,IAAI,UAAU,MAAM,CAAC,OAAO,GAAG,IAAI,KAAG,MAAM;iCAnEzD,MAAM;8BAIP,MAAM,KAAG,MAAM;uCAsEtC,qBAAqB,WACpB,MAAM,EAAE,KAChB,OAAO,CAAC,KAAK,CAAC;oCAhEsB,MAAM,sBAAsB,MAAM,KAAG,OAAO;6CA2G7B,MAAM,EAAE,KAAG,OAAO,CAAC,OAAO,CAAC;yCAtE1E,MAAM,sBACS,MAAM,QACrB,MAAM,CAAC,UAAU,KACrB,qBAAqB,GAAG,SAAS;EAqTlC,CAAC;AAEH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"zintrust-main.d.ts","sourceRoot":"","sources":["../../bin/zintrust-main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,KAAK,qBAAqB,GAAG;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAiYF,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAMzC;AAED,eAAO,MAAM,mBAAmB;;qCAhWQ,MAAM,KAAG,qBAAqB,GAAG,SAAS;mCA6C5C,MAAM,GAAG,IAAI,UAAU,MAAM,CAAC,OAAO,GAAG,IAAI,KAAG,MAAM;iCAnEzD,MAAM;8BAIP,MAAM,KAAG,MAAM;uCAoGtC,qBAAqB,WACpB,MAAM,EAAE,KAChB,OAAO,CAAC,KAAK,CAAC;oCA9FsB,MAAM,sBAAsB,MAAM,KAAG,OAAO;6CAkI7B,MAAM,EAAE,KAAG,OAAO,CAAC,OAAO,CAAC;yCA7F1E,MAAM,sBACS,MAAM,QACrB,MAAM,CAAC,UAAU,KACrB,qBAAqB,GAAG,SAAS;EA4UlC,CAAC;AAaH,OAAO,EAAE,CAAC"}
@@ -80,6 +80,28 @@ const getHandoffExitCode = (exitCode, signal) => {
80
80
  return 0;
81
81
  return 1;
82
82
  };
83
+ const waitForChildTermination = async (child) => {
84
+ return await new Promise((resolve, reject) => {
85
+ let settled = false;
86
+ const finish = (exitCode, signal) => {
87
+ if (settled)
88
+ return;
89
+ settled = true;
90
+ child.off?.('exit', onExit);
91
+ child.off?.('close', onClose);
92
+ resolve({ exitCode, signal });
93
+ };
94
+ const onExit = (exitCode, signal) => {
95
+ finish(exitCode, signal);
96
+ };
97
+ const onClose = (exitCode, signal) => {
98
+ finish(exitCode, signal);
99
+ };
100
+ child.once('error', reject);
101
+ child.once('exit', onExit);
102
+ child.once('close', onClose);
103
+ });
104
+ };
83
105
  const handoffToProjectLocalCli = async (target, rawArgs) => {
84
106
  const child = spawn(process.execPath, [target.binPath, ...rawArgs], {
85
107
  stdio: 'inherit',
@@ -104,12 +126,7 @@ const handoffToProjectLocalCli = async (target, rawArgs) => {
104
126
  process.on('SIGINT', onSigint);
105
127
  process.on('SIGTERM', onSigterm);
106
128
  try {
107
- const result = await new Promise((resolve, reject) => {
108
- child.once('error', reject);
109
- child.once('close', (exitCode, signal) => {
110
- resolve({ exitCode, signal });
111
- });
112
- });
129
+ const result = await waitForChildTermination(child);
113
130
  process.exit(getHandoffExitCode(result.exitCode, result.signal));
114
131
  }
115
132
  finally {
@@ -317,3 +334,12 @@ export const CliLauncherInternal = Object.freeze({
317
334
  maybeHandoffToProjectLocalCli,
318
335
  resolveProjectLocalCliHandoff,
319
336
  });
337
+ const shouldRunAsMain = () => {
338
+ const entryArg = process.argv[1];
339
+ if (typeof entryArg !== 'string' || entryArg.trim() === '')
340
+ return false;
341
+ return getRealPath(entryArg) === getRealPath(fileURLToPath(import.meta.url));
342
+ };
343
+ if (shouldRunAsMain()) {
344
+ await run();
345
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust.d.ts","sourceRoot":"","sources":["../../bin/zintrust.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAKH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"zintrust.d.ts","sourceRoot":"","sources":["../../bin/zintrust.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AA8CH,OAAO,EAAE,CAAC"}
package/bin/zintrust.js CHANGED
@@ -6,5 +6,38 @@
6
6
  * bin/zintrust-main.ts. Keeping the implementation hashbang-free allows other
7
7
  * shortcuts (zin/z/zt) to import it without parse issues.
8
8
  */
9
- import { run } from './zintrust-main.js';
10
- await run();
9
+ import { spawn } from 'node:child_process';
10
+ import { existsSync } from 'node:fs';
11
+ import { createRequire } from 'node:module';
12
+ import path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ const here = path.dirname(fileURLToPath(import.meta.url));
15
+ const require = createRequire(import.meta.url);
16
+ const tsTarget = path.join(here, 'zintrust-main.ts');
17
+ const jsTarget = path.join(here, 'zintrust-main.js');
18
+ const target = existsSync(tsTarget) ? tsTarget : jsTarget;
19
+ const tsxImportPath = require.resolve('tsx');
20
+ const nodeArgs = target.endsWith('.ts')
21
+ ? ['--import', tsxImportPath, target, ...process.argv.slice(2)]
22
+ : [target, ...process.argv.slice(2)];
23
+ const child = spawn(process.execPath, nodeArgs, {
24
+ stdio: 'inherit',
25
+ env: process.env,
26
+ });
27
+ const result = await new Promise((resolve, reject) => {
28
+ let settled = false;
29
+ const finalize = (exitCode, signal) => {
30
+ if (settled) {
31
+ return;
32
+ }
33
+ settled = true;
34
+ child.off?.('error', reject);
35
+ child.off?.('exit', finalize);
36
+ child.off?.('close', finalize);
37
+ resolve({ exitCode, signal });
38
+ };
39
+ child.once('error', reject);
40
+ child.once('exit', finalize);
41
+ child.once('close', finalize);
42
+ });
43
+ process.exit(result.exitCode ?? (result.signal === 'SIGINT' || result.signal === 'SIGTERM' ? 0 : 1));
package/bin/zt.js CHANGED
@@ -3,5 +3,25 @@
3
3
  * ZinTrust CLI Shortcut - 'z'
4
4
  * Mirrors bin/zintrust.ts for convenience
5
5
  */
6
- import { run } from './zintrust-main.js';
7
- await run();
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ const here = path.dirname(fileURLToPath(import.meta.url));
11
+ const tsTarget = path.join(here, 'zintrust-main.ts');
12
+ const jsTarget = path.join(here, 'zintrust-main.js');
13
+ const target = existsSync(tsTarget) ? tsTarget : jsTarget;
14
+ const nodeArgs = target.endsWith('.ts')
15
+ ? ['--import', 'tsx', target, ...process.argv.slice(2)]
16
+ : [target, ...process.argv.slice(2)];
17
+ const child = spawn(process.execPath, nodeArgs, {
18
+ stdio: 'inherit',
19
+ env: process.env,
20
+ });
21
+ const result = await new Promise((resolve, reject) => {
22
+ child.once('error', reject);
23
+ child.once('close', (exitCode, signal) => {
24
+ resolve({ exitCode, signal });
25
+ });
26
+ });
27
+ process.exit(result.exitCode ?? (result.signal === 'SIGINT' || result.signal === 'SIGTERM' ? 0 : 1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -3,5 +3,5 @@
3
3
  * Entry point for running the ZinTrust server
4
4
  * Sealed namespace for immutability
5
5
  */
6
- export {};
6
+ export declare const bootstrapReady: Promise<void>;
7
7
  //# sourceMappingURL=bootstrap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/boot/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/boot/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyWH,eAAO,MAAM,cAAc,eAAmB,CAAC"}
@@ -283,15 +283,20 @@ const BootstrapFunctions = Object.freeze({
283
283
  });
284
284
  },
285
285
  });
286
- // Run bootstrap
287
- await BootstrapFunctions.start().catch((error) => {
286
+ const startBootstrap = async () => {
288
287
  try {
289
- Logger.error('Failed to bootstrap application:', error);
288
+ await BootstrapFunctions.start();
289
+ BootstrapFunctions.setupShutdownHandler();
290
290
  }
291
- catch {
292
- // best-effort logging
291
+ catch (error) {
292
+ try {
293
+ Logger.error('Failed to bootstrap application:', error);
294
+ }
295
+ catch {
296
+ // best-effort logging
297
+ }
298
+ process.exit(1);
293
299
  }
294
- process.exit(1);
295
- });
296
- // Handle graceful shutdown
297
- BootstrapFunctions.setupShutdownHandler();
300
+ };
301
+ // Run bootstrap without parse-time top-level await so Worker bundles can load this module.
302
+ export const bootstrapReady = startBootstrap();
@@ -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;AAsgCvF,eAAO,MAAM,YAAY;cACb,YAAY;;mCA18BU,MAAM,KAAG,OAAO;4CAaP,MAAM,KAAG,MAAM;4CA4Bf,MAAM,KAAG,OAAO;sCAetB,MAAM,WAAW,MAAM,KAAG,OAAO;oCAmBnC,MAAM,KAAG,OAAO;;EA06BjD,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;AAugCvF,eAAO,MAAM,YAAY;cACb,YAAY;;mCA38BU,MAAM,KAAG,OAAO;4CAaP,MAAM,KAAG,MAAM;4CA4Bf,MAAM,KAAG,OAAO;sCAetB,MAAM,WAAW,MAAM,KAAG,OAAO;oCAmBnC,MAAM,KAAG,OAAO;;EA26BjD,CAAC"}
@@ -635,6 +635,7 @@ const executeNodeStart = async (cmd, context, mode, watchEnabled, _port) => {
635
635
  command: dev.command,
636
636
  args: dev.args,
637
637
  forwardSignals: false,
638
+ ttySignalForwardDelayMs: 1500,
638
639
  env: {
639
640
  ...buildStartEnv(context.projectRoot),
640
641
  ZINTRUST_BOOTSTRAP_PREFERENCE: 'source',
@@ -4,6 +4,7 @@ export interface SpawnAndWaitInput {
4
4
  cwd?: string;
5
5
  env?: NodeJS.ProcessEnv;
6
6
  forwardSignals?: boolean;
7
+ ttySignalForwardDelayMs?: number;
7
8
  shell?: boolean;
8
9
  }
9
10
  export declare const SpawnUtil: Readonly<{
@@ -1 +1 @@
1
- {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/spawn.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAiDD,eAAO,MAAM,SAAS;wBACM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;EAiF7D,CAAC"}
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/spawn.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA8HD,eAAO,MAAM,SAAS;wBACM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;EA2F7D,CAAC"}
@@ -45,6 +45,60 @@ const buildCommandNotFoundMessage = (command) => {
45
45
  }
46
46
  return `Error: '${command}' not found on PATH.`;
47
47
  };
48
+ const waitForChildExit = async (child, onExit) => {
49
+ return new Promise((resolve, reject) => {
50
+ let settled = false;
51
+ const finish = (result) => {
52
+ if (settled)
53
+ return;
54
+ settled = true;
55
+ child.off?.('exit', onExitEvent);
56
+ child.off?.('close', onCloseEvent);
57
+ onExit();
58
+ resolve(result);
59
+ };
60
+ const onExitEvent = (code, signal) => {
61
+ finish({ exitCode: code, signal });
62
+ };
63
+ const onCloseEvent = (code, signal) => {
64
+ finish({ exitCode: code, signal });
65
+ };
66
+ child.once('error', (error) => {
67
+ reject(error);
68
+ });
69
+ child.once('exit', onExitEvent);
70
+ child.once('close', onCloseEvent);
71
+ });
72
+ };
73
+ const createDelayedSignalForwarder = (input) => {
74
+ let delayedSignalTimer;
75
+ const clear = () => {
76
+ if (delayedSignalTimer === undefined)
77
+ return;
78
+ clearTimeout(delayedSignalTimer);
79
+ delayedSignalTimer = undefined;
80
+ };
81
+ const schedule = (signal) => {
82
+ if (input.ttySignalForwardDelayMs <= 0 ||
83
+ delayedSignalTimer !== undefined ||
84
+ input.isChildClosed()) {
85
+ return;
86
+ }
87
+ delayedSignalTimer = globalThis.setTimeout(() => {
88
+ delayedSignalTimer = undefined;
89
+ if (input.isChildClosed())
90
+ return;
91
+ try {
92
+ input.forwardSignal(signal);
93
+ }
94
+ catch {
95
+ // best-effort fallback for interactive watch processes
96
+ }
97
+ }, input.ttySignalForwardDelayMs);
98
+ delayedSignalTimer.unref?.();
99
+ };
100
+ return { clear, schedule };
101
+ };
48
102
  export const SpawnUtil = Object.freeze({
49
103
  async spawnAndWait(input) {
50
104
  const cwd = input.cwd ?? process.cwd();
@@ -59,6 +113,10 @@ export const SpawnUtil = Object.freeze({
59
113
  // (and often SIGTERM) so forwarding can cause duplicates. `tsx watch` is
60
114
  // especially sensitive here and can print "Previous process hasn't exited yet. Force killing...".
61
115
  const forwardSignals = typeof input.forwardSignals === 'boolean' ? input.forwardSignals : !process.stdin.isTTY;
116
+ const ttySignalForwardDelayMs = process.stdin.isTTY === true && forwardSignals === false
117
+ ? Math.max(0, input.ttySignalForwardDelayMs ?? 0)
118
+ : 0;
119
+ let childClosed = false;
62
120
  const forwardSignal = (signal) => {
63
121
  try {
64
122
  child.kill(signal);
@@ -75,29 +133,34 @@ export const SpawnUtil = Object.freeze({
75
133
  throw wrapped;
76
134
  }
77
135
  };
136
+ const delayedSignalForwarder = createDelayedSignalForwarder({
137
+ ttySignalForwardDelayMs,
138
+ isChildClosed: () => childClosed,
139
+ forwardSignal,
140
+ });
78
141
  const onSigint = () => {
79
142
  if (forwardSignals) {
80
143
  forwardSignal('SIGINT');
144
+ return;
81
145
  }
82
- // If not forwarding, handle to prevent the parent from exiting before the child
83
- // finishes its graceful shutdown (child receives SIGINT directly in TTY).
146
+ // In interactive TTY mode, let the child receive the terminal SIGINT directly first.
147
+ // If it is still alive after a short grace period, send one fallback signal so the
148
+ // watcher exits without requiring a second Ctrl+C from the user.
149
+ delayedSignalForwarder.schedule('SIGINT');
84
150
  };
85
151
  const onSigterm = () => {
86
152
  if (forwardSignals) {
87
153
  forwardSignal('SIGTERM');
154
+ return;
88
155
  }
89
- // Same rationale as SIGINT: keep parent alive while waiting for child to exit.
156
+ delayedSignalForwarder.schedule('SIGTERM');
90
157
  };
91
158
  process.on('SIGINT', onSigint);
92
159
  process.on('SIGTERM', onSigterm);
93
160
  try {
94
- const result = await new Promise((resolve, reject) => {
95
- child.once('error', (error) => {
96
- reject(error);
97
- });
98
- child.once('close', (code, signal) => {
99
- resolve({ exitCode: code, signal });
100
- });
161
+ const result = await waitForChildExit(child, () => {
162
+ childClosed = true;
163
+ delayedSignalForwarder.clear();
101
164
  });
102
165
  return getExitCode(result.exitCode, result.signal);
103
166
  }
@@ -109,6 +172,7 @@ export const SpawnUtil = Object.freeze({
109
172
  throw ErrorFactory.createTryCatchError('Failed to spawn child process', error);
110
173
  }
111
174
  finally {
175
+ delayedSignalForwarder.clear();
112
176
  process.off('SIGINT', onSigint);
113
177
  process.off('SIGTERM', onSigterm);
114
178
  }
@@ -12,13 +12,13 @@ export { notificationConfig, type NotificationConfig } from './notification';
12
12
  export { queueConfig, type QueueConfig } from './queue';
13
13
  export { securityConfig } from './security';
14
14
  export { storageConfig, type StorageConfig } from './storage';
15
+ export type { MiddlewareConfigType } from './type';
15
16
  export { createRedisConnection } from './workers';
16
17
  /**
17
18
  * Combined configuration object
18
19
  * Sealed namespace for immutability
19
20
  */
20
21
  export declare const config: Readonly<{
21
- readonly middleware: import("./type").MiddlewareConfigType;
22
22
  readonly app: Readonly<{
23
23
  readonly name: string;
24
24
  readonly prefix: string;
@@ -211,6 +211,7 @@ export declare const config: Readonly<{
211
211
  readonly namespace: string;
212
212
  };
213
213
  }>;
214
+ readonly middleware: import("./type").MiddlewareConfigType;
214
215
  readonly cache: {
215
216
  default: string;
216
217
  drivers: import("./type").CacheConfigInput["drivers"];
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;8BAkC0f,CAAC;;;;;;;;;;;;;;;;;;;;2EA9C3e,CAAA;;;;;;;;;;;;;;;;;;;;8BAD2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+C8X,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAHnb,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,KAAK,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;8BAkCgc,CAAC;;;;;;;;;;;;;;;;;;;;iFA/Cjb,CAAA;;;;;;;;;;;;;;;;;;;;8BAD2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAgDoU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAHzX,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
@@ -28,9 +28,6 @@ export { createRedisConnection } from './workers.js';
28
28
  * Sealed namespace for immutability
29
29
  */
30
30
  export const config = Object.freeze({
31
- get middleware() {
32
- return middlewareConfig;
33
- },
34
31
  get app() {
35
32
  return appConfig;
36
33
  },
@@ -52,6 +49,9 @@ export const config = Object.freeze({
52
49
  get microservices() {
53
50
  return microservicesConfig;
54
51
  },
52
+ get middleware() {
53
+ return middlewareConfig;
54
+ },
55
55
  get cache() {
56
56
  return cacheConfig;
57
57
  },
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../../src/config/queue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGhG,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH,CAAC,CAAC;AA+EH;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAO,kBA4DnC,CAAC;AAgDH,QAAA,MAAM,iBAAiB,QAAO;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,CAAC,YAAY,EAAE,sBAAsB,KAAK,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACzF,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CAuEH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAqB/D,eAAO,MAAM,WAAW,EAAE,WAYxB,CAAC"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../../src/config/queue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,sBAAsB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAyChG,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC;IACzC,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACrC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH,CAAC,CAAC;AA+EH;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAO,kBA4DnC,CAAC;AAgDH,QAAA,MAAM,iBAAiB,QAAO;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,SAAS,EAAE,CAAC,YAAY,EAAE,sBAAsB,KAAK,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACzF,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,OAAO,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,WAAW,EAAE,OAAO,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CAuEH,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAqB/D,eAAO,MAAM,WAAW,EAAE,WAYxB,CAAC"}
@@ -6,10 +6,39 @@
6
6
  import { Cloudflare } from './cloudflare.js';
7
7
  import { Env } from './env.js';
8
8
  import { Logger } from './logger.js';
9
- import { isKnownMiddlewareName, middlewareConfig } from './middleware.js';
10
9
  import { ErrorFactory } from '../exceptions/ZintrustError.js';
11
10
  import { ZintrustLang } from '../lang/lang.js';
12
11
  import { StartupConfigFile, StartupConfigFileRegistry } from '../runtime/StartupConfigFileRegistry.js';
12
+ const StaticMiddlewareKeys = Object.freeze({
13
+ log: true,
14
+ error: true,
15
+ security: true,
16
+ rateLimit: true,
17
+ sanitizeBody: true,
18
+ fillRateLimit: true,
19
+ authRateLimit: true,
20
+ userMutationRateLimit: true,
21
+ csrf: true,
22
+ auth: true,
23
+ jwt: true,
24
+ bulletproof: true,
25
+ validateLogin: true,
26
+ validateRegister: true,
27
+ validateUserStore: true,
28
+ validateUserUpdate: true,
29
+ validateUserFill: true,
30
+ });
31
+ const isKnownQueueMonitorMiddlewareName = (value) => {
32
+ return (Object.hasOwn(StaticMiddlewareKeys, value) || /^rateLimit:\d+:\d+(?:\.\d+)?$/.test(value.trim()));
33
+ };
34
+ const getConfiguredQueueMonitorRouteKeys = () => {
35
+ const middlewareOverrides = StartupConfigFileRegistry.get(StartupConfigFile.Middleware) ?? {};
36
+ const routeConfig = middlewareOverrides.route;
37
+ if (typeof routeConfig !== 'object' || routeConfig === null || Array.isArray(routeConfig)) {
38
+ return new Set();
39
+ }
40
+ return new Set(Object.keys(routeConfig));
41
+ };
13
42
  const getQueueDriver = (driverConfig) => {
14
43
  const driverName = driverConfig.default;
15
44
  return driverConfig.drivers[driverName];
@@ -124,9 +153,9 @@ const createBaseMonitor = () => {
124
153
  .map((m) => m.trim())
125
154
  .filter((m) => m.length > 0);
126
155
  if (enabled && middleware.length > 0) {
127
- const knownKeys = new Set(Object.keys(middlewareConfig.route ?? {}));
156
+ const knownKeys = getConfiguredQueueMonitorRouteKeys();
128
157
  const unknownKeys = middleware.filter((name) => {
129
- return !knownKeys.has(name) && !isKnownMiddlewareName(name);
158
+ return !knownKeys.has(name) && !isKnownQueueMonitorMiddlewareName(name);
130
159
  });
131
160
  if (unknownKeys.length > 0) {
132
161
  Logger.error('Unknown QUEUE_MONITOR_MIDDLEWARE keys configured', {
@@ -1 +1 @@
1
- {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../src/http/RequestContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAqGD,eAAO,MAAM,cAAc;QACf,CAAC,WAAW,eAAe,YAAY,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;eAKpD,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;YAK7C,eAAe,GAAG,SAAS;gBAIvB,QAAQ,GAAG,eAAe;gBAoC1B,QAAQ,WAAW,eAAe,GAAG,IAAI;aAS5C,QAAQ,GAAG,eAAe,GAAG,SAAS;oBAM/B,eAAe,UAAU,MAAM,GAAG,eAAe;mBASlD,QAAQ,UAAU,MAAM,GAAG,SAAS,GAAG,IAAI;qBAIzC,QAAQ,YAAY,MAAM,GAAG,SAAS,GAAG,IAAI;oBAI9C,QAAQ,WAAW,MAAM,GAAG,SAAS,GAAG,IAAI;EAG5D,CAAC;AAEH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../src/http/RequestContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAwGD,eAAO,MAAM,cAAc;QACf,CAAC,WAAW,eAAe,YAAY,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;eAKpD,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC;YAK7C,eAAe,GAAG,SAAS;gBAIvB,QAAQ,GAAG,eAAe;gBAoC1B,QAAQ,WAAW,eAAe,GAAG,IAAI;aAS5C,QAAQ,GAAG,eAAe,GAAG,SAAS;oBAM/B,eAAe,UAAU,MAAM,GAAG,eAAe;mBASlD,QAAQ,UAAU,MAAM,GAAG,SAAS,GAAG,IAAI;qBAIzC,QAAQ,YAAY,MAAM,GAAG,SAAS,GAAG,IAAI;oBAI9C,QAAQ,WAAW,MAAM,GAAG,SAAS,GAAG,IAAI;EAG5D,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -77,9 +77,12 @@ const setContextField = (req, field, value) => {
77
77
  ctx[field] = value;
78
78
  }
79
79
  };
80
- const resolvedStorage = await resolveStorage();
81
- syncStorage = resolvedStorage;
82
- const STORAGE_PROMISE = Promise.resolve(resolvedStorage);
80
+ const initializeStorage = async () => {
81
+ const storage = await resolveStorage();
82
+ syncStorage = storage;
83
+ return storage;
84
+ };
85
+ const STORAGE_PROMISE = initializeStorage();
83
86
  export const RequestContext = Object.freeze({
84
87
  async run(context, callback) {
85
88
  const storage = await STORAGE_PROMISE;
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v1.5.2
2
+ * @zintrust/core v1.5.4
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-04-25T12:35:41.534Z
8
+ * Built: 2026-04-29T07:41:51.160Z
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-04-25T12:35:41.502Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-04-29T07:41:51.121Z'; // 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":"Schema.d.ts","sourceRoot":"","sources":["../../../../src/migrations/schema/Schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM/C,OAAO,KAAK,EAAgC,aAAa,EAAE,MAAM,0BAA0B,CAAC;AA6M5F,iBAAS,mBAAmB,CAAC,EAAE,EAAE,SAAS,GAAG,aAAa,CAWzD;AAED,eAAO,MAAM,MAAM;;EAEjB,CAAC"}
1
+ {"version":3,"file":"Schema.d.ts","sourceRoot":"","sources":["../../../../src/migrations/schema/Schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM/C,OAAO,KAAK,EAKV,aAAa,EACd,MAAM,0BAA0B,CAAC;AAoYlC,iBAAS,mBAAmB,CAAC,EAAE,EAAE,SAAS,GAAG,aAAa,CAWzD;AAED,eAAO,MAAM,MAAM;;EAEjB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
2
+ import { isNonEmptyString, isObject } from '../../helper/index.js';
2
3
  import { BaseAdapter } from '../../orm/DatabaseAdapter.js';
3
4
  import { isSqliteFamily } from '../enum/index.js';
4
5
  import { MigrationBlueprint } from '../schema/Blueprint.js';
@@ -9,18 +10,128 @@ function assertIdentifier(label, value) {
9
10
  throw ErrorFactory.createValidationError(`Invalid ${label} identifier: ${value}`);
10
11
  }
11
12
  }
12
- function isRecord(value) {
13
- return typeof value === 'object' && value !== null;
14
- }
15
13
  function getStringProp(value, key) {
16
- if (!isRecord(value))
14
+ if (!isObject(value))
17
15
  return null;
18
16
  const v = value[key];
19
- return typeof v === 'string' ? v : null;
17
+ return isNonEmptyString(v) ? v : null;
20
18
  }
21
19
  function mapNames(rows) {
22
20
  return rows.map((r) => getStringProp(r, 'name') ?? '').filter((name) => name.length > 0);
23
21
  }
22
+ function normalizeSqliteAffinity(type) {
23
+ if (!isNonEmptyString(type))
24
+ return null;
25
+ const normalized = type.trim().toUpperCase();
26
+ if (normalized.includes('INT'))
27
+ return 'INTEGER';
28
+ if (normalized.includes('CHAR') ||
29
+ normalized.includes('CLOB') ||
30
+ normalized.includes('TEXT') ||
31
+ normalized.includes('UUID') ||
32
+ normalized.includes('DATE') ||
33
+ normalized.includes('TIME') ||
34
+ normalized.includes('JSON')) {
35
+ return 'TEXT';
36
+ }
37
+ if (normalized.includes('BLOB'))
38
+ return 'BLOB';
39
+ if (normalized.includes('REAL') || normalized.includes('FLOA') || normalized.includes('DOUB')) {
40
+ return 'REAL';
41
+ }
42
+ return 'NUMERIC';
43
+ }
44
+ function getPlannedSqliteAffinity(def) {
45
+ switch (def.type) {
46
+ case 'STRING':
47
+ case 'DATE':
48
+ case 'UUID':
49
+ case 'TEXT':
50
+ case 'JSON':
51
+ case 'TIMESTAMP':
52
+ return 'TEXT';
53
+ case 'INTEGER':
54
+ case 'BIGINT':
55
+ return 'INTEGER';
56
+ case 'REAL':
57
+ return 'REAL';
58
+ case 'BLOB':
59
+ return 'BLOB';
60
+ case 'BOOLEAN':
61
+ return 'NUMERIC';
62
+ default:
63
+ return 'NUMERIC';
64
+ }
65
+ }
66
+ async function getSqliteTableColumns(db, tableName) {
67
+ assertIdentifier('table', tableName);
68
+ const rows = await db.query(`PRAGMA table_info("${tableName}")`, [], true);
69
+ return rows
70
+ .map((row) => {
71
+ const name = getStringProp(row, 'name');
72
+ if (!isNonEmptyString(name))
73
+ return null;
74
+ return {
75
+ name,
76
+ affinity: normalizeSqliteAffinity(getStringProp(row, 'type')),
77
+ };
78
+ })
79
+ .filter((row) => row !== null);
80
+ }
81
+ function getColumnAffinityLabel(affinity) {
82
+ return affinity ?? 'unknown';
83
+ }
84
+ function buildPlannedColumnMap(columns) {
85
+ return new Map(columns.map((column) => [column.name, getPlannedSqliteAffinity(column)]));
86
+ }
87
+ function getColumnAffinity(columnName, existingColumns, plannedColumns) {
88
+ return plannedColumns.get(columnName) ?? existingColumns.get(columnName) ?? null;
89
+ }
90
+ function describeSqliteForeignKey(tableName, fk, localColumns, plannedColumns, referencedColumns) {
91
+ const local = fk.columns.map((column) => {
92
+ const affinity = getColumnAffinity(column, localColumns, plannedColumns);
93
+ return `${tableName}.${column} [${getColumnAffinityLabel(affinity)}]`;
94
+ });
95
+ const referenced = fk.referencedColumns.map((column) => {
96
+ const affinity = referencedColumns.get(column) ?? null;
97
+ return `${fk.referencedTable}.${column} [${getColumnAffinityLabel(affinity)}]`;
98
+ });
99
+ const hasMismatch = fk.columns.some((column, index) => {
100
+ const localAffinity = getColumnAffinity(column, localColumns, plannedColumns);
101
+ const referencedAffinity = referencedColumns.get(fk.referencedColumns[index]) ?? null;
102
+ return (isNonEmptyString(localAffinity) &&
103
+ isNonEmptyString(referencedAffinity) &&
104
+ localAffinity !== referencedAffinity);
105
+ });
106
+ const mismatchSuffix = hasMismatch
107
+ ? ' (detected SQLite affinity mismatch between local and referenced columns)'
108
+ : '';
109
+ return `Add foreign key "${fk.name}": ${local.join(', ')} -> ${referenced.join(', ')}${mismatchSuffix}`;
110
+ }
111
+ async function buildSqliteAlterTableDiagnostic(db, tableName, plan) {
112
+ const details = [];
113
+ if (plan.dropColumns.length > 0) {
114
+ details.push(`Drop columns: ${plan.dropColumns.join(', ')}`);
115
+ }
116
+ if (plan.addForeignKeys.length > 0) {
117
+ const localColumns = await getSqliteTableColumns(db, tableName);
118
+ const localColumnMap = new Map(localColumns.map((column) => [column.name, column.affinity]));
119
+ const plannedColumnMap = buildPlannedColumnMap(plan.addColumns);
120
+ const foreignKeyDetails = await Promise.all(plan.addForeignKeys.map(async (fk) => {
121
+ const referenced = await getSqliteTableColumns(db, fk.referencedTable);
122
+ const referencedColumnMap = new Map(referenced.map((column) => [column.name, column.affinity]));
123
+ return describeSqliteForeignKey(tableName, fk, localColumnMap, plannedColumnMap, referencedColumnMap);
124
+ }));
125
+ details.push(...foreignKeyDetails);
126
+ }
127
+ if (plan.dropForeignKeys.length > 0) {
128
+ details.push(`Drop foreign keys: ${plan.dropForeignKeys.join(', ')}`);
129
+ }
130
+ return [
131
+ `SQLite/D1 schema.table('${tableName}') cannot drop columns or alter foreign keys without a table rebuild.`,
132
+ ...details,
133
+ ].join(' ');
134
+ }
24
135
  function buildParameterized(db, sql, parameters) {
25
136
  const adapter = db.getAdapterInstance(false);
26
137
  return BaseAdapter.buildParameterizedQuery(sql, parameters, (i) => adapter.getPlaceholder(i));
@@ -58,6 +169,12 @@ async function schemaTable(db, tableName, callback) {
58
169
  addForeignKeys: def.foreignKeys,
59
170
  dropForeignKeys: blueprint.getDropForeignKeys(),
60
171
  };
172
+ if (isSqliteFamily(db.getType()) &&
173
+ (plan.dropColumns.length > 0 ||
174
+ plan.addForeignKeys.length > 0 ||
175
+ plan.dropForeignKeys.length > 0)) {
176
+ throw ErrorFactory.createValidationError(await buildSqliteAlterTableDiagnostic(db, tableName, plan));
177
+ }
61
178
  const statements = MigrationSchemaCompiler.compileAlterTable(db.getType(), tableName, plan);
62
179
  await runStatements(db, statements);
63
180
  }
@@ -1 +1 @@
1
- {"version":3,"file":"PluginManager.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginManager.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAsRhE,eAAO,MAAM,aAAa;IACxB;;OAEG;YACK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxC;;OAEG;yBACkB,MAAM,GAAG,MAAM,GAAG,IAAI;IAW3C;;OAEG;0BACyB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0DrD;;OAEG;sBACqB,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrF;;;;OAIG;wBACuB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EA2ChD,CAAC"}
1
+ {"version":3,"file":"PluginManager.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginManager.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AA2ShE,eAAO,MAAM,aAAa;IACxB;;OAEG;YACK,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAIxC;;OAEG;yBACkB,MAAM,GAAG,MAAM,GAAG,IAAI;IAW3C;;OAEG;0BACyB,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0DrD;;OAEG;sBACqB,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrF;;;;OAIG;wBACuB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EA2ChD,CAAC"}
@@ -3,7 +3,6 @@
3
3
  * Plugin Manager
4
4
  * Handles installation and removal of framework plugins.
5
5
  */
6
- import { SpawnUtil } from '../cli/utils/spawn.js';
7
6
  import { readEnvString } from '../common/ExternalServiceUtils.js';
8
7
  import { esmDirname, resolvePackageManager } from '../common/index.js';
9
8
  import { Logger } from '../config/logger.js';
@@ -13,6 +12,11 @@ import * as path from '../node-singletons/path.js';
13
12
  import { PluginRegistry } from './PluginRegistry.js';
14
13
  const __dirname = esmDirname(import.meta.url);
15
14
  const MAX_PACKAGE_ROOT_SEARCH_DEPTH = 20;
15
+ let spawnUtilPromise;
16
+ const loadSpawnUtil = async () => {
17
+ spawnUtilPromise ??= import('../cli/utils/spawn.js').then((module) => module.SpawnUtil);
18
+ return spawnUtilPromise;
19
+ };
16
20
  function findPackageRoot(startDir) {
17
21
  let current = startDir;
18
22
  for (let i = 0; i < MAX_PACKAGE_ROOT_SEARCH_DEPTH; i++) {
@@ -109,8 +113,9 @@ async function npmInstall(packages, options) {
109
113
  break;
110
114
  }
111
115
  try {
116
+ const spawnUtil = await loadSpawnUtil();
112
117
  // Use async spawn for all package managers to avoid blocking event loop
113
- const exit = await SpawnUtil.spawnAndWait({ command: cmd, args, cwd: projectRoot });
118
+ const exit = await spawnUtil.spawnAndWait({ command: cmd, args, cwd: projectRoot });
114
119
  if (exit !== 0) {
115
120
  throw ErrorFactory.createCliError(`Package manager ${pm} failed to install ${options.label}`, {
116
121
  exit,
@@ -213,7 +218,8 @@ async function runPostInstall(plugin) {
213
218
  Logger.info(`Running post-install command: ${plugin.postInstall.command}...`);
214
219
  let exit;
215
220
  try {
216
- exit = await SpawnUtil.spawnAndWait({
221
+ const spawnUtil = await loadSpawnUtil();
222
+ exit = await spawnUtil.spawnAndWait({
217
223
  command: plugin.postInstall.command,
218
224
  args: [],
219
225
  cwd: projectRoot,
@@ -1,5 +1,5 @@
1
1
  export declare const WorkerAdapterImports: Readonly<{
2
- loaded: boolean;
3
- ready: void;
2
+ loaded: true;
3
+ ready: Promise<void>;
4
4
  }>;
5
5
  //# sourceMappingURL=WorkerAdapterImports.d.ts.map
@@ -22,7 +22,7 @@ const tryImportOptional = async () => {
22
22
  await tryImportProjectRuntime();
23
23
  await import('./WorkerProjectPlugins.js');
24
24
  };
25
- const ready = await tryImportOptional();
25
+ const ready = tryImportOptional();
26
26
  export const WorkerAdapterImports = Object.freeze({
27
27
  loaded: true,
28
28
  ready,
@@ -1 +1 @@
1
- {"version":3,"file":"trace-runtime.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace-runtime.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG;IACpB,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CACtC,CAAC;AAwEF,eAAO,MAAM,WAAW,QAAO,OAA0C,CAAC;AAE1E,eAAO,MAAM,WAAW,EAAE,cAIxB,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,eAIzB,CAAC;AAEH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,OAAO,EACf,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,OAAO,EACf,SAAS,OAAO,EAChB,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,EACd,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KAChF,IASF,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAa,OAAO,CAAC,IAAI,CAIhE,CAAC"}
1
+ {"version":3,"file":"trace-runtime.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace-runtime.ts"],"names":[],"mappings":"AAAA,KAAK,cAAc,GAAG;IACpB,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC;CACtC,CAAC;AAwEF,eAAO,MAAM,WAAW,QAAO,OAA0C,CAAC;AAE1E,eAAO,MAAM,WAAW,EAAE,cAIxB,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,eAIzB,CAAC;AAEH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,OAAO,EACf,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,OAAO,EACf,SAAS,OAAO,EAChB,UAAU;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAAE,KAClE,IAEF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,EACd,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KAChF,IASF,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAa,OAAO,CAAC,IAAI,CAKhE,CAAC"}
@@ -59,5 +59,6 @@ export const ensureSystemTraceRegistered = async () => {
59
59
  const module = await loadSystemTraceModule();
60
60
  if (module === undefined)
61
61
  return;
62
- await import('../../../packages/trace/src/register.js').catch(() => undefined);
62
+ const registerModule = await import('../../../packages/trace/src/register.js').catch(() => undefined);
63
+ await registerModule?.registerTraceReady?.catch(() => undefined);
63
64
  };
@@ -1,2 +1,3 @@
1
1
  export type {};
2
+ export declare const ready: Promise<void>;
2
3
  //# sourceMappingURL=trace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../../../src/runtime/plugins/trace.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,CAAC;AAmBf,eAAO,MAAM,KAAK,eAAoB,CAAC"}
@@ -7,8 +7,10 @@ const tryImport = async (specifier) => {
7
7
  return false;
8
8
  }
9
9
  };
10
- const importedPackagePlugin = await tryImport('@zintrust/trace/plugin');
11
- if (!importedPackagePlugin) {
12
- await import('../../../packages/trace/src/plugin.js').catch(() => undefined);
13
- }
14
- export {};
10
+ const loadTracePlugin = async () => {
11
+ const importedPackagePlugin = await tryImport('@zintrust/trace/plugin');
12
+ if (!importedPackagePlugin) {
13
+ await import('../../../packages/trace/src/plugin.js').catch(() => undefined);
14
+ }
15
+ };
16
+ export const ready = loadTracePlugin();
@@ -32,17 +32,17 @@ export default {
32
32
  .filter((m: string) => m.length > 0) as ReadonlyArray<string>,
33
33
  fillRateLimit: {
34
34
  windowMs: 60_000,
35
- max: 5,
35
+ maxRequests: 5,
36
36
  message: 'Too many fill requests, please try again later.',
37
37
  },
38
38
  authRateLimit: {
39
39
  windowMs: 60_000,
40
- max: 4,
40
+ maxRequests: 4,
41
41
  message: 'Too many authentication attempts, please try again later.',
42
42
  },
43
43
  userMutationRateLimit: {
44
44
  windowMs: 60_000,
45
- max: 20,
45
+ maxRequests: 20,
46
46
  message: 'Too many user mutation requests, please try again later.',
47
47
  },
48
48
  responders: {
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Auto-generated fallback module.
3
- * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
- * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
2
+ * ZinTrust plugin auto-imports
3
+ *
4
+ * In real projects, this file is managed by `zin plugin install` and contains
5
+ * side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
6
+ * optional adapters/drivers into core registries.
7
+ *
5
8
  */
6
9
  export type {};
7
10
  export declare const __zintrustGeneratedPluginStub = "zintrust.plugins.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,YAAY,EAAE,CAAC;AAgBf,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
1
+ {"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,YAAY,EAAE,CAAC;AAgBf,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Auto-generated fallback module.
3
- * This file is created by scripts/ensure-worker-plugins.mjs when missing.
4
- * It allows optional runtime plugin imports to resolve in CI/scaffolded setups.
2
+ * ZinTrust plugin auto-imports
3
+ *
4
+ * In real projects, this file is managed by `zin plugin install` and contains
5
+ * side-effect imports (e.g. `@zintrust/db-sqlite/register`) that register
6
+ * optional adapters/drivers into core registries.
7
+ *
5
8
  */
6
9
  import * as TraceRuntime from './runtime/plugins/trace-runtime.js';
7
10
  globalThis.__zintrust_system_trace_plugin_requested__ = true;