@zintrust/core 0.4.26 → 0.4.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.26",
3
+ "version": "0.4.27",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@cloudflare/containers": "^0.1.1",
53
- "@zintrust/workers": "^0.4.0",
53
+ "@zintrust/workers": "^0.4.25",
54
54
  "bcryptjs": "^3.0.3",
55
55
  "bullmq": "^5.71.0",
56
56
  "chalk": "^5.6.2",
@@ -10,6 +10,7 @@ import { Env } from '../config/env.js';
10
10
  import { Logger } from '../config/logger.js';
11
11
  import { ErrorFactory } from '../exceptions/ZintrustError.js';
12
12
  import { ProjectRuntime } from '../runtime/ProjectRuntime.js';
13
+ import { WorkerProjectAutoImports } from '../runtime/WorkerProjectAutoImports.js';
13
14
  import { loadWorkersModule } from '../runtime/WorkersModule.js';
14
15
  let appInstance;
15
16
  let serverInstance;
@@ -165,6 +166,10 @@ async function useWorkerStarter() {
165
166
  // Initialize worker management system
166
167
  let workerInit = null;
167
168
  try {
169
+ const projectWorkerEntrypoint = await WorkerProjectAutoImports.tryImportProjectWorkerEntrypoint();
170
+ if (!projectWorkerEntrypoint.ok && projectWorkerEntrypoint.reason === 'import-failed') {
171
+ Logger.warn(`Project worker entrypoint import failed: ${projectWorkerEntrypoint.errorMessage ?? 'Unknown error'}`);
172
+ }
168
173
  const workers = await loadWorkersModule();
169
174
  if (workers?.WorkerInit !== undefined) {
170
175
  workerInit = workers.WorkerInit;
@@ -1 +1 @@
1
- {"version":3,"file":"InitContainerCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/InitContainerCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAyKlE,eAAO,MAAM,oBAAoB;cACrB,YAAY;EAkBtB,CAAC"}
1
+ {"version":3,"file":"InitContainerCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/InitContainerCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAqSlE,eAAO,MAAM,oBAAoB;cACrB,YAAY;EAqBtB,CAAC"}
@@ -7,12 +7,13 @@ const DOCKER_COMPOSE_WORKERS_TEMPLATE = `name: zintrust-workers
7
7
 
8
8
  services:
9
9
  # Workers/Jobs API Service (Port 7772)
10
- # Exposes the Workers API to create/manage jobs
10
+ # Exposes the Workers API to create/manage jobs using the project worker overlay image.
11
11
  workers-api:
12
- image: \${WORKERS_IMAGE:-zintrust/zintrust-workers:latest}
12
+ image: \${WORKERS_IMAGE:-zintrust-workers-local:latest}
13
13
  build:
14
14
  context: .
15
- dockerfile: Dockerfile
15
+ dockerfile: Dockerfile.workers
16
+ target: runtime
16
17
  command: ["node", "--experimental-specifier-resolution=node", "dist/src/boot/bootstrap.js"]
17
18
  environment:
18
19
  # Runtime
@@ -26,7 +27,6 @@ services:
26
27
  - APP_KEY=\${APP_KEY}
27
28
  - ENCRYPTION_CIPHER=\${ENCRYPTION_CIPHER:-aes-256-cbc}
28
29
  - LOG_LEVEL=\${LOG_LEVEL:-info}
29
- - ZINTRUST_PROJECT_ROOT=/app/dist
30
30
 
31
31
  # Workers & Queue
32
32
  - WORKER_ENABLED=\${WORKER_ENABLED:-false}
@@ -96,23 +96,148 @@ services:
96
96
  ports:
97
97
  - '7772:7772'
98
98
 
99
+ # Dedicated background worker runner.
100
+ # Uses the same project overlay image but boots the worker target.
101
+ worker-runner:
102
+ image: \${WORKERS_RUNNER_IMAGE:-zintrust-workers-local:latest}
103
+ build:
104
+ context: .
105
+ dockerfile: Dockerfile.workers
106
+ target: worker
107
+ environment:
108
+ # Runtime
109
+ - NODE_ENV=\${NODE_ENV:-development}
110
+ - HOST=0.0.0.0
111
+
112
+ # Application
113
+ - APP_NAME=\${APP_NAME:-ZinTrust}
114
+ - APP_KEY=\${APP_KEY}
115
+ - ENCRYPTION_CIPHER=\${ENCRYPTION_CIPHER:-aes-256-cbc}
116
+ - LOG_LEVEL=\${LOG_LEVEL:-info}
117
+
118
+ # Workers & Queue
119
+ - DOCKER_WORKER=true
120
+ - WORKER_ENABLED=\${WORKER_ENABLED:-true}
121
+ - WORKER_AUTO_START=\${WORKER_AUTO_START:-true}
122
+ - QUEUE_ENABLED=true
123
+ - QUEUE_MONITOR_ENABLED=\${QUEUE_MONITOR_ENABLED:-false}
124
+ - QUEUE_MONITOR_MIDDLEWARE=\${QUEUE_MONITOR_MIDDLEWARE:-}
125
+ - WORKER_PERSISTENCE_DRIVER=\${WORKER_PERSISTENCE_DRIVER:-redis}
126
+ - WORKER_PERSISTENCE_DB_CONNECTION=\${WORKER_PERSISTENCE_DB_CONNECTION:-mysql}
127
+ - WORKER_PERSISTENCE_REDIS_KEY_PREFIX=\${WORKER_PERSISTENCE_REDIS_KEY_PREFIX}
128
+ - QUEUE_DRIVER=\${QUEUE_DRIVER:-redis}
129
+ - QUEUE_CONNECTION=\${QUEUE_CONNECTION:-redis}
130
+ - CACHE_DRIVER=\${CACHE_DRIVER:-redis}
131
+
132
+ # Redis
133
+ - REDIS_HOST=\${DOCKER_REDIS_HOST:-host.docker.internal}
134
+ - REDIS_PORT=\${REDIS_PORT:-6379}
135
+ - REDIS_PASSWORD=\${REDIS_PASSWORD}
136
+ - REDIS_QUEUE_DB=\${REDIS_QUEUE_DB:-1}
137
+
138
+ # Database
139
+ - DB_CONNECTION=\${DB_CONNECTION:-postgres}
140
+ - DB_HOST=\${DOCKER_DB_HOST:-host.docker.internal}
141
+ - DB_PORT=\${DB_PORT:-3306}
142
+ - DB_DATABASE=\${DB_DATABASE:-zintrust}
143
+ - DB_USERNAME=\${DB_USERNAME:-zintrust}
144
+ - DB_PASSWORD=\${DB_PASSWORD:-}
145
+
146
+ # SMTP Mail
147
+ - MAIL_DRIVER=\${MAIL_DRIVER:-smtp}
148
+ - MAIL_CONNECTION=\${MAIL_CONNECTION:-smtp}
149
+ - MAIL_HOST=\${MAIL_HOST}
150
+ - MAIL_PORT=\${MAIL_PORT:-587}
151
+ - MAIL_SECURE=\${MAIL_SECURE:-false}
152
+ - MAIL_USERNAME=\${MAIL_USERNAME}
153
+ - MAIL_PASSWORD=\${MAIL_PASSWORD}
154
+ - MAIL_FROM_ADDRESS=\${MAIL_FROM_ADDRESS}
155
+ - MAIL_FROM_NAME=\${MAIL_FROM_NAME:-ZinTrust}
156
+
157
+ # PostgreSQL
158
+ - DB_PORT_POSTGRESQL=\${DB_PORT_POSTGRESQL:-5432}
159
+ - DB_DATABASE_POSTGRESQL=\${DB_DATABASE_POSTGRESQL:-zintrust}
160
+ - DB_USERNAME_POSTGRESQL=\${DB_USERNAME_POSTGRESQL:-zintrust}
161
+ - DB_PASSWORD_POSTGRESQL=\${DB_PASSWORD_POSTGRESQL:-}
162
+
163
+ # MySQL
164
+ - DB_PORT_MYSQL=\${DB_PORT_MYSQL:-3306}
165
+ - DB_DATABASE_MYSQL=\${DB_DATABASE_MYSQL:-zintrust}
166
+ - DB_USERNAME_MYSQL=\${DB_USERNAME_MYSQL:-zintrust}
167
+ - DB_PASSWORD_MYSQL=\${DB_PASSWORD_MYSQL:-}
168
+
169
+ # Cloudflare D1
170
+ - D1_DATABASE_ID=\${D1_DATABASE_ID}
171
+ - D1_ACCOUNT_ID=\${D1_ACCOUNT_ID}
172
+ - D1_API_TOKEN=\${D1_API_TOKEN}
173
+ - D1_REMOTE_URL=\${D1_REMOTE_URL}
174
+ - D1_REMOTE_KEY_ID=\${D1_REMOTE_KEY_ID}
175
+ - D1_REMOTE_SECRET=\${D1_REMOTE_SECRET}
176
+
177
+ # Cloudflare KV
178
+ - KV_NAMESPACE_ID=\${KV_NAMESPACE_ID}
179
+ - KV_ACCOUNT_ID=\${KV_ACCOUNT_ID}
180
+ - KV_API_TOKEN=\${KV_API_TOKEN}
181
+ - KV_REMOTE_URL=\${KV_REMOTE_URL}
182
+ - KV_REMOTE_KEY_ID=\${KV_REMOTE_KEY_ID}
183
+ - KV_REMOTE_SECRET=\${KV_REMOTE_SECRET}
184
+
99
185
  `;
100
- const DOCKERFILE_TEMPLATE = String.raw `FROM zintrust/zintrust:latest AS runtime
186
+ const DOCKERFILE_TEMPLATE = String.raw `# Multi-stage worker overlay image.
187
+ #
188
+ # This compiles the local ZinTrust app first, then copies only the compiled worker-related
189
+ # artifacts onto the published zintrust/zintrust runtime image.
190
+
191
+ FROM node:20-alpine AS project-build
192
+
193
+ WORKDIR /project
194
+
195
+ ENV NPM_CONFIG_CACHE=/root/.npm
196
+ ENV NPM_CONFIG_PREFER_OFFLINE=true
197
+
198
+ RUN apk upgrade --no-cache \
199
+ && apk add --no-cache g++ git make python3
200
+
201
+ COPY package.json package-lock.json ./
202
+
203
+ RUN --mount=type=cache,target=/root/.npm,id=zintrust-worker-overlay-npm-cache,sharing=locked \
204
+ npm config set fetch-retries 5 \
205
+ && npm config set fetch-retry-mintimeout 20000 \
206
+ && npm config set fetch-retry-maxtimeout 120000 \
207
+ && npm ci
208
+
209
+ COPY . .
210
+
211
+ # Fresh projects always ship npm run build; do not depend on framework-internal build variants.
212
+ RUN --mount=type=cache,target=/root/.npm,id=zintrust-worker-overlay-npm-cache,sharing=locked npm run build
213
+
214
+ FROM project-build AS worker-overlay
215
+
216
+ RUN set -eu; \
217
+ overlay_root=/overlay; \
218
+ mkdir -p "$overlay_root/dist"; \
219
+ if [ -d /project/dist/app ]; then cp -R /project/dist/app "$overlay_root/dist/app"; fi; \
220
+ mkdir -p "$overlay_root/dist/src"; \
221
+ if [ -f /project/dist/src/zintrust.workers.js ]; then cp /project/dist/src/zintrust.workers.js "$overlay_root/dist/src/zintrust.workers.js"; fi; \
222
+ if [ -f /project/dist/src/zintrust.workers.js.map ]; then cp /project/dist/src/zintrust.workers.js.map "$overlay_root/dist/src/zintrust.workers.js.map"; fi
223
+
224
+ FROM zintrust/zintrust:latest AS runtime
101
225
 
102
226
  WORKDIR /app
103
227
 
104
- ENV NODE_ENV=production
105
- ENV PORT=7772
106
- ENV HOST=0.0.0.0
228
+ COPY --from=worker-overlay --chown=nodejs:nodejs /overlay/dist/ /app/dist/
107
229
 
108
- USER nodejs
230
+ FROM runtime AS worker
109
231
 
110
- HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
111
- CMD node -e "require('node:http').get('http://localhost:7772/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
232
+ ENV DOCKER_WORKER=true
233
+ ENV WORKER_ENABLED=true
234
+ ENV WORKER_AUTO_START=true
235
+ ENV QUEUE_ENABLED=true
236
+ ENV PORT=0
112
237
 
113
- EXPOSE 7772
238
+ HEALTHCHECK NONE
114
239
 
115
- CMD ["node", "dist/src/boot/bootstrap.js"]
240
+ CMD ["node", "dist/bin/zin.js", "worker:start-all"]
116
241
  `;
117
242
  const backupSuffix = () => new Date().toISOString().replaceAll(/[:.]/g, '-');
118
243
  const backupFileIfExists = (filePath) => {
@@ -138,19 +263,18 @@ async function writeDockerComposeFile(cwd) {
138
263
  }
139
264
  }
140
265
  async function writeDockerfile(cwd) {
141
- const dockerfilePath = join(cwd, 'Dockerfile');
266
+ const dockerfilePath = join(cwd, 'Dockerfile.workers');
142
267
  let shouldWrite = true;
143
268
  if (existsSync(dockerfilePath)) {
144
- // Only ask if it's different or just generic confirm? Let's just ask.
145
- shouldWrite = await PromptHelper.confirm('Dockerfile already exists. Overwrite with standard worker configuration?', false);
269
+ shouldWrite = await PromptHelper.confirm('Dockerfile.workers already exists. Overwrite with the ZinTrust worker overlay image?', false);
146
270
  }
147
271
  if (shouldWrite) {
148
272
  backupFileIfExists(dockerfilePath);
149
273
  writeFileSync(dockerfilePath, DOCKERFILE_TEMPLATE);
150
- Logger.info('✅ Created Dockerfile');
274
+ Logger.info('✅ Created Dockerfile.workers');
151
275
  }
152
276
  else {
153
- Logger.info('Skipped Dockerfile');
277
+ Logger.info('Skipped Dockerfile.workers');
154
278
  }
155
279
  }
156
280
  export const InitContainerCommand = Object.freeze({
@@ -165,7 +289,8 @@ export const InitContainerCommand = Object.freeze({
165
289
  await writeDockerComposeFile(cwd);
166
290
  await writeDockerfile(cwd);
167
291
  Logger.info('✅ Container worker scaffolding complete.');
168
- Logger.info('Run with: docker-compose -f docker-compose.workers.yml up');
292
+ Logger.info('Run with: docker compose -f docker-compose.workers.yml up');
293
+ Logger.info('Build worker runner with: docker build -f Dockerfile.workers --target worker .');
169
294
  await Promise.resolve();
170
295
  },
171
296
  });
@@ -215,7 +215,7 @@ const warnIfAdapterMissing = (cmd, conn) => {
215
215
  if (conn.driver === 'mysql' && DatabaseAdapterRegistry.get('mysql') === undefined) {
216
216
  cmd.warn('MySQL adapter is not installed/registered; migrations may not hit a real MySQL DB.');
217
217
  cmd.warn('Install via `zin plugin install adapter:mysql` (or `zin add db:mysql`).');
218
- cmd.debug('[debug] Expected a side-effect import in src/zintrust.plugins.ts like: import "@zintrust/db-mysql/register";');
218
+ cmd.debug('[debug] Expected a side-effect import in src/zintrust.plugins.ts like: import "../../../packages/db-mysql/src/register";');
219
219
  }
220
220
  if (conn.driver === 'postgresql' && DatabaseAdapterRegistry.get('postgresql') === undefined) {
221
221
  cmd.warn('PostgreSQL adapter is not installed/registered; migrations may not hit a real PostgreSQL DB.');
@@ -1 +1 @@
1
- {"version":3,"file":"MigrateWorkerCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/MigrateWorkerCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAiNrE,eAAO,MAAM,oBAAoB;cACrB,YAAY;EAUtB,CAAC"}
1
+ {"version":3,"file":"MigrateWorkerCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/MigrateWorkerCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuNrE,eAAO,MAAM,oBAAoB;cACrB,YAAY;EAUtB,CAAC"}
@@ -15,7 +15,12 @@ const isBuiltInDriver = (driver) => driver === 'sqlite' ||
15
15
  driver === 'sqlserver' ||
16
16
  driver === 'd1' ||
17
17
  driver === 'd1-remote';
18
- const getWorkerPersistenceConnectionName = () => {
18
+ const getWorkerPersistenceConnectionName = (optionValue) => {
19
+ if (typeof optionValue === 'string') {
20
+ const trimmed = optionValue.trim();
21
+ if (trimmed.length > 0)
22
+ return trimmed;
23
+ }
19
24
  if (typeof process === 'undefined')
20
25
  return 'default';
21
26
  const raw = process.env?.['WORKER_PERSISTENCE_DB_CONNECTION'];
@@ -31,6 +36,7 @@ const addOptions = (command) => {
31
36
  .option('--step <number>', 'Number of batches to rollback (use with --rollback)', '1')
32
37
  .option('--force', 'Skip production confirmation (allow unsafe operations in production)')
33
38
  .option('--all', 'Run migrations for all configured database connections')
39
+ .option('--connection <name>', 'Use a specific database connection for worker migrations')
34
40
  .option('--no-interactive', 'Disable interactive prompts (useful for CI/CD)');
35
41
  };
36
42
  const getInteractive = (options) => options['interactive'] !== false;
@@ -137,7 +143,7 @@ const executeMigrateWorker = async (options, cmd) => {
137
143
  }
138
144
  }
139
145
  else {
140
- const selected = getWorkerPersistenceConnectionName();
146
+ const selected = getWorkerPersistenceConnectionName(options['connection']);
141
147
  const hasSelected = selected.length > 0;
142
148
  const connections = databaseConfig.connections;
143
149
  const configured = hasSelected ? connections[selected] : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerCommands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/WorkerCommands.ts"],"names":[],"mappings":"AACA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgiBrD;;GAEG;AACH,eAAO,MAAM,cAAc;mCApYS,YAAY;qCA4CV,YAAY;oCAiDb,YAAY;uCAkFT,YAAY;mCA0FhB,YAAY;sCAgCT,YAAY;sCAgCZ,YAAY;CAmElD,CAAC"}
1
+ {"version":3,"file":"WorkerCommands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/WorkerCommands.ts"],"names":[],"mappings":"AACA;;;GAGG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA0lBrD;;GAEG;AACH,eAAO,MAAM,cAAc;mCAtaS,YAAY;qCA4CV,YAAY;oCAiDb,YAAY;uCAqHT,YAAY;mCAyFhB,YAAY;sCAgCT,YAAY;sCAgCZ,YAAY;CAmElD,CAAC"}
@@ -7,14 +7,26 @@ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
7
7
  import { BaseCommand } from '../BaseCommand.js';
8
8
  import { Env } from '../../config/env.js';
9
9
  import { Logger } from '../../config/logger.js';
10
+ import { WorkerProjectAutoImports } from '../../runtime/WorkerProjectAutoImports.js';
10
11
  import { loadWorkersModule as loadWorkersRuntimeModule } from '../../runtime/WorkersModule.js';
11
12
  // Lazy initialization to prevent temporal dead zone issues
12
13
  let WorkerFactory;
13
14
  let WorkerRegistry;
14
15
  let HealthMonitor;
15
16
  let ResourceMonitor;
17
+ let workerProjectEntrypointAttempted = false;
18
+ const ensureProjectWorkerEntrypointLoaded = async () => {
19
+ if (workerProjectEntrypointAttempted)
20
+ return;
21
+ workerProjectEntrypointAttempted = true;
22
+ const result = await WorkerProjectAutoImports.tryImportProjectWorkerEntrypoint();
23
+ if (!result.ok && result.reason === 'import-failed') {
24
+ Logger.warn(`Project worker entrypoint import failed: ${result.errorMessage ?? 'Unknown error'}`);
25
+ }
26
+ };
16
27
  const loadWorkersModule = async () => {
17
28
  try {
29
+ await ensureProjectWorkerEntrypointLoaded();
18
30
  return (await loadWorkersRuntimeModule());
19
31
  }
20
32
  catch (error) {
@@ -232,6 +244,32 @@ const isRegisteredWorkerRunning = async (workerLike) => {
232
244
  const isPaused = typeof pausedFn === 'function' ? pausedFn() : false;
233
245
  return isRunning && !isPaused;
234
246
  };
247
+ const resolveAutoStartWorkerNames = async (factory, workersModule) => {
248
+ const persistedRecords = await factory.listPersistedRecords();
249
+ const fileRecords = typeof factory.listFileBackedRecords === 'function'
250
+ ? await factory.listFileBackedRecords()
251
+ : [];
252
+ if (typeof workersModule.selectAutoStartNames === 'function') {
253
+ return workersModule.selectAutoStartNames(persistedRecords, fileRecords, Logger.warn);
254
+ }
255
+ const persistedNames = persistedRecords
256
+ .filter((record) => record.activeStatus !== false && record.autoStart === true)
257
+ .map((record) => record.name);
258
+ if (persistedNames.length > 0) {
259
+ return { names: persistedNames, source: 'persisted' };
260
+ }
261
+ const seen = new Set();
262
+ const fileNames = [];
263
+ for (const record of fileRecords) {
264
+ if (record.activeStatus === false || record.autoStart !== true)
265
+ continue;
266
+ if (seen.has(record.name))
267
+ continue;
268
+ seen.add(record.name);
269
+ fileNames.push(record.name);
270
+ }
271
+ return { names: fileNames, source: fileNames.length > 0 ? 'file' : 'none' };
272
+ };
235
273
  /**
236
274
  * Worker Start All Command
237
275
  */
@@ -244,17 +282,19 @@ const createWorkerStartAllCommand = () => {
244
282
  return;
245
283
  }
246
284
  const factory = await getWorkerFactory();
247
- const records = await factory.listPersistedRecords();
248
- const autoStartRecords = records.filter((record) => record.activeStatus !== false && record.autoStart === true);
249
- const workers = autoStartRecords.map((record) => record.name);
285
+ const workersModule = await loadWorkersModule();
286
+ const { names: workers, source } = await resolveAutoStartWorkerNames(factory, workersModule);
250
287
  if (workers.length === 0) {
251
- Logger.info('No auto-start eligible persisted workers found.');
288
+ Logger.info('No auto-start eligible persisted or file-backed workers found.');
252
289
  return;
253
290
  }
254
- const discoveredWorkers = await pollForPersistedWorkers(factory);
255
- const eligibleWorkers = workers.filter((name) => discoveredWorkers.includes(name));
291
+ let eligibleWorkers = workers;
292
+ if (source === 'persisted') {
293
+ const discoveredWorkers = await pollForPersistedWorkers(factory);
294
+ eligibleWorkers = workers.filter((name) => discoveredWorkers.includes(name));
295
+ }
256
296
  if (eligibleWorkers.length === 0) {
257
- Logger.info('No auto-start eligible workers are currently persisted.');
297
+ Logger.info('No auto-start eligible workers are currently available.');
258
298
  return;
259
299
  }
260
300
  const results = await Promise.all(eligibleWorkers.map(async (name) => {
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.26
2
+ * @zintrust/core v0.4.27
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-03-28T10:16:07.129Z
8
+ * Built: 2026-03-28T14:15:33.465Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.41';
24
- export const ZINTRUST_BUILD_DATE = '2026-03-28T10:16:07.099Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-28T14:15:33.427Z'; // 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';
@@ -0,0 +1,14 @@
1
+ type ImportResult = {
2
+ ok: true;
3
+ loadedPath: string;
4
+ } | {
5
+ ok: false;
6
+ loadedPath?: string;
7
+ reason: 'not-found' | 'import-failed';
8
+ errorMessage?: string;
9
+ };
10
+ export declare const WorkerProjectAutoImports: Readonly<{
11
+ tryImportProjectWorkerEntrypoint(): Promise<ImportResult>;
12
+ }>;
13
+ export {};
14
+ //# sourceMappingURL=WorkerProjectAutoImports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkerProjectAutoImports.d.ts","sourceRoot":"","sources":["../../../src/runtime/WorkerProjectAutoImports.ts"],"names":[],"mappings":"AAKA,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;AA6BN,eAAO,MAAM,wBAAwB;wCACO,OAAO,CAAC,YAAY,CAAC;EA0C/D,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { Logger } from '../config/logger.js';
2
+ import { existsSync } from '../node-singletons/fs.js';
3
+ import * as path from '../node-singletons/path.js';
4
+ import { pathToFileURL } from '../node-singletons/url.js';
5
+ const resolveProjectRoot = () => {
6
+ const projectRoot = String(process.env['ZINTRUST_PROJECT_ROOT'] ?? '').trim();
7
+ return projectRoot.length > 0 ? projectRoot : process.cwd();
8
+ };
9
+ const getCandidates = (projectRoot) => {
10
+ return [
11
+ path.join(projectRoot, 'src', 'zintrust.workers.ts'),
12
+ path.join(projectRoot, 'dist', 'src', 'zintrust.workers.js'),
13
+ path.join(projectRoot, 'src', 'zintrust.workers.js'),
14
+ ];
15
+ };
16
+ const invokeWorkerEntrypoint = async (mod) => {
17
+ let registerWorkers = null;
18
+ if (typeof mod['registerWorkers'] === 'function') {
19
+ registerWorkers = mod['registerWorkers'];
20
+ }
21
+ else if (typeof mod['default'] === 'function') {
22
+ registerWorkers = mod['default'];
23
+ }
24
+ if (registerWorkers !== null) {
25
+ await registerWorkers();
26
+ }
27
+ };
28
+ export const WorkerProjectAutoImports = Object.freeze({
29
+ async tryImportProjectWorkerEntrypoint() {
30
+ const candidates = getCandidates(resolveProjectRoot()).filter((candidate) => existsSync(candidate));
31
+ if (candidates.length === 0) {
32
+ return { ok: false, reason: 'not-found' };
33
+ }
34
+ let firstFailure = null;
35
+ for (const candidate of candidates) {
36
+ try {
37
+ // eslint-disable-next-line no-await-in-loop
38
+ const mod = (await import(pathToFileURL(candidate).href));
39
+ // eslint-disable-next-line no-await-in-loop
40
+ await invokeWorkerEntrypoint(mod);
41
+ return { ok: true, loadedPath: candidate };
42
+ }
43
+ catch (error) {
44
+ const errorMessage = error instanceof Error ? error.message : String(error);
45
+ Logger.debug('[workers] Project worker entrypoint import failed', {
46
+ candidate,
47
+ errorMessage,
48
+ });
49
+ firstFailure ??= {
50
+ ok: false,
51
+ loadedPath: candidate,
52
+ reason: 'import-failed',
53
+ errorMessage,
54
+ };
55
+ }
56
+ }
57
+ return (firstFailure ?? {
58
+ ok: false,
59
+ reason: 'import-failed',
60
+ errorMessage: 'All worker entrypoint candidates failed',
61
+ });
62
+ },
63
+ });
@@ -0,0 +1,17 @@
1
+ import { Logger } from '@zintrust/core';
2
+
3
+ export const workerDefinition = Object.freeze({
4
+ name: 'example-worker',
5
+ queueName: 'example-worker',
6
+ version: '1.0.0',
7
+ autoStart: false,
8
+ activeStatus: true,
9
+ concurrency: 1,
10
+ processorSpec: 'app/Workers/ExampleWorker.ts',
11
+ });
12
+
13
+ export async function ZinTrustProcessor(payload: unknown): Promise<void> {
14
+ Logger.info('Example worker processed job', { payload });
15
+ }
16
+
17
+ export default ZinTrustProcessor;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Optional ZinTrust worker bootstrap.
3
+ *
4
+ * Docker worker containers and worker CLI commands auto-load this file when it exists.
5
+ * For simple code-first workers that export `workerDefinition`, you usually do not need to
6
+ * update this file after scaffolding. Keep it for advanced pre-registration or grouped imports.
7
+ */
8
+
9
+ import '@app/Workers/ExampleWorker';
10
+
11
+ export const __zintrustGeneratedWorkerStub = 'zintrust.workers.ts';
@@ -0,0 +1,9 @@
1
+ import '../packages/db-d1/src/register.js';
2
+ import '../packages/db-mysql/src/register.js';
3
+ import '../packages/db-postgres/src/register.js';
4
+ import '../packages/db-sqlite/src/register.js';
5
+ import '../packages/db-sqlserver/src/register.js';
6
+ import '../packages/mail-sendgrid/src/register.js';
7
+ import '../packages/mail-smtp/src/register.js';
8
+ import '../packages/queue-redis/src/register.js';
9
+ //# sourceMappingURL=zintrust.comon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zintrust.comon.d.ts","sourceRoot":"","sources":["../../src/zintrust.comon.ts"],"names":[],"mappings":"AAQA,OAAO,mCAAmC,CAAC;AAC3C,OAAO,sCAAsC,CAAC;AAC9C,OAAO,yCAAyC,CAAC;AACjD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,0CAA0C,CAAC;AAClD,OAAO,2CAA2C,CAAC;AACnD,OAAO,uCAAuC,CAAC;AAC/C,OAAO,yCAAyC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /* eslint-disable no-restricted-imports */
2
+ // /**
3
+ // * ZinTrust comon plugin auto-imports
4
+ // *
5
+ // * This file is managed by `zin plugin install` and contains side-effect
6
+ // * imports that register optional adapters/drivers into core registries.
7
+ // */
8
+ import '../packages/db-d1/src/register.js';
9
+ import '../packages/db-mysql/src/register.js';
10
+ import '../packages/db-postgres/src/register.js';
11
+ import '../packages/db-sqlite/src/register.js';
12
+ import '../packages/db-sqlserver/src/register.js';
13
+ import '../packages/mail-sendgrid/src/register.js';
14
+ import '../packages/mail-smtp/src/register.js';
15
+ import '../packages/queue-redis/src/register.js';
@@ -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 declare const __zintrustGeneratedPluginStub = "zintrust.plugins.ts";
7
10
  declare const _default: {};
@@ -1 +1 @@
1
- {"version":3,"file":"zintrust.plugins.d.ts","sourceRoot":"","sources":["../../src/zintrust.plugins.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,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;AAuBH,eAAO,MAAM,6BAA6B,wBAAwB,CAAC;;AACnE,wBAAkB"}
@@ -1,7 +1,31 @@
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
  */
9
+ // Example: pre-register persisted worker processorSpec mappings.
10
+ // Uncomment and adapt to your project as needed.
11
+ //
12
+ // import { ZinTrustProcessor } from '../app/Workers/AdvancEmailWorker';
13
+ // import { WorkerFactory, type WorkerFactoryConfig } from '@zintrust/workers';
14
+ //
15
+ // type PreRegisteredProcessorSpec = {
16
+ // processorSpec: string;
17
+ // processor: WorkerFactoryConfig['processor'];
18
+ // };
19
+ //
20
+ // export const preRegisteredWorkerProcessorSpecs: ReadonlyArray<PreRegisteredProcessorSpec> = [
21
+ // {
22
+ // processorSpec: 'https://wk.zintrust.com/AdvancEmailWorker.js',
23
+ // processor: ZinTrustProcessor,
24
+ // },
25
+ // ];
26
+ //
27
+ // for (const entry of preRegisteredWorkerProcessorSpecs) {
28
+ // WorkerFactory.registerProcessorSpec(entry.processorSpec, entry.processor);
29
+ // }
6
30
  export const __zintrustGeneratedPluginStub = 'zintrust.plugins.ts';
7
31
  export default {};