@zintrust/core 0.1.52 → 0.1.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/boot/bootstrap.js +6 -5
  3. package/src/boot/registry/runtime.d.ts.map +1 -1
  4. package/src/boot/registry/runtime.js +10 -5
  5. package/src/cli/scaffolding/ProjectScaffolder.js +1 -1
  6. package/src/cli/scaffolding/env.d.ts.map +1 -1
  7. package/src/cli/scaffolding/env.js +2 -8
  8. package/src/config/app.d.ts +8 -0
  9. package/src/config/app.d.ts.map +1 -1
  10. package/src/config/app.js +8 -0
  11. package/src/config/env.d.ts +3 -1
  12. package/src/config/env.d.ts.map +1 -1
  13. package/src/config/env.js +4 -1
  14. package/src/config/index.d.ts +2 -0
  15. package/src/config/index.d.ts.map +1 -1
  16. package/src/index.js +3 -3
  17. package/src/orm/Database.d.ts.map +1 -1
  18. package/src/orm/Database.js +7 -4
  19. package/src/routes/doc.d.ts +1 -1
  20. package/src/routes/doc.d.ts.map +1 -1
  21. package/src/routes/doc.js +9 -8
  22. package/src/runtime/PluginAutoImports.d.ts.map +1 -1
  23. package/src/runtime/PluginAutoImports.js +0 -1
  24. package/src/runtime/detectRuntime.d.ts.map +1 -1
  25. package/src/runtime/detectRuntime.js +4 -1
  26. package/src/start.js +1 -1
  27. package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +35 -5
  28. package/src/templates/project/basic/app/Types/controller.ts.tpl +1 -0
  29. package/src/templates/project/basic/database/migrations/{create_users_table.ts.tpl → 20260109074544_create_users_table.ts.tpl} +6 -5
  30. package/src/templates/project/basic/database/migrations/20260218121500_create_jwt_revocations_table.ts.tpl +36 -0
  31. package/src/templates/project/basic/wrangler.jsonc.tpl +12 -0
  32. package/src/templates/project/basic/app/Middleware/ProfilerMiddleware.ts.tpl +0 -54
  33. package/src/templates/project/basic/app/Middleware/index.ts.tpl +0 -293
  34. package/src/templates/project/basic/database/migrations/create_tasks_table.ts.tpl +0 -28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -118,7 +118,8 @@ const gracefulShutdown = async (signal) => {
118
118
  forceExitTimer.unref?.();
119
119
  await withTimeout((async () => {
120
120
  // Shutdown worker management system FIRST (before database closes)
121
- if ((appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda') &&
121
+ if (appConfig.worker === true &&
122
+ (appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda') &&
122
123
  appConfig.dockerWorker === false) {
123
124
  try {
124
125
  const workers = await loadWorkersModule();
@@ -156,8 +157,7 @@ const gracefulShutdown = async (signal) => {
156
157
  };
157
158
  async function useWorkerStarter() {
158
159
  // Check if workers are enabled in this environment
159
- const workerEnabled = Env.getBool('WORKER_ENABLED', false);
160
- if (!workerEnabled || appConfig.dockerWorker === true) {
160
+ if (!appConfig.worker || appConfig.dockerWorker === true) {
161
161
  Logger.info('Workers disabled in this runtime (WORKER_ENABLED=false && DOCKER_WORKER=true), skipping worker management initialization');
162
162
  return;
163
163
  }
@@ -220,10 +220,11 @@ const BootstrapFunctions = Object.freeze({
220
220
  // Start listening
221
221
  await server.listen();
222
222
  Logger.info(`Server running at http://${host}:${port}`);
223
- Logger.info(`ZinTrust documentation at http://${host}:${port}/doc`);
223
+ Logger.info(`ZinTrust documentation at http://${host}:${port}/zintrust-doc`);
224
224
  // Start schedules for long-running runtimes (Node.js / Fargate)
225
225
  await startSchedulesIfNeeded(app);
226
- if (appConfig.dockerWorker === false &&
226
+ if (appConfig.worker === true &&
227
+ appConfig.dockerWorker === false &&
227
228
  (appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda')) {
228
229
  await useWorkerStarter();
229
230
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/runtime.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AA+I9C,eAAO,MAAM,8BAA8B,GAAI,iBAAiB,gBAAgB,KAAG,IA4BlF,CAAC;AAyLF,eAAO,MAAM,eAAe,GAAI,QAAQ;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,gBAAgB,CAAC;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,KAAG;IAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CA2E7D,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/boot/registry/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAsJ9C,eAAO,MAAM,8BAA8B,GAAI,iBAAiB,gBAAgB,KAAG,IA4BlF,CAAC;AAyLF,eAAO,MAAM,eAAe,GAAI,QAAQ;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,gBAAgB,CAAC;IAClC,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC,KAAG;IAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CA6E7D,CAAC"}
@@ -4,7 +4,6 @@ import { loadQueueMonitorModule, loadWorkersModule } from '../../runtime/Workers
4
4
  import { registerCachesFromRuntimeConfig } from '../../cache/CacheRuntimeRegistration.js';
5
5
  import broadcastConfig from '../../config/broadcast.js';
6
6
  import { Cloudflare } from '../../config/cloudflare.js';
7
- import { Env } from '../../config/env.js';
8
7
  import { FeatureFlags } from '../../config/features.js';
9
8
  import { Logger } from '../../config/logger.js';
10
9
  import notificationConfig from '../../config/notification.js';
@@ -39,7 +38,12 @@ const readRuntimeConfig = (key, fallback) => {
39
38
  return fallback;
40
39
  }
41
40
  };
42
- const appConfig = readRuntimeConfig('appConfig', { port: 7777, dockerWorker: false });
41
+ const appConfig = readRuntimeConfig('appConfig', {
42
+ port: 7777,
43
+ dockerWorker: false,
44
+ worker: false,
45
+ });
46
+ // exported solely for tests to exercise the default detectRuntime handler
43
47
  const cacheConfig = readRuntimeConfig('cacheConfig', RuntimeConfig.cacheConfig);
44
48
  const databaseConfig = readRuntimeConfig('databaseConfig', {
45
49
  default: 'sqlite',
@@ -331,14 +335,15 @@ export const createLifecycle = (params) => {
331
335
  await registerFromRuntimeConfig();
332
336
  await initializeArtifactDirectories(params.resolvedBasePath);
333
337
  await registerMasterRoutes(params.resolvedBasePath, params.router);
334
- const workerEnabled = Env.getBool('WORKER_ENABLED', false);
335
- if (Cloudflare.getWorkersEnv() === null && appConfig.dockerWorker === false && workerEnabled) {
338
+ if (Cloudflare.getWorkersEnv() === null &&
339
+ appConfig.dockerWorker === false &&
340
+ appConfig.worker === true) {
336
341
  await initializeWorkers(params.router);
337
342
  await initializeQueueMonitor(params.router);
338
343
  await initializeQueueHttpGateway(params.router);
339
344
  await initializeScheduleHttpGateway(params.router);
340
345
  }
341
- else if (!workerEnabled) {
346
+ else if (!appConfig.dockerWorker) {
342
347
  Logger.info('Skipping worker module initialization (WORKER_ENABLED=false).');
343
348
  }
344
349
  // Register service providers
@@ -503,7 +503,7 @@ zin s
503
503
 
504
504
  - Health: http://localhost:{{port}}/health
505
505
  - Tasks: http://localhost:{{port}}/api/tasks
506
- - Doc: http://localhost:{{port}}/doc
506
+ - Doc: http://localhost:{{port}}/zintrust-doc
507
507
  `;
508
508
  }
509
509
  return createFiles(state.projectPath, files, variables);
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"AAuiBA,eAAO,MAAM,OAAO,GAClB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,oBAAoB,MAAM,EAC1B,SAAS,MAAM,EAAE,EACjB,cAAc,MAAM,KACnB,MAAM,EAWR,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"AAiiBA,eAAO,MAAM,OAAO,GAClB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,oBAAoB,MAAM,EAC1B,SAAS,MAAM,EAAE,EACjB,cAAc,MAAM,KACnB,MAAM,EAWR,CAAC"}
@@ -147,12 +147,6 @@ const DatabaseAndCloudflare = (databaseNormalized, dbLines, sqliteDbPath) => [
147
147
  ...dbLines,
148
148
  '',
149
149
  '# Generic SQL defaults (used by some adapters)',
150
- 'DB_HOST=localhost',
151
- 'DB_PORT=5432',
152
- 'DB_DATABASE=',
153
- 'DB_USERNAME=postgres',
154
- 'DB_PASSWORD=',
155
- 'DB_READ_HOSTS=',
156
150
  '',
157
151
  '# PostgreSQL (optional)',
158
152
  'DB_PORT_POSTGRESQL=5432',
@@ -355,14 +349,14 @@ const WorkersSchedulingAndSSE = () => [
355
349
  '# WORKERS / SCHEDULER',
356
350
  '# ============================================================================',
357
351
  '',
358
- 'WORKER_ENABLED=true',
352
+ 'WORKER_ENABLED=false',
359
353
  'WORKER_AUTO_START=false',
360
354
  'WORKER_CONCURRENCY=5',
361
355
  'WORKER_TIMEOUT=60',
362
356
  'WORKER_RETRIES=3',
363
357
  'WORKER_PRIORITY=1',
364
358
  'WORKER_HEALTH_CHECK_INTERVAL=60',
365
- 'WORKER_CLUSTER_MODE=true',
359
+ 'WORKER_CLUSTER_MODE=false',
366
360
  'WORKER_REGION=us-east-1',
367
361
  '',
368
362
  'WORKER_PERSISTENCE_DRIVER=memory',
@@ -46,6 +46,14 @@ export declare const appConfig: Readonly<{
46
46
  * Indicates if the application is running inside a Docker worker container
47
47
  */
48
48
  readonly dockerWorker: boolean;
49
+ /**
50
+ * Indicates if the application is running as a Cloudflare Worker
51
+ */
52
+ readonly cloudflareWorker: boolean;
53
+ /**
54
+ * Indicates if the application is running as a generic Worker (e.g. Cloudflare, AWS Lambda)
55
+ */
56
+ readonly worker: boolean;
49
57
  /**
50
58
  * Application timezone
51
59
  */
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/config/app.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAoI3D,QAAA,MAAM,UAAU,QAAO,MAAM,CAAC,UAA2B,CAAC;AA2F1D,eAAO,MAAM,SAAS;IA3EpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;kCACc,OAAO;IAIxB;;OAEG;iCACa,OAAO;IAIvB;;OAEG;8BACU,OAAO;IAIpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;+BApFkB,MAAM,CAAC,UAAU;kCAhDd,MAAM;EA2IoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../src/config/app.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,cAAc,CAAC;AAoI3D,QAAA,MAAM,UAAU,QAAO,MAAM,CAAC,UAA2B,CAAC;AAqG1D,eAAO,MAAM,SAAS;IArFpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;kCACc,OAAO;IAIxB;;OAEG;iCACa,OAAO;IAIvB;;OAEG;8BACU,OAAO;IAIpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;+BA9FkB,MAAM,CAAC,UAAU;kCAhDd,MAAM;EAqJoB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,CAAC;AAEtB,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC"}
package/src/config/app.js CHANGED
@@ -169,6 +169,14 @@ const appConfigObj = {
169
169
  * Indicates if the application is running inside a Docker worker container
170
170
  */
171
171
  dockerWorker: readEnvBool('DOCKER_WORKER', Env.DOCKER_WORKER),
172
+ /**
173
+ * Indicates if the application is running as a Cloudflare Worker
174
+ */
175
+ cloudflareWorker: readEnvBool('CLOUDFLARE_WORKER', Env.CLOUDFLARE_WORKER),
176
+ /**
177
+ * Indicates if the application is running as a generic Worker (e.g. Cloudflare, AWS Lambda)
178
+ */
179
+ worker: readEnvBool('WORKER_ENABLED', Env.WORKER_ENABLED),
172
180
  /**
173
181
  * Application timezone
174
182
  */
@@ -189,12 +189,14 @@ export declare const Env: Readonly<{
189
189
  LOG_LEVEL: "debug" | "info" | "warn" | "error";
190
190
  LOG_FORMAT: string;
191
191
  LOG_CHANNEL: string;
192
- DOCKER_WORKER: boolean;
193
192
  DISABLE_LOGGING: boolean;
194
193
  LOG_HTTP_REQUEST: boolean;
195
194
  LOG_TO_FILE: boolean;
196
195
  LOG_ROTATION_SIZE: number;
197
196
  LOG_ROTATION_DAYS: number;
197
+ CLOUDFLARE_WORKER: boolean;
198
+ WORKER_ENABLED: boolean;
199
+ DOCKER_WORKER: boolean;
198
200
  ZINTRUST_PROJECT_ROOT: string;
199
201
  ZINTRUST_ALLOW_POSTINSTALL: string;
200
202
  ZINTRUST_ENV_FILE: string;
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBlF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgFxF,CAAC;AAEH,eAAO,MAAM,aAAa,QAAO,MAchC,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../src/config/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAuBlF,eAAO,MAAM,cAAc,QAAO,WAAW,GAAG,SAAwB,CAAC;AAEzE,eAAO,MAAM,mBAAmB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,MAKzE,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAIxD,CAAC;AAEF,eAAO,MAAM,MAAM,GAAI,KAAK,MAAM,EAAE,cAAc,MAAM,KAAG,MAI1D,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,EAAE,eAAe,MAAM,KAAG,MAI7D,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,EAAE,eAAe,OAAO,KAAG,OAI7D,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,IAGhD,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,KAAG,IAInC,CAAC;AAEF,eAAO,MAAM,SAAS,GAAI,QAAQ,SAAS,GAAG,IAAI,KAAG,IAEpD,CAAC;AAEF,eAAO,MAAM,QAAQ,QAAO,MAAM,CAAC,MAAM,EAAE,MAAM,CAOhD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAAO,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAKjE,CAAC;AACF,eAAO,MAAM,mBAAmB,QAAuC,CAAC;AAKxE,eAAO,MAAM,GAAG;eA3DS,MAAM,iBAAiB,MAAM,KAAG,MAAM;kBAMnC,MAAM,gBAAgB,MAAM,KAAG,MAAM;mBAYpC,MAAM,iBAAiB,OAAO,KAAG,OAAO;oBANvC,MAAM,iBAAiB,MAAM,KAAG,MAAM;eAY3C,MAAM,SAAS,MAAM,KAAG,IAAI;iBAK1B,MAAM,KAAG,IAAI;wBAMN,SAAS,GAAG,IAAI,KAAG,IAAI;oBAI7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;cAgCJ,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAgOpB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoFxF,CAAC;AAEH,eAAO,MAAM,aAAa,QAAO,MAchC,CAAC"}
package/src/config/env.js CHANGED
@@ -284,12 +284,15 @@ export const Env = Object.freeze({
284
284
  LOG_LEVEL: get('LOG_LEVEL', getDefaultLogLevel()),
285
285
  LOG_FORMAT: get('LOG_FORMAT', 'text'),
286
286
  LOG_CHANNEL: get('LOG_CHANNEL', ''),
287
- DOCKER_WORKER: getBool('DOCKER_WORKER', false),
288
287
  DISABLE_LOGGING: getBool('DISABLE_LOGGING', false),
289
288
  LOG_HTTP_REQUEST: getBool('LOG_HTTP_REQUEST', false),
290
289
  LOG_TO_FILE: getBool('LOG_TO_FILE', false),
291
290
  LOG_ROTATION_SIZE: getInt('LOG_ROTATION_SIZE', 10485760),
292
291
  LOG_ROTATION_DAYS: getInt('LOG_ROTATION_DAYS', 7),
292
+ // Worker-specific
293
+ CLOUDFLARE_WORKER: getBool('CLOUDFLARE_WORKER', false),
294
+ WORKER_ENABLED: getBool('WORKER_ENABLED', false),
295
+ DOCKER_WORKER: getBool('DOCKER_WORKER', false),
293
296
  // zintrust-specific
294
297
  ZINTRUST_PROJECT_ROOT: get('ZINTRUST_PROJECT_ROOT', ''),
295
298
  ZINTRUST_ALLOW_POSTINSTALL: get('ZINTRUST_ALLOW_POSTINSTALL', ''),
@@ -30,6 +30,8 @@ export declare const config: Readonly<{
30
30
  readonly isTesting: () => boolean;
31
31
  readonly debug: boolean;
32
32
  readonly dockerWorker: boolean;
33
+ readonly cloudflareWorker: boolean;
34
+ readonly worker: boolean;
33
35
  readonly timezone: string;
34
36
  readonly requestTimeout: number;
35
37
  readonly maxBodySize: number;
@@ -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;;;;;;;;;;;;;;;;;;;;;;;8BAZc,CAAC;;;;;;;;;;;;;;;;;;;;2EAAC,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,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;GAGG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;8BAZc,CAAC;;;;;;;;;;;;;;;;;;;;2EAAC,CAAA;;;;;;;;;;;;;;;;;;;;8BAD2B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA+C8X,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAHnb,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC"}
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.1.52
2
+ * @zintrust/core v0.1.54
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-02-27T08:07:36.891Z
8
+ * Built: 2026-03-02T09:00:32.128Z
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-02-27T08:07:36.860Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-02T09:00:32.097Z'; // 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":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAghBD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAI1C,CAAC;AAIH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,cAAc,GAAG,SAAqB,EAC9C,uBAA0B,KACzB,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAMxC,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAoBtF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAWnD"}
1
+ {"version":3,"file":"Database.d.ts","sourceRoot":"","sources":["../../../src/orm/Database.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWxD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,MAAM,WAAW,SAAS;IACxB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,WAAW,IAAI,OAAO,CAAC;IACvB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACjF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC7B,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAAC;IACnC,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1F,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IAC1E,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC;IAC3F,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,gBAAgB,CAAC;IACvD,OAAO,IAAI,eAAe,CAAC;IAC3B,SAAS,IAAI,cAAc,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;CACjB;AAqhBD,eAAO,MAAM,QAAQ;IACnB;;OAEG;oBACa,cAAc,GAAG,SAAS;EAI1C,CAAC;AAIH,eAAO,MAAM,oBAAoB,GAC/B,SAAQ,cAAc,GAAG,SAAqB,EAC9C,uBAA0B,KACzB,OAAO,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAMxC,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,UAAU,SAAY,GAAG,SAAS,CAoBtF;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAWnD"}
@@ -116,7 +116,7 @@ const resolveWorkersAdapter = (cfg) => {
116
116
  const workersEnv = Cloudflare.getWorkersEnv();
117
117
  if (workersEnv === null)
118
118
  return null;
119
- Logger.info('[Database] Resolving adapter for Cloudflare Workers runtime', {
119
+ Logger.debug('[Database] Resolving adapter for Cloudflare Workers runtime', {
120
120
  driver: cfg.driver,
121
121
  useMySqlProxy: Env.getBool('USE_MYSQL_PROXY', false),
122
122
  mysqlProxyUrlConfigured: Env.get('MYSQL_PROXY_URL', '').trim() !== '',
@@ -138,13 +138,16 @@ const createAdapter = (cfg) => {
138
138
  const explicitProxy = resolveExplicitProxyAdapter(cfg);
139
139
  if (explicitProxy)
140
140
  return explicitProxy;
141
- const workersAdapter = resolveWorkersAdapter(cfg);
142
- if (workersAdapter)
143
- return workersAdapter;
141
+ // First: Check the specific configured driver via registry
144
142
  const registered = DatabaseAdapterRegistry.get(cfg.driver);
145
143
  if (registered !== undefined) {
146
144
  return registered(cfg);
147
145
  }
146
+ // Second: Try workers adapter resolution (only if registry lookup failed)
147
+ const workersAdapter = resolveWorkersAdapter(cfg);
148
+ if (workersAdapter)
149
+ return workersAdapter;
150
+ // Third: Fallback to direct adapter creation
148
151
  switch (cfg.driver) {
149
152
  case 'postgresql':
150
153
  return PostgreSQLAdapter.create(cfg);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Documentation Routes
3
- * Serves static files from /doc/* paths with relaxed CSP headers.
3
+ * Serves static files from /zintrust-doc/* paths with relaxed CSP headers.
4
4
  */
5
5
  import type { IRouter } from './Router';
6
6
  import type { IResponse } from '../http/Response';
@@ -1 +1 @@
1
- {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routes/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,aAAa,EACb,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AAkCjC;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,SAAS,KAAG,IAWhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,MAAM,EACf,UAAU,SAAS,KAClB,OAAO,CAAC,OAAO,CAgDjB,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,IAMnD,CAAC;;gCANwC,OAAO,KAAG,IAAI;2CA5EH,SAAS,KAAG,IAAI;2CAiB1D,MAAM,YACL,SAAS,KAClB,OAAO,CAAC,OAAO,CAAC;;AAiEnB,wBAIE"}
1
+ {"version":3,"file":"doc.d.ts","sourceRoot":"","sources":["../../../src/routes/doc.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAIhD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,aAAa,EACb,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AAmCjC;;GAEG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,SAAS,KAAG,IAWhE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,MAAM,EACf,UAAU,SAAS,KAClB,OAAO,CAAC,OAAO,CAgDjB,CAAC;AASF,eAAO,MAAM,iBAAiB,GAAI,QAAQ,OAAO,KAAG,IAMnD,CAAC;;gCANwC,OAAO,KAAG,IAAI;2CA5EH,SAAS,KAAG,IAAI;2CAiB1D,MAAM,YACL,SAAS,KAClB,OAAO,CAAC,OAAO,CAAC;;AAiEnB,wBAIE"}
package/src/routes/doc.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Documentation Routes
3
- * Serves static files from /doc/* paths with relaxed CSP headers.
3
+ * Serves static files from /zintrust-doc/* paths with relaxed CSP headers.
4
4
  */
5
5
  import { HTTP_HEADERS } from '../config/constants.js';
6
6
  import { MIME_TYPES_MAP, resolveSafePath, tryDecodeURIComponent } from './common.js';
@@ -16,6 +16,7 @@ export { MIME_TYPES_MAP } from './common.js';
16
16
  */
17
17
  // Backward-compatible re-exports
18
18
  export { findPackageRoot, findPackageRootAsync, getFrameworkPublicRoots, getFrameworkPublicRootsAsync, getPublicRoot, getPublicRootAsync, } from './publicRoot.js';
19
+ const docPath = 'zintrust-doc';
19
20
  const PUBLIC_ROOT_CACHE_TTL_MS = 3000000000; // 50 minutes, can be adjusted as needed
20
21
  let cachedPublicRoot = null;
21
22
  const getCachedPublicRootAsync = async () => {
@@ -33,10 +34,10 @@ const getCachedPublicRootAsync = async () => {
33
34
  const mapStaticPathAsync = async (urlPath) => {
34
35
  const publicRoot = await getCachedPublicRootAsync();
35
36
  const normalize = (p) => (p.startsWith('/') ? p.slice(1) : p);
36
- if (urlPath === '/doc' || urlPath === '/doc/')
37
+ if (urlPath === `/${docPath}` || urlPath === `/${docPath}/`)
37
38
  return publicRoot;
38
- if (urlPath.startsWith('/doc/')) {
39
- const rawRelative = urlPath.slice('/doc/'.length);
39
+ if (urlPath.startsWith(`/${docPath}/`)) {
40
+ const rawRelative = urlPath.slice(`/${docPath}/`.length);
40
41
  const normalizedRelative = tryDecodeURIComponent(rawRelative).replaceAll('\\', '/');
41
42
  return resolveSafePath(publicRoot, normalize(normalizedRelative));
42
43
  }
@@ -112,10 +113,10 @@ const handleDocRequest = async (req, res) => {
112
113
  };
113
114
  export const registerDocRoutes = (router) => {
114
115
  // Root docs entrypoints.
115
- Router.get(router, '/doc', handleDocRequest);
116
- Router.get(router, '/doc/', handleDocRequest);
117
- // Greedy path match for nested assets like /doc/assets/app.js
118
- Router.get(router, '/doc/:path*', handleDocRequest);
116
+ Router.get(router, `/${docPath}`, handleDocRequest);
117
+ Router.get(router, `/${docPath}/`, handleDocRequest);
118
+ // Greedy path match for nested assets like /zintrust-doc/assets/app.js
119
+ Router.get(router, `/${docPath}/:path*`, handleDocRequest);
119
120
  };
120
121
  export default {
121
122
  registerDocRoutes,
@@ -1 +1 @@
1
- {"version":3,"file":"PluginAutoImports.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginAutoImports.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,eAAe,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA0GN,eAAO,MAAM,iBAAiB;IAC5B;;;;;;OAMG;mCACkC,OAAO,CAAC,YAAY,CAAC;qCAmEnB,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;EAavE,CAAC"}
1
+ {"version":3,"file":"PluginAutoImports.d.ts","sourceRoot":"","sources":["../../../src/runtime/PluginAutoImports.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAChC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,WAAW,GAAG,eAAe,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA0GN,eAAO,MAAM,iBAAiB;IAC5B;;;;;;OAMG;mCACkC,OAAO,CAAC,YAAY,CAAC;qCAkEnB,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC;EAavE,CAAC"}
@@ -107,7 +107,6 @@ export const PluginAutoImports = Object.freeze({
107
107
  // Filter out non-existent candidates first
108
108
  const existingCandidates = candidates.filter((candidate) => existsSync(candidate));
109
109
  if (existingCandidates.length === 0) {
110
- Logger.debug('[plugins] No plugin auto-imports file found', { projectRoot, candidates });
111
110
  return { ok: false, reason: 'not-found' };
112
111
  }
113
112
  const tryImportCandidate = async (candidate) => {
@@ -1 +1 @@
1
- {"version":3,"file":"detectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/detectRuntime.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,YAAY,GAAG,aAAa,CAAC;AAE9E,eAAO,MAAM,aAAa,QAAO,OAUhC,CAAC;AAUF,eAAO,MAAM,cAAc,QAAO,WA6BjC,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAwBhB,CAAC"}
1
+ {"version":3,"file":"detectRuntime.d.ts","sourceRoot":"","sources":["../../../src/runtime/detectRuntime.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,WAAW,GAAG,oBAAoB,GAAG,YAAY,GAAG,aAAa,CAAC;AAE9E,eAAO,MAAM,aAAa,QAAO,OAWhC,CAAC;AAUF,eAAO,MAAM,cAAc,QAAO,WA6BjC,CAAC;AAEF,eAAO,MAAM,aAAa,QAAO;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CAwBhB,CAAC"}
@@ -1,8 +1,11 @@
1
+ import { appConfig } from '../config/index.js';
1
2
  import { ZintrustLang } from '../lang/lang.js';
2
3
  export const isNodeRuntime = () => {
3
4
  // Avoid importing any `node:*` modules so this file remains Worker-safe.
4
5
  // In Workers/Deno, `process` is typically undefined.
5
- return (typeof process !== ZintrustLang.UNDEFINED &&
6
+ const detectRuntime = appConfig.detectRuntime() !== 'cloudflare';
7
+ return (detectRuntime &&
8
+ typeof process !== ZintrustLang.UNDEFINED &&
6
9
  typeof process === ZintrustLang.OBJECT &&
7
10
  process !== null &&
8
11
  typeof process.versions === ZintrustLang.OBJECT);
package/src/start.js CHANGED
@@ -35,7 +35,7 @@ export const start = async () => {
35
35
  // Compiled output places bootstrap at `dist/src/boot/bootstrap.js`.
36
36
  // This file compiles to `dist/src/start.js`, so relative import is stable.
37
37
  // In unit tests, importing bootstrap has heavy side effects (starts server + exits).
38
- await import('./boot/' + ZintrustLang.BOOTSTRAPJS);
38
+ await import('./boot/bootstrap.js');
39
39
  };
40
40
  /**
41
41
  * Cloudflare Workers entry (module worker style).
@@ -10,11 +10,13 @@ import {
10
10
  Auth,
11
11
  JwtManager,
12
12
  Logger,
13
- TokenRevocation,
14
13
  getString,
15
14
  getValidatedBody,
15
+ isUndefinedOrNull,
16
16
  } from '@zintrust/core';
17
17
 
18
+
19
+
18
20
  const pickPublicUser = (row: UserRow): { id: unknown; name: string; email: string } => {
19
21
  return {
20
22
  id: row.id,
@@ -77,9 +79,15 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
77
79
  return undefined;
78
80
  })();
79
81
 
80
- const token = JwtManager.signAccessToken({
82
+ // Bulletproof Auth (device binding) expects a device id header to match a JWT claim.
83
+ // For the example app, we mint a stable device id derived from the subject.
84
+ // Production apps should issue a per-device id and manage a per-device signing secret.
85
+ const deviceId = isUndefinedOrNull(subject) ? undefined : `dev-${subject}`;
86
+
87
+ const token = await JwtManager.signAccessToken({
81
88
  sub: subject,
82
89
  email,
90
+ ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
83
91
  });
84
92
 
85
93
  Logger.info('AuthController.login: successful login', {
@@ -92,6 +100,7 @@ async function login(req: IRequest, res: IResponse): Promise<void> {
92
100
  res.json({
93
101
  token,
94
102
  token_type: 'Bearer',
103
+ ...(isUndefinedOrNull(deviceId) ? {} : { deviceId }),
95
104
  user,
96
105
  });
97
106
  } catch (error) {
@@ -166,7 +175,10 @@ async function register(req: IRequest, res: IResponse): Promise<void> {
166
175
 
167
176
  res.setStatus(201).json({ message: 'Registered' });
168
177
  } else {
169
- Logger.error('Failed to retrieve inserted user ID');
178
+ Logger.error('Failed to retrieve inserted user ID', {
179
+ email,
180
+ ip: ipAddress,
181
+ });
170
182
  res.setStatus(500).json({ error: 'Registration failed' });
171
183
  }
172
184
  return;
@@ -187,10 +199,27 @@ async function register(req: IRequest, res: IResponse): Promise<void> {
187
199
  async function logout(req: IRequest, res: IResponse): Promise<void> {
188
200
  const authHeader =
189
201
  typeof req.getHeader === 'function' ? req.getHeader('authorization') : undefined;
190
- await TokenRevocation.revoke(authHeader);
202
+ await JwtManager.logout(authHeader);
191
203
  res.json({ message: 'Logged out' });
192
204
  }
193
205
 
206
+ /**
207
+ * Logs out the current user from all devices by removing all active sessions for their subject.
208
+ *
209
+ * With session allowlist enforcement, deleting a user's session records causes any previously issued
210
+ * tokens to become unauthorized (401) immediately.
211
+ */
212
+ async function logoutAll(req: IRequest, res: IResponse): Promise<void> {
213
+ const sub = typeof req.user?.sub === 'string' ? req.user.sub.trim() : '';
214
+ if (sub === '') {
215
+ res.setStatus(401).json({ error: 'Unauthorized' });
216
+ return;
217
+ }
218
+
219
+ await JwtManager.logoutAll(sub);
220
+ res.json({ message: 'Logged out everywhere' });
221
+ }
222
+
194
223
  /**
195
224
  * Refreshes the user's JWT access token.
196
225
  * Generates a new token with the same claims as the current user.
@@ -206,7 +235,7 @@ async function refresh(req: IRequest, res: IResponse): Promise<void> {
206
235
  return;
207
236
  }
208
237
 
209
- const token = JwtManager.signAccessToken(user);
238
+ const token = await JwtManager.signAccessToken(user);
210
239
  res.json({ token, token_type: 'Bearer' });
211
240
  }
212
241
 
@@ -216,6 +245,7 @@ export const AuthController = Object.freeze({
216
245
  login,
217
246
  register,
218
247
  logout,
248
+ logoutAll,
219
249
  refresh,
220
250
  };
221
251
  },
@@ -23,6 +23,7 @@ export type AuthControllerApi = {
23
23
  login(req: IRequest, res: IResponse): Promise<void>;
24
24
  register(req: IRequest, res: IResponse): Promise<void>;
25
25
  logout(req: IRequest, res: IResponse): Promise<void>;
26
+ logoutAll(req: IRequest, res: IResponse): Promise<void>;
26
27
  refresh(req: IRequest, res: IResponse): Promise<void>;
27
28
  };
28
29
 
@@ -1,5 +1,9 @@
1
- import { MigrationSchema, type Blueprint } from '@zintrust/core';
2
- import type { IDatabase } from '@zintrust/core';
1
+ /**
2
+ * Migration: CreateUsersTable
3
+ * Creates users table
4
+ */
5
+ import type { Blueprint, IDatabase } from '@zintrust/core';
6
+ import { MigrationSchema } from '@zintrust/core';
3
7
 
4
8
  export interface Migration {
5
9
  up(db: IDatabase): Promise<void>;
@@ -15,10 +19,7 @@ export const migration: Migration = {
15
19
  table.string('name');
16
20
  table.string('email').unique();
17
21
  table.string('password');
18
- table.timestamp('email_verified_at').nullable();
19
- table.boolean('active').default(true);
20
22
  table.timestamps();
21
- table.timestamp('deleted_at').nullable();
22
23
  });
23
24
  },
24
25
 
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Migration: CreateJwtRevocationsTable
3
+ * Creates a storage table for JWT token revocation (token invalidation).
4
+ */
5
+ import type { Blueprint, IDatabase } from '@zintrust/core';
6
+ import { MigrationSchema } from '@zintrust/core';
7
+
8
+ export interface Migration {
9
+ up(db: IDatabase): Promise<void>;
10
+ down(db: IDatabase): Promise<void>;
11
+ }
12
+
13
+ export const migration: Migration = {
14
+ async up(db: IDatabase): Promise<void> {
15
+ const schema = MigrationSchema.create(db);
16
+
17
+ await schema.create('zintrust_jwt_revocations', (table: Blueprint) => {
18
+ table.id();
19
+ table.string('user_id', 191).nullable();
20
+ table.string('jti', 128).unique();
21
+ table.string('sub', 191).nullable();
22
+ table.string('kind', 16).notNullable().default('revoked');
23
+ table.bigInteger('expires_at_ms');
24
+ table.timestamp('revoked_at').notNullable().default('CURRENT_TIMESTAMP');
25
+
26
+ table.index(['user_id']);
27
+ table.index(['kind']);
28
+ table.index(['expires_at_ms']);
29
+ });
30
+ },
31
+
32
+ async down(db: IDatabase): Promise<void> {
33
+ const schema = MigrationSchema.create(db);
34
+ await schema.dropIfExists('zintrust_jwt_revocations');
35
+ },
36
+ };
@@ -5,6 +5,18 @@
5
5
  "compatibility_flags": ["nodejs_compat"],
6
6
  "workers_dev": true,
7
7
  "minify": false,
8
+ "alias": {
9
+ "@routes/api.ts": "./routes/api.ts",
10
+ "../zintrust.plugins.wg.js": "./src/zintrust.plugins.wg.ts",
11
+ "@runtime-config/broadcast.ts": "./config/broadcast.ts",
12
+ "@runtime-config/cache.ts": "./config/cache.ts",
13
+ "@runtime-config/database.ts": "./config/database.ts",
14
+ "@runtime-config/mail.ts": "./config/mail.ts",
15
+ "@runtime-config/storage.ts": "./config/storage.ts",
16
+ "@runtime-config/queue.ts": "./config/queue.ts",
17
+ "@runtime-config/notification.ts": "./config/notification.ts",
18
+ "@runtime-config/middleware.ts": "./config/middleware.ts"
19
+ },
8
20
 
9
21
  "vars": {
10
22
  "ENVIRONMENT": "development"
@@ -1,54 +0,0 @@
1
- // @ts-nocheck - Example middleware - WIP
2
- /**
3
- * Profiler Middleware
4
- * Enables request profiling when ENABLE_PROFILER environment variable is set
5
- */
6
-
7
- import { Middleware, Logger , RequestProfiler} from '@zintrust/core';
8
-
9
- /**
10
- * ProfilerMiddleware wraps request execution with performance profiling
11
- * Enabled via ENABLE_PROFILER=true environment variable
12
- * Attaches profiling report to response headers
13
- */
14
- export const ProfilerMiddleware: Middleware = async (req, res, next) => {
15
- const isEnabled = process.env.ENABLE_PROFILER === 'true';
16
-
17
- if (!isEnabled) {
18
- // Pass through without profiling
19
- await next();
20
- return;
21
- }
22
-
23
- const profiler = RequestProfiler.create();
24
- const queryLogger = profiler.getQueryLogger();
25
-
26
- // Set up query logging if database is available
27
- const db = req.context.db;
28
- if (db !== undefined && db !== null && typeof db.onAfterQuery === 'function') {
29
- db.onAfterQuery((sql: string, params: unknown[], duration: number) => {
30
- queryLogger.logQuery(sql, params, duration, 'middleware-profiling');
31
- });
32
- }
33
-
34
- // Capture request execution
35
- const profile = await profiler.captureRequest(async () => next());
36
-
37
- // Attach profile to response
38
- res.locals.profile = profile;
39
-
40
- // Add profiling report to response header
41
- try {
42
- const report = profiler.generateReport(profile);
43
- res.setHeader('X-Profiler-Report', Buffer.from(report).toString('base64'));
44
- res.setHeader('X-Profiler-Queries', profile.queriesExecuted.toString());
45
- res.setHeader('X-Profiler-Duration', profile.duration.toString());
46
-
47
- if (profile.n1Patterns.length > 0) {
48
- res.setHeader('X-Profiler-N1-Patterns', profile.n1Patterns.length.toString());
49
- }
50
- } catch (error) {
51
- // Silently fail if header encoding fails
52
- Logger.error('Failed to encode profiler report header:', error);
53
- }
54
- };
@@ -1,293 +0,0 @@
1
- /**
2
- * Example Middleware
3
- * Common middleware patterns for ZinTrust
4
- */
5
-
6
- import type { IJwtManager, JwtAlgorithm, JwtManagerType, ISchema, SchemaType, CsrfTokenManagerType, ICsrfTokenManager } from '@zintrust/core';
7
- import { Logger , Validator, IRequest, IResponse, XssProtection} from '@zintrust/core';
8
-
9
-
10
- type JwtManagerInput = IJwtManager | JwtManagerType;
11
- type CsrfManagerInput = ICsrfTokenManager | CsrfTokenManagerType;
12
-
13
- const resolveJwtManager = (jwtManager: JwtManagerInput): IJwtManager =>
14
- 'verify' in jwtManager ? jwtManager : jwtManager.create();
15
-
16
- const resolveCsrfManager = (csrfManager: CsrfManagerInput): ICsrfTokenManager =>
17
- 'validateToken' in csrfManager ? csrfManager : csrfManager.create();
18
-
19
- type ValidationSchema = ISchema | SchemaType;
20
-
21
- const resolveSchema = (schema: ValidationSchema): ISchema =>
22
- 'getRules' in schema ? schema : schema.create();
23
-
24
- /**
25
- * Authentication Middleware
26
- * Verify user is authenticated
27
- */
28
- export const authMiddleware = async (
29
- req: IRequest,
30
- res: IResponse,
31
- next: () => Promise<void>
32
- ): Promise<void> => {
33
- const token = req.getHeader('authorization');
34
-
35
- if (token === undefined || token === '') {
36
- res.setStatus(401).json({ error: 'Unauthorized' });
37
- return;
38
- }
39
-
40
- await next();
41
- };
42
-
43
- /**
44
- * CORS Middleware
45
- * Handle CORS headers
46
- */
47
- export const corsMiddleware = async (
48
- req: IRequest,
49
- res: IResponse,
50
- next: () => Promise<void>
51
- ): Promise<void> => {
52
- res.setHeader('Access-Control-Allow-Origin', '*');
53
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
54
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
55
-
56
- if (req.getMethod() === 'OPTIONS') {
57
- res.setStatus(200).send('');
58
- return;
59
- }
60
-
61
- await next();
62
- };
63
-
64
- /**
65
- * JSON Request Middleware
66
- * Parse JSON request bodies
67
- */
68
- export const jsonMiddleware = async (
69
- req: IRequest,
70
- res: IResponse,
71
- next: () => Promise<void>
72
- ): Promise<void> => {
73
- if (req.getMethod() === 'GET' || req.getMethod() === 'DELETE') {
74
- await next();
75
- return;
76
- }
77
-
78
- if (req.isJson() === false) {
79
- res.setStatus(415).json({ error: 'Content-Type must be application/json' });
80
- return;
81
- }
82
-
83
- await next();
84
- };
85
-
86
- /**
87
- * Logging Middleware
88
- * Log all requests
89
- */
90
- export const loggingMiddleware = async (
91
- req: IRequest,
92
- res: IResponse,
93
- next: () => Promise<void>
94
- ): Promise<void> => {
95
- const startTime = Date.now();
96
- const method = req.getMethod();
97
- const path = req.getPath();
98
-
99
- Logger.info(`→ ${method} ${path}`);
100
-
101
- await next();
102
-
103
- const duration = Date.now() - startTime;
104
- const status = res.getStatus();
105
- Logger.info(`← ${status} ${method} ${path} (${duration}ms)`);
106
- };
107
-
108
- /**
109
- * Rate Limiting Middleware
110
- * Simple in-memory rate limiting
111
- */
112
- const requestCounts = new Map<string, number[]>();
113
-
114
- export const rateLimitMiddleware = async (
115
- req: IRequest,
116
- res: IResponse,
117
- next: () => Promise<void>
118
- ): Promise<void> => {
119
- const ip = req.getRaw().socket.remoteAddress ?? 'unknown';
120
- const now = Date.now();
121
- const windowMs = 60 * 1000; // 1 minute
122
- const maxRequests = 100;
123
-
124
- if (requestCounts.has(ip) === false) {
125
- requestCounts.set(ip, []);
126
- }
127
-
128
- const requests = requestCounts.get(ip) ?? [];
129
- const recentRequests = requests.filter((time) => now - time < windowMs);
130
-
131
- if (recentRequests.length >= maxRequests) {
132
- res.setStatus(429).json({ error: 'Too many requests' });
133
- return;
134
- }
135
-
136
- recentRequests.push(now);
137
- requestCounts.set(ip, recentRequests);
138
-
139
- await next();
140
- };
141
-
142
- /**
143
- * Trailing Slash Middleware
144
- * Redirect URLs with trailing slashes
145
- */
146
- export const trailingSlashMiddleware = async (
147
- req: IRequest,
148
- res: IResponse,
149
- next: () => Promise<void>
150
- ): Promise<void> => {
151
- const path = req.getPath();
152
-
153
- if (path.length > 1 && path.endsWith('/') === true) {
154
- const withoutSlash = path.slice(0, -1);
155
- res.redirect(withoutSlash, 301);
156
- return;
157
- }
158
-
159
- await next();
160
- };
161
-
162
- /**
163
- * JWT Authentication Middleware
164
- * Verify JWT token and extract claims
165
- */
166
- export const jwtMiddleware = (jwtManager: JwtManagerInput, algorithm: JwtAlgorithm = 'HS256') => {
167
- return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
168
- const authHeader = req.getHeader('authorization');
169
-
170
- if (authHeader === undefined || authHeader === '') {
171
- res.setStatus(401).json({ error: 'Missing authorization header' });
172
- return;
173
- }
174
-
175
- const authHeaderStr = Array.isArray(authHeader) ? authHeader[0] : authHeader;
176
- const [scheme, token] = authHeaderStr.split(' ');
177
-
178
- if (scheme !== 'Bearer' || token === undefined || token === '') {
179
- res.setStatus(401).json({ error: 'Invalid authorization header format' });
180
- return;
181
- }
182
-
183
- try {
184
- const payload = resolveJwtManager(jwtManager).verify(token, algorithm);
185
- // Store in request context (TypeScript allows dynamic properties)
186
- req.user = payload;
187
- await next();
188
- } catch (error) {
189
- Logger.error('JWT verification failed:', error);
190
- res.setStatus(401).json({ error: 'Invalid or expired token' });
191
- }
192
- };
193
- };
194
-
195
- /**
196
- * CSRF Protection Middleware
197
- * Validate CSRF tokens for state-changing requests
198
- */
199
- export const csrfMiddleware = (csrfManager: CsrfManagerInput) => {
200
- return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
201
- const method = req.getMethod();
202
-
203
- // Only validate on state-changing requests
204
- if (['POST', 'PUT', 'PATCH', 'DELETE'].includes(method) === false) {
205
- await next();
206
- return;
207
- }
208
-
209
- const sessionId = req.sessionId ?? req.getHeader('x-session-id');
210
-
211
- if (sessionId === undefined || sessionId === '') {
212
- res.setStatus(400).json({ error: 'Missing session ID' });
213
- return;
214
- }
215
-
216
- const csrfToken = req.getHeader('x-csrf-token');
217
-
218
- if (csrfToken === undefined || csrfToken === '') {
219
- res.setStatus(403).json({ error: 'Missing CSRF token' });
220
- return;
221
- }
222
-
223
- const isValid = await resolveCsrfManager(csrfManager).validateToken(
224
- String(sessionId),
225
- String(csrfToken)
226
- );
227
-
228
- if (isValid === false) {
229
- res.setStatus(403).json({ error: 'Invalid or expired CSRF token' });
230
- return;
231
- }
232
-
233
- await next();
234
- };
235
- };
236
-
237
- /**
238
- * Input Validation Middleware
239
- * Validate request body against schema
240
- */
241
- export const validationMiddleware = (schema: SchemaType) => {
242
- return async (req: IRequest, res: IResponse, next: () => Promise<void>): Promise<void> => {
243
- if (req.getMethod() === 'GET' || req.getMethod() === 'DELETE') {
244
- await next();
245
- return;
246
- }
247
-
248
- try {
249
- const body = req.body ?? {};
250
- Validator.validate(body, resolveSchema(schema));
251
- await next();
252
- } catch (error: unknown) {
253
- Logger.error('Validation error:', error);
254
- const newError = error as Error & { toObject?: () => Record<string, unknown> };
255
- if (
256
- error !== undefined &&
257
- 'toObject' in newError &&
258
- typeof newError.toObject === 'function'
259
- ) {
260
- res.setStatus(422).json({ errors: newError.toObject() });
261
- } else {
262
- res.setStatus(400).json({ error: 'Invalid request body' });
263
- }
264
- }
265
- };
266
- };
267
-
268
- /**
269
- * XSS Protection Middleware
270
- * Sanitize and escape user input
271
- */
272
- export const xssProtectionMiddleware = async (
273
- req: IRequest,
274
- res: IResponse,
275
- next: () => Promise<void>
276
- ): Promise<void> => {
277
- // Add XSS protection headers
278
- res.setHeader('X-Content-Type-Options', 'nosniff');
279
- res.setHeader('X-Frame-Options', 'DENY');
280
- res.setHeader('X-XSS-Protection', '1; mode=block');
281
-
282
- // Sanitize request body if present
283
- const body = req.body;
284
- if (body !== undefined && body !== null && typeof body === 'object') {
285
- for (const [key, value] of Object.entries(body)) {
286
- if (typeof value === 'string') {
287
- body[key] = XssProtection.escape(value);
288
- }
289
- }
290
- }
291
-
292
- await next();
293
- };
@@ -1,28 +0,0 @@
1
- import { MigrationSchema, type Blueprint } from '@zintrust/core';
2
- import type { IDatabase } from '@zintrust/core';
3
-
4
- export interface Migration {
5
- up(db: IDatabase): Promise<void>;
6
- down(db: IDatabase): Promise<void>;
7
- }
8
-
9
- export const migration: Migration = {
10
- async up(db: IDatabase): Promise<void> {
11
- const schema = MigrationSchema.create(db);
12
-
13
- await schema.create('tasks', (table: Blueprint) => {
14
- table.id();
15
- table.string('title');
16
- table.text('description').nullable();
17
- table.string('status').default('pending');
18
- table.integer('user_id');
19
- table.foreign('user_id').references('id').on('users').onDelete('CASCADE');
20
- table.timestamps();
21
- });
22
- },
23
-
24
- async down(db: IDatabase): Promise<void> {
25
- const schema = MigrationSchema.create(db);
26
- await schema.dropIfExists('tasks');
27
- },
28
- };