@spfn/core 0.3.0-beta.3 → 0.3.0-beta.31
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/README.md +304 -12
- package/dist/app-config/index.d.ts +39 -1
- package/dist/app-config/index.js.map +1 -1
- package/dist/authz/index.js +47 -391
- package/dist/authz/index.js.map +1 -1
- package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
- package/dist/codegen/index.d.ts +157 -16
- package/dist/codegen/index.js +772 -166
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.js +4 -4
- package/dist/config/index.js.map +1 -1
- package/dist/contract/index.d.ts +39 -16
- package/dist/contract/index.js +147 -23
- package/dist/contract/index.js.map +1 -1
- package/dist/db/index.d.ts +230 -35
- package/dist/db/index.js +316 -160
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.d.ts +6 -442
- package/dist/env/index.js +13 -16
- package/dist/env/index.js.map +1 -1
- package/dist/env/loader.js +24 -1
- package/dist/env/loader.js.map +1 -1
- package/dist/error-CIZlYJlL.d.ts +157 -0
- package/dist/errors/index.d.ts +18 -1
- package/dist/errors/index.js +47 -391
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +5 -5
- package/dist/event/index.js +202 -3
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +18 -4
- package/dist/event/sse/client.js +94 -18
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +18 -5
- package/dist/event/sse/index.js +452 -114
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +3 -2
- package/dist/event/ws/index.d.ts +4 -3
- package/dist/job/index.d.ts +48 -3
- package/dist/job/index.js +131 -52
- package/dist/job/index.js.map +1 -1
- package/dist/logger/index.js +0 -12
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.js +52 -397
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +75 -14
- package/dist/nextjs/index.js +200 -84
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +46 -8
- package/dist/nextjs/server.js +15 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/ops/index.d.ts +61 -42
- package/dist/ops/index.js +648 -97
- package/dist/ops/index.js.map +1 -1
- package/dist/registry-C2KQCh0m.d.ts +473 -0
- package/dist/route/index.d.ts +5 -2
- package/dist/route/index.js +107 -28
- package/dist/route/index.js.map +1 -1
- package/dist/route/types.d.ts +7 -1
- package/dist/{router-Cy7rAfmj.d.ts → route-builder-DRW-MSCJ.d.ts} +51 -126
- package/dist/router-C57y-vz1.d.ts +184 -0
- package/dist/router-CX5uVjmU.d.ts +406 -0
- package/dist/server/index.d.ts +130 -7
- package/dist/server/index.js +1174 -164
- package/dist/server/index.js.map +1 -1
- package/dist/state/client.d.ts +206 -0
- package/dist/state/client.js +678 -0
- package/dist/state/client.js.map +1 -0
- package/dist/state/index.d.ts +443 -0
- package/dist/state/index.js +644 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/react.d.ts +49 -0
- package/dist/state/react.js +50 -0
- package/dist/state/react.js.map +1 -0
- package/dist/surface/index.d.ts +115 -0
- package/dist/surface/index.js +1013 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
- package/dist/{types-CfYVhIQ9.d.ts → types-5Rl11_Nb.d.ts} +43 -4
- package/dist/types-CKH0D3Dy.d.ts +441 -0
- package/dist/{types-ZQODsBft.d.ts → types-CurMab2Q.d.ts} +52 -2
- package/dist/{types-2AbaW4Ie.d.ts → types-DncqXfWN.d.ts} +4 -3
- package/docs/file-upload.md +195 -333
- package/package.json +41 -11
- package/src/cache/README.md +330 -0
- package/src/codegen/README.md +721 -0
- package/src/config/README.md +333 -0
- package/src/contract/README.md +442 -0
- package/src/db/README.md +589 -0
- package/src/db/manager/README.md +500 -0
- package/src/db/schema/README.md +354 -0
- package/src/db/transaction/README.md +845 -0
- package/src/env/README.md +787 -0
- package/src/errors/README.md +445 -0
- package/src/event/README.md +994 -0
- package/src/job/README.md +613 -0
- package/src/logger/README.md +321 -0
- package/src/middleware/README.md +634 -0
- package/src/nextjs/README.md +756 -0
- package/src/route/README.md +763 -0
- package/src/security/README.md +100 -0
- package/src/server/README.md +704 -0
- package/src/state/README.md +806 -0
- package/dist/token-manager-BT5EnUAR.d.ts +0 -278
- package/dist/types-9oszaJqp.d.ts +0 -250
package/dist/job/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import PgBoss from 'pg-boss';
|
|
2
1
|
import { logger } from '@spfn/core/logger';
|
|
2
|
+
import PgBoss from 'pg-boss';
|
|
3
3
|
import { env } from '@spfn/core/config';
|
|
4
4
|
|
|
5
|
-
// src/job/
|
|
5
|
+
// src/job/job-builder.ts
|
|
6
6
|
var jobLogger = logger.child("@spfn/core:job");
|
|
7
7
|
function requiresSSLWithoutVerification(connectionString) {
|
|
8
8
|
try {
|
|
@@ -94,9 +94,42 @@ function shouldSweepOrphanSchedules() {
|
|
|
94
94
|
return getBossConfig()?.sweepOrphanSchedules ?? false;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/job/queue-policy.ts
|
|
98
|
+
function resolveQueuePolicy(job2) {
|
|
99
|
+
if (job2.options?.policy) {
|
|
100
|
+
return job2.options.policy;
|
|
101
|
+
}
|
|
102
|
+
if (job2.runOnce || job2.options?.singletonKey) {
|
|
103
|
+
return "exclusive";
|
|
104
|
+
}
|
|
105
|
+
return "standard";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/job/retry-options.ts
|
|
109
|
+
function toPgBossRetryOptions(options) {
|
|
110
|
+
return {
|
|
111
|
+
retryLimit: options?.retryLimit ?? 3,
|
|
112
|
+
retryDelay: Math.ceil((options?.retryDelay ?? 1e3) / 1e3),
|
|
113
|
+
// Exponential backoff by default — a failed cohort (e.g. a provider 429)
|
|
114
|
+
// would otherwise all retry at the same fixed offset (thundering herd).
|
|
115
|
+
retryBackoff: options?.retryBackoff ?? true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
97
119
|
// src/job/job-builder.ts
|
|
120
|
+
var jobLogger2 = logger.child("@spfn/core:job");
|
|
121
|
+
var warnedIgnoredSingletonKeys = /* @__PURE__ */ new Set();
|
|
122
|
+
function warnIfSingletonKeyIgnored(name, source) {
|
|
123
|
+
if (resolveQueuePolicy(source) !== "standard" || warnedIgnoredSingletonKeys.has(name)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
warnedIgnoredSingletonKeys.add(name);
|
|
127
|
+
jobLogger2.warn(
|
|
128
|
+
`[Job:${name}] singletonKey ignored: queue policy is "standard"; set .options({ policy: 'exclusive' }) (or another non-standard policy) to deduplicate`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
98
131
|
function buildPgBossOptions(defaults, sendOptions) {
|
|
99
|
-
const options = {};
|
|
132
|
+
const options = { ...toPgBossRetryOptions(defaults) };
|
|
100
133
|
if (sendOptions?.startAfter) {
|
|
101
134
|
options.startAfter = sendOptions.startAfter;
|
|
102
135
|
}
|
|
@@ -106,12 +139,6 @@ function buildPgBossOptions(defaults, sendOptions) {
|
|
|
106
139
|
if (sendOptions?.priority !== void 0) {
|
|
107
140
|
options.priority = sendOptions.priority;
|
|
108
141
|
}
|
|
109
|
-
if (defaults?.retryLimit !== void 0) {
|
|
110
|
-
options.retryLimit = defaults.retryLimit;
|
|
111
|
-
}
|
|
112
|
-
if (defaults?.retryDelay !== void 0) {
|
|
113
|
-
options.retryDelay = defaults.retryDelay;
|
|
114
|
-
}
|
|
115
142
|
if (defaults?.expireInSeconds !== void 0) {
|
|
116
143
|
options.expireInSeconds = defaults.expireInSeconds;
|
|
117
144
|
}
|
|
@@ -255,6 +282,9 @@ var JobBuilder = class _JobBuilder {
|
|
|
255
282
|
);
|
|
256
283
|
}
|
|
257
284
|
const [input, sendOptions] = inputSchema ? [inputOrOptions, maybeOptions] : [void 0, inputOrOptions];
|
|
285
|
+
if (sendOptions?.singletonKey) {
|
|
286
|
+
warnIfSingletonKeyIgnored(name, { runOnce, options });
|
|
287
|
+
}
|
|
258
288
|
return await boss.send(
|
|
259
289
|
name,
|
|
260
290
|
input ?? {},
|
|
@@ -276,6 +306,9 @@ var JobBuilder = class _JobBuilder {
|
|
|
276
306
|
);
|
|
277
307
|
}
|
|
278
308
|
const [inputs, sendOptions] = inputSchema ? [inputsOrOptions, maybeOptions] : [void 0, inputsOrOptions];
|
|
309
|
+
if (sendOptions?.singletonKey) {
|
|
310
|
+
warnIfSingletonKeyIgnored(name, { runOnce, options });
|
|
311
|
+
}
|
|
279
312
|
const pgBossOptions = buildPgBossOptions(options, sendOptions);
|
|
280
313
|
const jobs = (inputs ?? [{}]).map((data) => ({
|
|
281
314
|
name,
|
|
@@ -320,29 +353,38 @@ function defineJobRouter(jobs) {
|
|
|
320
353
|
_jobs: jobs
|
|
321
354
|
};
|
|
322
355
|
}
|
|
323
|
-
function
|
|
324
|
-
const jobs
|
|
356
|
+
function mergeJobRouters(base, extra) {
|
|
357
|
+
for (const key of Object.keys(extra.jobs)) {
|
|
358
|
+
if (key in base.jobs) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`jobs(): key "${key}" is already registered by an earlier .jobs() call; rename it or nest it under a domain router`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return defineJobRouter({ ...base.jobs, ...extra.jobs });
|
|
365
|
+
}
|
|
366
|
+
function collectJobEntries(router, prefix = "") {
|
|
367
|
+
const entries = [];
|
|
325
368
|
for (const [key, value] of Object.entries(router.jobs)) {
|
|
326
|
-
const
|
|
369
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
327
370
|
if (isJobRouter(value)) {
|
|
328
|
-
|
|
371
|
+
entries.push(...collectJobEntries(value, path));
|
|
329
372
|
} else if (isJobDef(value)) {
|
|
330
|
-
|
|
373
|
+
entries.push({ key: path, job: value });
|
|
331
374
|
}
|
|
332
375
|
}
|
|
333
|
-
return
|
|
376
|
+
return entries;
|
|
334
377
|
}
|
|
335
|
-
|
|
378
|
+
function collectJobs(router, prefix = "") {
|
|
379
|
+
return collectJobEntries(router, prefix).map((entry) => entry.job);
|
|
380
|
+
}
|
|
381
|
+
var jobLogger3 = logger.child("@spfn/core:job");
|
|
336
382
|
function getEventQueueName(eventName) {
|
|
337
383
|
return `event:${eventName}`;
|
|
338
384
|
}
|
|
339
385
|
function getDefaultJobOptions(options) {
|
|
340
386
|
return {
|
|
341
|
-
|
|
342
|
-
retryDelay: options?.retryDelay ?? 1e3,
|
|
343
|
-
// Exponential backoff by default — a failed cohort (e.g. a provider 429)
|
|
344
|
-
// would otherwise all retry at the same fixed offset (thundering herd).
|
|
345
|
-
retryBackoff: options?.retryBackoff ?? true,
|
|
387
|
+
...toPgBossRetryOptions(options),
|
|
346
388
|
expireInSeconds: options?.expireInSeconds ?? 300
|
|
347
389
|
};
|
|
348
390
|
}
|
|
@@ -353,11 +395,12 @@ async function registerJobs(router) {
|
|
|
353
395
|
"pg-boss not initialized. Call initBoss() before registerJobs()"
|
|
354
396
|
);
|
|
355
397
|
}
|
|
398
|
+
assertUniqueJobNames(router);
|
|
356
399
|
const jobs = collectJobs(router);
|
|
357
400
|
const clearOnStart = shouldClearOnStart();
|
|
358
|
-
|
|
401
|
+
jobLogger3.info(`Registering ${jobs.length} job(s)...`);
|
|
359
402
|
if (clearOnStart) {
|
|
360
|
-
|
|
403
|
+
jobLogger3.info("Clearing existing jobs before registration...");
|
|
361
404
|
for (const job2 of jobs) {
|
|
362
405
|
await boss.deleteAllJobs(job2.name);
|
|
363
406
|
if (job2.subscribedEvent) {
|
|
@@ -365,7 +408,7 @@ async function registerJobs(router) {
|
|
|
365
408
|
await boss.deleteAllJobs(eventQueue);
|
|
366
409
|
}
|
|
367
410
|
}
|
|
368
|
-
|
|
411
|
+
jobLogger3.info("Existing jobs cleared");
|
|
369
412
|
}
|
|
370
413
|
await Promise.all(jobs.map((job2) => registerJob(job2)));
|
|
371
414
|
for (const job2 of jobs) {
|
|
@@ -376,42 +419,77 @@ async function registerJobs(router) {
|
|
|
376
419
|
if (shouldSweepOrphanSchedules()) {
|
|
377
420
|
await sweepOrphanSchedules(boss);
|
|
378
421
|
}
|
|
379
|
-
|
|
422
|
+
jobLogger3.info("All jobs registered successfully");
|
|
423
|
+
}
|
|
424
|
+
function assertUniqueJobNames(router) {
|
|
425
|
+
const keysByName = /* @__PURE__ */ new Map();
|
|
426
|
+
for (const { key, job: job2 } of collectJobEntries(router)) {
|
|
427
|
+
const existingKey = keysByName.get(job2.name);
|
|
428
|
+
if (existingKey) {
|
|
429
|
+
throw new Error(
|
|
430
|
+
`registerJobs(): job name "${job2.name}" is defined twice (keys "${existingKey}" and "${key}"); job names are pg-boss queue names and must be unique`
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
keysByName.set(job2.name, key);
|
|
434
|
+
}
|
|
380
435
|
}
|
|
381
436
|
var registeredCronNames = /* @__PURE__ */ new Set();
|
|
382
437
|
async function sweepOrphanSchedules(boss) {
|
|
383
438
|
if (registeredCronNames.size === 0) {
|
|
384
|
-
|
|
439
|
+
jobLogger3.debug("No cron jobs declared; skipping orphan schedule sweep");
|
|
385
440
|
return;
|
|
386
441
|
}
|
|
387
442
|
try {
|
|
388
443
|
const schedules = await boss.getSchedules();
|
|
389
444
|
const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));
|
|
390
445
|
if (orphans.length === 0) {
|
|
391
|
-
|
|
446
|
+
jobLogger3.debug("No orphan schedules found");
|
|
392
447
|
return;
|
|
393
448
|
}
|
|
394
449
|
await Promise.all(orphans.map(async (orphan) => {
|
|
395
450
|
try {
|
|
396
451
|
await boss.unschedule(orphan.name, orphan.key);
|
|
397
|
-
|
|
452
|
+
jobLogger3.info(`Removed orphan schedule: ${orphan.name}`);
|
|
398
453
|
} catch (error) {
|
|
399
|
-
|
|
454
|
+
jobLogger3.error(`Failed to remove orphan schedule: ${orphan.name}`, {
|
|
400
455
|
error: error instanceof Error ? error.message : String(error)
|
|
401
456
|
});
|
|
402
457
|
}
|
|
403
458
|
}));
|
|
404
459
|
} catch (error) {
|
|
405
|
-
|
|
460
|
+
jobLogger3.error("Orphan schedule sweep failed", {
|
|
406
461
|
error: error instanceof Error ? error.message : String(error)
|
|
407
462
|
});
|
|
408
463
|
}
|
|
409
464
|
}
|
|
410
|
-
async function ensureQueue(boss, queueName) {
|
|
411
|
-
await boss.createQueue(queueName);
|
|
465
|
+
async function ensureQueue(boss, queueName, policy) {
|
|
466
|
+
await boss.createQueue(queueName, { policy });
|
|
467
|
+
const existing = await boss.getQueue(queueName);
|
|
468
|
+
if (!existing || existing.policy === policy) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
if (shouldClearOnStart()) {
|
|
472
|
+
await boss.deleteQueue(queueName);
|
|
473
|
+
await boss.createQueue(queueName, { policy });
|
|
474
|
+
jobLogger3.info(`[Queue:${queueName}] policy ${existing.policy} \u2192 ${policy} (recreated: clearOnStart)`);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
jobLogger3.error(
|
|
478
|
+
`[Queue:${queueName}] policy mismatch: the queue exists as "${existing.policy}" but its jobs require "${policy}". Delete the queue in a quiet window (getBoss().deleteQueue('${queueName}') drops its job rows) or set clearOnStart in development; until then singletonKey/runOnce on this queue do not deduplicate`
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
function createQueueEnsurer(boss, policy) {
|
|
482
|
+
const ensured = /* @__PURE__ */ new Set();
|
|
483
|
+
return async (queueName) => {
|
|
484
|
+
if (ensured.has(queueName)) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
ensured.add(queueName);
|
|
488
|
+
await ensureQueue(boss, queueName, policy);
|
|
489
|
+
};
|
|
412
490
|
}
|
|
413
491
|
async function executeJobHandler(job2, pgBossJob) {
|
|
414
|
-
|
|
492
|
+
jobLogger3.debug(`[Job:${job2.name}] Executing...`, { jobId: pgBossJob.id });
|
|
415
493
|
const startTime = Date.now();
|
|
416
494
|
try {
|
|
417
495
|
if (job2.inputSchema) {
|
|
@@ -420,13 +498,13 @@ async function executeJobHandler(job2, pgBossJob) {
|
|
|
420
498
|
await job2.handler();
|
|
421
499
|
}
|
|
422
500
|
const duration = Date.now() - startTime;
|
|
423
|
-
|
|
501
|
+
jobLogger3.info(`[Job:${job2.name}] Completed in ${duration}ms`, {
|
|
424
502
|
jobId: pgBossJob.id,
|
|
425
503
|
duration
|
|
426
504
|
});
|
|
427
505
|
} catch (error) {
|
|
428
506
|
const duration = Date.now() - startTime;
|
|
429
|
-
|
|
507
|
+
jobLogger3.error(`[Job:${job2.name}] Failed after ${duration}ms`, {
|
|
430
508
|
jobId: pgBossJob.id,
|
|
431
509
|
duration,
|
|
432
510
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -434,8 +512,8 @@ async function executeJobHandler(job2, pgBossJob) {
|
|
|
434
512
|
throw error;
|
|
435
513
|
}
|
|
436
514
|
}
|
|
437
|
-
async function registerWorker(boss, job2, queueName) {
|
|
438
|
-
await
|
|
515
|
+
async function registerWorker(boss, job2, queueName, ensureQueueOnce) {
|
|
516
|
+
await ensureQueueOnce(queueName);
|
|
439
517
|
const batchSize = job2.options?.batchSize ?? 1;
|
|
440
518
|
const pollingIntervalSeconds = job2.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;
|
|
441
519
|
await boss.work(
|
|
@@ -469,28 +547,28 @@ function connectEventToQueue(boss, job2, queueName) {
|
|
|
469
547
|
eventDef._registerJobQueue(queueName, async (queue, payload) => {
|
|
470
548
|
await boss.send(queue, payload, getDefaultJobOptions(job2.options));
|
|
471
549
|
});
|
|
472
|
-
|
|
550
|
+
jobLogger3.debug(`[Job:${job2.name}] Connected to event: ${job2.subscribedEvent}`);
|
|
473
551
|
}
|
|
474
|
-
async function registerCronSchedule(boss, job2) {
|
|
552
|
+
async function registerCronSchedule(boss, job2, ensureQueueOnce) {
|
|
475
553
|
if (!job2.cronExpression) {
|
|
476
554
|
return;
|
|
477
555
|
}
|
|
478
|
-
|
|
479
|
-
await
|
|
556
|
+
jobLogger3.debug(`[Job:${job2.name}] Scheduling cron: ${job2.cronExpression}`);
|
|
557
|
+
await ensureQueueOnce(job2.name);
|
|
480
558
|
await boss.schedule(
|
|
481
559
|
job2.name,
|
|
482
560
|
job2.cronExpression,
|
|
483
561
|
{},
|
|
484
562
|
getDefaultJobOptions(job2.options)
|
|
485
563
|
);
|
|
486
|
-
|
|
564
|
+
jobLogger3.info(`[Job:${job2.name}] Cron scheduled: ${job2.cronExpression}`);
|
|
487
565
|
}
|
|
488
|
-
async function queueRunOnceJob(boss, job2) {
|
|
566
|
+
async function queueRunOnceJob(boss, job2, ensureQueueOnce) {
|
|
489
567
|
if (!job2.runOnce) {
|
|
490
568
|
return;
|
|
491
569
|
}
|
|
492
|
-
|
|
493
|
-
await
|
|
570
|
+
jobLogger3.debug(`[Job:${job2.name}] Queuing runOnce job`);
|
|
571
|
+
await ensureQueueOnce(job2.name);
|
|
494
572
|
await boss.send(
|
|
495
573
|
job2.name,
|
|
496
574
|
{},
|
|
@@ -499,7 +577,7 @@ async function queueRunOnceJob(boss, job2) {
|
|
|
499
577
|
singletonKey: `runOnce:${job2.name}`
|
|
500
578
|
}
|
|
501
579
|
);
|
|
502
|
-
|
|
580
|
+
jobLogger3.info(`[Job:${job2.name}] runOnce job queued`);
|
|
503
581
|
}
|
|
504
582
|
async function registerJob(job2) {
|
|
505
583
|
const boss = getBoss();
|
|
@@ -507,17 +585,18 @@ async function registerJob(job2) {
|
|
|
507
585
|
throw new Error("pg-boss not initialized");
|
|
508
586
|
}
|
|
509
587
|
const queueName = job2.subscribedEvent ? getEventQueueName(job2.subscribedEvent) : job2.name;
|
|
510
|
-
|
|
588
|
+
jobLogger3.debug(`Registering job: ${job2.name}`, {
|
|
511
589
|
queueName,
|
|
512
590
|
subscribedEvent: job2.subscribedEvent
|
|
513
591
|
});
|
|
514
|
-
|
|
592
|
+
const ensureQueueOnce = createQueueEnsurer(boss, resolveQueuePolicy(job2));
|
|
593
|
+
await registerWorker(boss, job2, queueName, ensureQueueOnce);
|
|
515
594
|
connectEventToQueue(boss, job2, queueName);
|
|
516
|
-
await registerCronSchedule(boss, job2);
|
|
517
|
-
await queueRunOnceJob(boss, job2);
|
|
518
|
-
|
|
595
|
+
await registerCronSchedule(boss, job2, ensureQueueOnce);
|
|
596
|
+
await queueRunOnceJob(boss, job2, ensureQueueOnce);
|
|
597
|
+
jobLogger3.debug(`Job registered: ${job2.name}`);
|
|
519
598
|
}
|
|
520
599
|
|
|
521
|
-
export { collectJobs, defineJobRouter, getBoss, initBoss, isBossRunning, isJobDef, isJobRouter, job, registerJobs, shouldClearOnStart, stopBoss };
|
|
600
|
+
export { collectJobs, defineJobRouter, getBoss, initBoss, isBossRunning, isJobDef, isJobRouter, job, mergeJobRouters, registerJobs, resolveQueuePolicy, shouldClearOnStart, stopBoss };
|
|
522
601
|
//# sourceMappingURL=index.js.map
|
|
523
602
|
//# sourceMappingURL=index.js.map
|
package/dist/job/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/job/boss.ts","../../src/job/job-builder.ts","../../src/job/job-router.ts","../../src/job/register-jobs.ts"],"names":["jobLogger","logger","job"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * pg-boss Wrapper\n *\n * Manages pg-boss instance lifecycle\n */\n\nimport PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Check if connection string uses SSL without certificate verification\n *\n * pg library verifies certificates by default even with sslmode=require.\n * For require/prefer modes (no explicit verification), we disable cert checking\n * to support self-signed certificates.\n */\nfunction requiresSSLWithoutVerification(connectionString: string): boolean\n{\n try\n {\n const url = new URL(connectionString);\n const sslmode = url.searchParams.get('sslmode');\n\n return sslmode === 'require' || sslmode === 'prefer';\n }\n catch\n {\n return false;\n }\n}\n\n/**\n * Remove sslmode parameter from connection string URL\n *\n * pg driver interprets sslmode=require as verify-full, which overrides\n * the ssl option object. Stripping it lets us control SSL via the ssl option only.\n */\nfunction stripSslModeFromUrl(connectionString: string): string\n{\n const url = new URL(connectionString);\n url.searchParams.delete('sslmode');\n\n return url.toString();\n}\n\n/**\n * globalThis keys for cross-module-cache singleton (ESM/CJS share same instance)\n */\nconst BOSS_KEY = Symbol.for('spfn:boss-instance');\nconst CONFIG_KEY = Symbol.for('spfn:boss-config');\n\nconst g = globalThis as any;\n\nfunction getBossInstance(): PgBoss | null\n{\n return g[BOSS_KEY] ?? null;\n}\n\nfunction setBossInstance(instance: PgBoss | null): void\n{\n g[BOSS_KEY] = instance;\n}\n\nfunction getBossConfig(): BossConfig | null\n{\n return g[CONFIG_KEY] ?? null;\n}\n\nfunction setBossConfig(config: BossConfig | null): void\n{\n g[CONFIG_KEY] = config;\n}\n\n/**\n * Options for pg-boss initialization\n *\n * @example\n * ```typescript\n * await initBoss({\n * connectionString: process.env.DATABASE_URL,\n * schema: 'spfn_queue',\n * clearOnStart: process.env.NODE_ENV === 'development',\n * });\n * ```\n */\nexport interface BossOptions\n{\n /**\n * PostgreSQL connection string\n *\n * @example 'postgresql://user:password@localhost:5432/mydb'\n */\n connectionString: string;\n\n /**\n * Schema name for pg-boss tables\n *\n * pg-boss creates its own tables in this schema.\n *\n * @default 'spfn_queue'\n */\n schema?: string;\n\n /**\n * Maintenance interval in seconds\n *\n * pg-boss runs maintenance tasks (cleanup, archiving) at this interval.\n *\n * @default 120\n */\n maintenanceIntervalSeconds?: number;\n\n /**\n * Monitor state changes interval in seconds\n *\n * When set, pg-boss emits state change events at this interval.\n *\n * @default undefined (disabled)\n */\n monitorIntervalSeconds?: number;\n\n /**\n * Clear all pending/scheduled jobs on startup\n *\n * Useful for development mode to start with a clean queue.\n * Should be false in production.\n *\n * @default false\n */\n clearOnStart?: boolean;\n\n /**\n * Unschedule cron schedules that are no longer declared on any registered\n * job router, once at startup after job registration. Queues and job rows\n * are never deleted — only the schedule row is removed.\n *\n * Leave disabled when multiple apps share the same pg-boss schema, when\n * schedules are created directly via getBoss(), or during rolling deploys\n * that mix router versions — the sweep only knows the routers registered\n * in this process and would unschedule everything else.\n *\n * @default false\n */\n sweepOrphanSchedules?: boolean;\n}\n\n/**\n * @deprecated Use BossOptions instead\n */\nexport type BossConfig = BossOptions;\n\n/**\n * Initialize pg-boss with the given configuration\n *\n * Must be called before registerJobs(). Typically handled by defineServerConfig().\n *\n * @param options - pg-boss configuration options\n * @returns The pg-boss instance\n *\n * @example\n * ```typescript\n * const boss = await initBoss({\n * connectionString: process.env.DATABASE_URL!,\n * schema: 'spfn_queue',\n * });\n * ```\n */\nexport async function initBoss(options: BossOptions): Promise<PgBoss>\n{\n const existing = getBossInstance();\n if (existing)\n {\n jobLogger.warn('pg-boss already initialized, returning existing instance');\n\n return existing;\n }\n\n jobLogger.info('Initializing pg-boss...');\n\n setBossConfig(options);\n\n const needsSSL = requiresSSLWithoutVerification(options.connectionString);\n\n const pgBossOptions: PgBoss.ConstructorOptions = {\n // pg 드라이버가 URL의 sslmode=require를 verify-full로 해석해서\n // ssl 옵션을 무시하므로, URL에서 sslmode를 빼고 ssl 객체만 전달\n connectionString: needsSSL\n ? stripSslModeFromUrl(options.connectionString)\n : options.connectionString,\n schema: options.schema ?? 'spfn_queue',\n maintenanceIntervalSeconds: options.maintenanceIntervalSeconds ?? 120,\n };\n\n if (needsSSL)\n {\n pgBossOptions.ssl = { rejectUnauthorized: false };\n }\n\n // Only set monitorIntervalSeconds if explicitly provided (must be >= 1)\n if (options.monitorIntervalSeconds !== undefined && options.monitorIntervalSeconds >= 1)\n {\n pgBossOptions.monitorIntervalSeconds = options.monitorIntervalSeconds;\n }\n\n const boss = new PgBoss(pgBossOptions);\n\n // Event handlers\n boss.on('error', (error) =>\n {\n jobLogger.error('pg-boss error:', error);\n });\n\n await boss.start();\n\n setBossInstance(boss);\n\n jobLogger.info('pg-boss started successfully');\n\n return boss;\n}\n\n/**\n * Get the current pg-boss instance\n */\nexport function getBoss(): PgBoss | null\n{\n return getBossInstance();\n}\n\n/**\n * Stop pg-boss gracefully\n */\nexport async function stopBoss(): Promise<void>\n{\n const boss = getBossInstance();\n if (!boss)\n {\n return;\n }\n\n jobLogger.info('Stopping pg-boss...');\n\n try\n {\n await boss.stop({ graceful: true, timeout: 30000 });\n jobLogger.info('pg-boss stopped gracefully');\n }\n catch (error)\n {\n jobLogger.error('Error stopping pg-boss:', error);\n throw error;\n }\n finally\n {\n setBossInstance(null);\n setBossConfig(null);\n }\n}\n\n/**\n * Check if pg-boss is initialized and running\n */\nexport function isBossRunning(): boolean\n{\n return getBossInstance() !== null;\n}\n\n/**\n * Check if jobs should be cleared on start\n */\nexport function shouldClearOnStart(): boolean\n{\n return getBossConfig()?.clearOnStart ?? false;\n}\n\n/**\n * Check if orphan cron schedules should be swept after registration\n */\nexport function shouldSweepOrphanSchedules(): boolean\n{\n return getBossConfig()?.sweepOrphanSchedules ?? false;\n}\n","/**\n * Job Builder\n *\n * Fluent API for defining jobs, similar to route builder pattern\n */\n\nimport type { Static, TSchema } from '@sinclair/typebox';\nimport type { CompensateHandler, JobDef, JobHandler, JobOptions, JobSendOptions } from './types';\nimport type { EventDef, InferEventPayload } from '@spfn/core/event';\nimport { getBoss } from './boss';\n\n/**\n * Build pg-boss options from job defaults and send options\n */\nfunction buildPgBossOptions(\n defaults?: JobOptions,\n sendOptions?: JobSendOptions,\n): Record<string, unknown>\n{\n const options: Record<string, unknown> = {};\n\n // Send options (per-job invocation)\n if (sendOptions?.startAfter)\n {\n options.startAfter = sendOptions.startAfter;\n }\n if (sendOptions?.singletonKey)\n {\n options.singletonKey = sendOptions.singletonKey;\n }\n if (sendOptions?.priority !== undefined)\n {\n options.priority = sendOptions.priority;\n }\n\n // Default options (from job definition)\n if (defaults?.retryLimit !== undefined)\n {\n options.retryLimit = defaults.retryLimit;\n }\n if (defaults?.retryDelay !== undefined)\n {\n options.retryDelay = defaults.retryDelay;\n }\n if (defaults?.expireInSeconds !== undefined)\n {\n options.expireInSeconds = defaults.expireInSeconds;\n }\n if (defaults?.priority !== undefined && sendOptions?.priority === undefined)\n {\n options.priority = defaults.priority;\n }\n if (defaults?.singletonKey && !sendOptions?.singletonKey)\n {\n options.singletonKey = defaults.singletonKey;\n }\n if (defaults?.retentionSeconds !== undefined)\n {\n options.retentionSeconds = defaults.retentionSeconds;\n }\n\n return options;\n}\n\n/**\n * Job builder class with fluent API\n */\nexport class JobBuilder<TInput = void, TOutput = void>\n{\n private readonly _name: string;\n private _inputSchema?: TSchema;\n private _outputSchema?: TSchema;\n private _cronExpression?: string;\n private _runOnce?: boolean;\n private _subscribedEvent?: string;\n private _subscribedEventDef?: EventDef<any>;\n private _options?: JobOptions;\n private _handler?: JobHandler<TInput, TOutput>;\n private _compensate?: CompensateHandler<TInput, TOutput>;\n\n constructor(name: string)\n {\n this._name = name;\n }\n\n /**\n * Define input schema with TypeBox\n */\n input<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<Static<TSchema>, TOutput>\n {\n const builder = new JobBuilder<Static<TSchema>, TOutput>(this._name);\n builder._inputSchema = schema;\n builder._outputSchema = this._outputSchema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Define output schema with TypeBox (for workflow integration)\n */\n output<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<TInput, Static<TSchema>>\n {\n const builder = new JobBuilder<TInput, Static<TSchema>>(this._name);\n builder._inputSchema = this._inputSchema;\n builder._outputSchema = schema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Subscribe to an event (decoupled triggering)\n *\n * @example\n * ```typescript\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const sendWelcomeEmail = job('send-welcome-email')\n * .on(userCreated)\n * .handler(async (payload) => {\n * // payload is typed as { userId: string }\n * });\n * ```\n */\n on<TEvent extends EventDef<any>>(\n event: TEvent,\n ): JobBuilder<InferEventPayload<TEvent>, TOutput>\n {\n const builder = new JobBuilder<InferEventPayload<TEvent>, TOutput>(this._name);\n builder._inputSchema = event.schema;\n builder._outputSchema = this._outputSchema;\n builder._subscribedEvent = event.name;\n builder._subscribedEventDef = event;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Set cron expression for scheduled execution\n */\n cron(expression: string): this\n {\n this._cronExpression = expression;\n\n return this;\n }\n\n /**\n * Mark job to run once on server start\n */\n runOnce(): this\n {\n this._runOnce = true;\n\n return this;\n }\n\n /**\n * Set job options (retry, expiration, etc.)\n */\n options(options: JobOptions): this\n {\n this._options = options;\n\n return this;\n }\n\n /**\n * Set job timeout in milliseconds\n * (Converts to expireInSeconds for pg-boss)\n */\n timeout(ms: number): this\n {\n this._options = {\n ...this._options,\n expireInSeconds: Math.ceil(ms / 1000),\n };\n\n return this;\n }\n\n /**\n * Define compensate handler for rollback (workflow integration)\n */\n compensate(fn: CompensateHandler<TInput, TOutput>): this\n {\n this._compensate = fn;\n\n return this;\n }\n\n /**\n * Define the job handler and finalize the job definition\n */\n handler(fn: JobHandler<TInput, TOutput>): JobDef<TInput, TOutput>\n {\n this._handler = fn;\n\n const name = this._name;\n const inputSchema = this._inputSchema;\n const outputSchema = this._outputSchema;\n const cronExpression = this._cronExpression;\n const runOnce = this._runOnce;\n const subscribedEvent = this._subscribedEvent;\n const subscribedEventDef = this._subscribedEventDef;\n const options = this._options;\n const handler = this._handler;\n const compensate = this._compensate;\n\n // Create send function\n const send = async (\n inputOrOptions?: TInput | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<string | null> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n // Determine input and options based on whether job has input schema\n const [input, sendOptions] = inputSchema\n ? [inputOrOptions as TInput, maybeOptions]\n : [undefined, inputOrOptions as JobSendOptions | undefined];\n\n return await boss.send(\n name,\n input ?? {},\n buildPgBossOptions(options, sendOptions),\n );\n };\n\n // Create run function (synchronous execution)\n const run = async (input?: TInput): Promise<TOutput> =>\n {\n if (inputSchema)\n {\n return await (handler as (input: TInput) => Promise<TOutput>)(input as TInput);\n }\n else\n {\n return await (handler as () => Promise<TOutput>)();\n }\n };\n\n // Create sendBatch function (bulk insert via pg-boss)\n const sendBatch = async (\n inputsOrOptions?: TInput[] | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<void> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n const [inputs, sendOptions] = inputSchema\n ? [inputsOrOptions as TInput[], maybeOptions]\n : [undefined, inputsOrOptions as JobSendOptions | undefined];\n\n const pgBossOptions = buildPgBossOptions(options, sendOptions);\n\n const jobs = (inputs ?? [{}]).map((data) => ({\n name,\n ...pgBossOptions,\n data: data as object,\n }));\n\n await boss.insert(name, jobs);\n };\n\n return {\n name,\n inputSchema,\n outputSchema,\n cronExpression,\n runOnce,\n subscribedEvent,\n _subscribedEventDef: subscribedEventDef,\n options,\n handler,\n compensate,\n send: send as JobDef<TInput, TOutput>['send'],\n sendBatch: sendBatch as JobDef<TInput, TOutput>['sendBatch'],\n run: run as JobDef<TInput, TOutput>['run'],\n _input: undefined as unknown as TInput,\n _output: undefined as unknown as TOutput,\n };\n }\n}\n\n/**\n * Create a new job definition\n *\n * @example\n * ```typescript\n * // Simple job without input\n * export const cleanupJob = job('cleanup')\n * .handler(async () => {\n * await db.cleanup();\n * });\n *\n * // Job with typed input\n * export const sendEmailJob = job('send-email')\n * .input(Type.Object({\n * to: Type.String(),\n * subject: Type.String(),\n * body: Type.String(),\n * }))\n * .handler(async (input) => {\n * await emailService.send(input.to, input.subject, input.body);\n * });\n *\n * // Cron job\n * export const dailyReportJob = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => {\n * await reportService.generateDaily();\n * });\n *\n * // Run once on server start\n * export const initCacheJob = job('init-cache')\n * .runOnce()\n * .handler(async () => {\n * await cache.warmup();\n * });\n *\n * // With options\n * export const importantJob = job('important-task')\n * .input(Type.Object({ id: Type.String() }))\n * .options({\n * retryLimit: 5,\n * retryDelay: 5000,\n * priority: 10,\n * })\n * .handler(async (input) => {\n * await processImportant(input.id);\n * });\n * ```\n */\nexport function job(name: string): JobBuilder<void>\n{\n return new JobBuilder(name);\n}\n","/**\n * Job Router\n *\n * Groups job definitions for registration with the server\n */\n\nimport type { JobDef, JobRouter, JobRouterEntry } from './types';\n\n/**\n * Type guard to check if value is a JobDef\n */\nexport function isJobDef(value: unknown): value is JobDef<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n 'send' in value &&\n 'run' in value\n );\n}\n\n/**\n * Type guard to check if value is a JobRouter\n */\nexport function isJobRouter(value: unknown): value is JobRouter<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'jobs' in value &&\n '_jobs' in value\n );\n}\n\n/**\n * Define a job router to group jobs together\n *\n * @example\n * ```typescript\n * // Flat structure\n * export const jobRouter = defineJobRouter({\n * sendWelcomeEmail,\n * dailyReport,\n * initCache,\n * });\n *\n * // Nested structure\n * export const jobRouter = defineJobRouter({\n * email: defineJobRouter({\n * sendWelcome: sendWelcomeEmailJob,\n * sendReset: sendResetPasswordJob,\n * }),\n * reports: defineJobRouter({\n * daily: dailyReportJob,\n * weekly: weeklyReportJob,\n * }),\n * });\n *\n * // Mixed\n * export const jobRouter = defineJobRouter({\n * initCache, // flat\n * email: defineJobRouter({ ... }), // nested\n * });\n * ```\n */\nexport function defineJobRouter<\n TJobs extends Record<string, JobRouterEntry>,\n>(jobs: TJobs): JobRouter<TJobs>\n{\n return {\n jobs,\n _jobs: jobs,\n };\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested)\n */\nexport function collectJobs(\n router: JobRouter<any>,\n prefix = '',\n): JobDef<any>[]\n{\n const jobs: JobDef<any>[] = [];\n\n for (const [key, value] of Object.entries(router.jobs))\n {\n const name = prefix ? `${prefix}.${key}` : key;\n\n if (isJobRouter(value))\n {\n // Nested router - recurse\n jobs.push(...collectJobs(value, name));\n }\n else if (isJobDef(value))\n {\n jobs.push(value);\n }\n }\n\n return jobs;\n}\n","/**\n * Job Registration\n *\n * Registers jobs with pg-boss\n */\n\nimport type PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\nimport { env } from '@spfn/core/config';\nimport type { JobDef, JobOptions, JobRouter } from './types';\nimport type { EventDef } from '@spfn/core/event';\nimport { collectJobs } from './job-router';\nimport { getBoss, shouldClearOnStart, shouldSweepOrphanSchedules } from './boss';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Get the pg-boss queue name for an event\n */\nexport function getEventQueueName(eventName: string): string\n{\n return `event:${eventName}`;\n}\n\n/**\n * Build default pg-boss options for a job\n */\nfunction getDefaultJobOptions(options?: JobOptions): PgBoss.SendOptions\n{\n return {\n retryLimit: options?.retryLimit ?? 3,\n retryDelay: options?.retryDelay ?? 1000,\n // Exponential backoff by default — a failed cohort (e.g. a provider 429)\n // would otherwise all retry at the same fixed offset (thundering herd).\n retryBackoff: options?.retryBackoff ?? true,\n expireInSeconds: options?.expireInSeconds ?? 300,\n };\n}\n\n/**\n * Register all jobs from a JobRouter with pg-boss\n *\n * This function:\n * 1. Collects all jobs from the router (including nested routers)\n * 2. Optionally clears existing jobs (if clearOnStart is enabled)\n * 3. Registers each job's worker handler with pg-boss\n * 4. Sets up cron schedules for scheduled jobs\n * 5. Queues runOnce jobs\n * 6. Connects event subscriptions to job queues\n *\n * @param router - JobRouter containing job definitions\n *\n * @example\n * ```typescript\n * // Define jobs\n * const sendEmail = job('send-email')\n * .input(Type.Object({ to: Type.String() }))\n * .handler(async (input) => { ... });\n *\n * const dailyReport = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => { ... });\n *\n * // Create router\n * const jobRouter = defineJobRouter({ sendEmail, dailyReport });\n *\n * // Initialize pg-boss first\n * await initBoss({ connectionString: process.env.DATABASE_URL! });\n *\n * // Register jobs\n * await registerJobs(jobRouter);\n * ```\n */\nexport async function registerJobs(router: JobRouter<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n 'pg-boss not initialized. Call initBoss() before registerJobs()',\n );\n }\n\n const jobs = collectJobs(router);\n const clearOnStart = shouldClearOnStart();\n\n jobLogger.info(`Registering ${jobs.length} job(s)...`);\n\n // Clear existing jobs if requested (useful for development)\n if (clearOnStart)\n {\n jobLogger.info('Clearing existing jobs before registration...');\n for (const job of jobs)\n {\n // Clear job queue\n await boss.deleteAllJobs(job.name);\n\n // Also clear event queue if subscribed\n if (job.subscribedEvent)\n {\n const eventQueue = getEventQueueName(job.subscribedEvent);\n await boss.deleteAllJobs(eventQueue);\n }\n }\n jobLogger.info('Existing jobs cleared');\n }\n\n // Each job registers an independent pg-boss queue/worker, so register them\n // concurrently — sequential awaits made startup scale linearly with job count.\n await Promise.all(jobs.map((job) => registerJob(job)));\n\n for (const job of jobs)\n {\n if (job.cronExpression)\n {\n registeredCronNames.add(job.name);\n }\n }\n\n if (shouldSweepOrphanSchedules())\n {\n await sweepOrphanSchedules(boss);\n }\n\n jobLogger.info('All jobs registered successfully');\n}\n\n// Cron names accumulate across registerJobs() calls so a sweep in one call\n// never treats another router's schedules (registered earlier in the same\n// process) as orphans.\nconst registeredCronNames = new Set<string>();\n\n/**\n * Reset sweep state accumulated by registerJobs()\n *\n * @internal test-only\n */\nexport function resetOrphanSweepState(): void\n{\n registeredCronNames.clear();\n}\n\n/**\n * Unschedule pg-boss schedules that are no longer declared on any router\n * registered in this process (opt-in via `sweepOrphanSchedules`)\n *\n * The router is the only sanctioned way to create cron schedules, so any\n * schedule whose name is not a declared cron job is an orphan: pg-boss keeps\n * creating jobs for it on every cron tick, and without a worker they\n * accumulate forever. Unscheduling stops the pile-up; the queue and its\n * existing job rows are left untouched so nothing is destroyed if the name\n * belongs to another app, an ad-hoc getBoss() schedule, or a newer deploy.\n *\n * Skipped entirely when no cron job has been declared — an empty router says\n * nothing about which schedules are orphans. Runs once after registration;\n * failures are logged per orphan and never block startup.\n */\nasync function sweepOrphanSchedules(boss: PgBoss): Promise<void>\n{\n if (registeredCronNames.size === 0)\n {\n jobLogger.debug('No cron jobs declared; skipping orphan schedule sweep');\n\n return;\n }\n\n try\n {\n const schedules = await boss.getSchedules();\n const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));\n\n if (orphans.length === 0)\n {\n jobLogger.debug('No orphan schedules found');\n\n return;\n }\n\n await Promise.all(orphans.map(async (orphan) =>\n {\n try\n {\n await boss.unschedule(orphan.name, orphan.key);\n jobLogger.info(`Removed orphan schedule: ${orphan.name}`);\n }\n catch (error)\n {\n jobLogger.error(`Failed to remove orphan schedule: ${orphan.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }));\n }\n catch (error)\n {\n jobLogger.error('Orphan schedule sweep failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * Create queue if not exists (required for pg-boss v11+)\n */\nasync function ensureQueue(boss: PgBoss, queueName: string): Promise<void>\n{\n await boss.createQueue(queueName);\n}\n\n/**\n * Execute a single job handler with logging\n */\nasync function executeJobHandler(\n job: JobDef<any>,\n pgBossJob: PgBoss.Job<any>,\n): Promise<void>\n{\n jobLogger.debug(`[Job:${job.name}] Executing...`, { jobId: pgBossJob.id });\n\n const startTime = Date.now();\n\n try\n {\n if (job.inputSchema)\n {\n await (job.handler as (input: unknown) => Promise<void>)(pgBossJob.data);\n }\n else\n {\n await (job.handler as () => Promise<void>)();\n }\n\n const duration = Date.now() - startTime;\n jobLogger.info(`[Job:${job.name}] Completed in ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n });\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n jobLogger.error(`[Job:${job.name}] Failed after ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n\n/**\n * Register worker handler for a job\n *\n * When batchSize > 1, jobs are processed in parallel.\n * Failed jobs are individually marked via boss.fail() so pg-boss can retry them.\n * Successful jobs are auto-completed when the handler callback resolves.\n */\nasync function registerWorker(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): Promise<void>\n{\n // Ensure queue exists before registering worker\n await ensureQueue(boss, queueName);\n\n const batchSize = job.options?.batchSize ?? 1;\n const pollingIntervalSeconds = job.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;\n\n await boss.work(\n queueName,\n { batchSize, pollingIntervalSeconds },\n async (pgBossJobs) =>\n {\n if (batchSize <= 1)\n {\n // Single job — throw on error for pg-boss retry\n await executeJobHandler(job, pgBossJobs[0]);\n\n return;\n }\n\n // Batch — parallel execution with individual failure handling\n const results = await Promise.allSettled(\n pgBossJobs.map((pgBossJob) => executeJobHandler(job, pgBossJob)),\n );\n\n // Collect failed job IDs and mark them individually.\n // boss.fail() sets state = 'failed'; the subsequent auto-complete\n // from work() only affects jobs still in 'active' state, so\n // already-failed jobs are not overwritten.\n const failedIds: string[] = [];\n\n for (let i = 0; i < results.length; i++)\n {\n if (results[i].status === 'rejected')\n {\n failedIds.push(pgBossJobs[i].id);\n }\n }\n\n if (failedIds.length > 0)\n {\n await boss.fail(queueName, failedIds);\n }\n\n // Callback resolves → pg-boss auto-completes remaining 'active' jobs\n },\n );\n}\n\n/**\n * Connect event to pg-boss queue\n */\nfunction connectEventToQueue(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): void\n{\n if (!job._subscribedEventDef)\n {\n return;\n }\n\n const eventDef = job._subscribedEventDef as EventDef<any>;\n eventDef._registerJobQueue(queueName, async (queue, payload) =>\n {\n await boss.send(queue, payload as object, getDefaultJobOptions(job.options));\n });\n\n jobLogger.debug(`[Job:${job.name}] Connected to event: ${job.subscribedEvent}`);\n}\n\n/**\n * Register cron schedule for a job\n */\nasync function registerCronSchedule(boss: PgBoss, job: JobDef<any>): Promise<void>\n{\n if (!job.cronExpression)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Scheduling cron: ${job.cronExpression}`);\n\n // Ensure queue exists for cron jobs (uses job.name as queue)\n await ensureQueue(boss, job.name);\n\n await boss.schedule(\n job.name,\n job.cronExpression,\n {},\n getDefaultJobOptions(job.options),\n );\n\n jobLogger.info(`[Job:${job.name}] Cron scheduled: ${job.cronExpression}`);\n}\n\n/**\n * Queue a runOnce job\n */\nasync function queueRunOnceJob(boss: PgBoss, job: JobDef<any>): Promise<void>\n{\n if (!job.runOnce)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Queuing runOnce job`);\n\n // Ensure queue exists for runOnce jobs (uses job.name as queue)\n await ensureQueue(boss, job.name);\n\n await boss.send(\n job.name,\n {},\n {\n ...getDefaultJobOptions(job.options),\n singletonKey: `runOnce:${job.name}`,\n },\n );\n\n jobLogger.info(`[Job:${job.name}] runOnce job queued`);\n}\n\n/**\n * Register a single job with pg-boss\n */\nasync function registerJob(job: JobDef<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error('pg-boss not initialized');\n }\n\n const queueName = job.subscribedEvent\n ? getEventQueueName(job.subscribedEvent)\n : job.name;\n\n jobLogger.debug(`Registering job: ${job.name}`, {\n queueName,\n subscribedEvent: job.subscribedEvent,\n });\n\n await registerWorker(boss, job, queueName);\n connectEventToQueue(boss, job, queueName);\n await registerCronSchedule(boss, job);\n await queueRunOnceJob(boss, job);\n\n jobLogger.debug(`Job registered: ${job.name}`);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/job/boss.ts","../../src/job/queue-policy.ts","../../src/job/retry-options.ts","../../src/job/job-builder.ts","../../src/job/job-router.ts","../../src/job/register-jobs.ts"],"names":["job","jobLogger","logger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * pg-boss Wrapper\n *\n * Manages pg-boss instance lifecycle\n */\n\nimport PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Check if connection string uses SSL without certificate verification\n *\n * pg library verifies certificates by default even with sslmode=require.\n * For require/prefer modes (no explicit verification), we disable cert checking\n * to support self-signed certificates.\n */\nfunction requiresSSLWithoutVerification(connectionString: string): boolean\n{\n try\n {\n const url = new URL(connectionString);\n const sslmode = url.searchParams.get('sslmode');\n\n return sslmode === 'require' || sslmode === 'prefer';\n }\n catch\n {\n return false;\n }\n}\n\n/**\n * Remove sslmode parameter from connection string URL\n *\n * pg driver interprets sslmode=require as verify-full, which overrides\n * the ssl option object. Stripping it lets us control SSL via the ssl option only.\n */\nfunction stripSslModeFromUrl(connectionString: string): string\n{\n const url = new URL(connectionString);\n url.searchParams.delete('sslmode');\n\n return url.toString();\n}\n\n/**\n * globalThis keys for cross-module-cache singleton (ESM/CJS share same instance)\n */\nconst BOSS_KEY = Symbol.for('spfn:boss-instance');\nconst CONFIG_KEY = Symbol.for('spfn:boss-config');\n\nconst g = globalThis as any;\n\nfunction getBossInstance(): PgBoss | null\n{\n return g[BOSS_KEY] ?? null;\n}\n\nfunction setBossInstance(instance: PgBoss | null): void\n{\n g[BOSS_KEY] = instance;\n}\n\nfunction getBossConfig(): BossConfig | null\n{\n return g[CONFIG_KEY] ?? null;\n}\n\nfunction setBossConfig(config: BossConfig | null): void\n{\n g[CONFIG_KEY] = config;\n}\n\n/**\n * Options for pg-boss initialization\n *\n * @example\n * ```typescript\n * await initBoss({\n * connectionString: process.env.DATABASE_URL,\n * schema: 'spfn_queue',\n * clearOnStart: process.env.NODE_ENV === 'development',\n * });\n * ```\n */\nexport interface BossOptions\n{\n /**\n * PostgreSQL connection string\n *\n * @example 'postgresql://user:password@localhost:5432/mydb'\n */\n connectionString: string;\n\n /**\n * Schema name for pg-boss tables\n *\n * pg-boss creates its own tables in this schema.\n *\n * @default 'spfn_queue'\n */\n schema?: string;\n\n /**\n * Maintenance interval in seconds\n *\n * pg-boss runs maintenance tasks (cleanup, archiving) at this interval.\n *\n * @default 120\n */\n maintenanceIntervalSeconds?: number;\n\n /**\n * Monitor state changes interval in seconds\n *\n * When set, pg-boss emits state change events at this interval.\n *\n * @default undefined (disabled)\n */\n monitorIntervalSeconds?: number;\n\n /**\n * Clear all pending/scheduled jobs on startup\n *\n * Useful for development mode to start with a clean queue.\n * Should be false in production.\n *\n * @default false\n */\n clearOnStart?: boolean;\n\n /**\n * Unschedule cron schedules that are no longer declared on any registered\n * job router, once at startup after job registration. Queues and job rows\n * are never deleted — only the schedule row is removed.\n *\n * Leave disabled when multiple apps share the same pg-boss schema, when\n * schedules are created directly via getBoss(), or during rolling deploys\n * that mix router versions — the sweep only knows the routers registered\n * in this process and would unschedule everything else.\n *\n * @default false\n */\n sweepOrphanSchedules?: boolean;\n}\n\n/**\n * @deprecated Use BossOptions instead\n */\nexport type BossConfig = BossOptions;\n\n/**\n * Initialize pg-boss with the given configuration\n *\n * Must be called before registerJobs(). Typically handled by defineServerConfig().\n *\n * @param options - pg-boss configuration options\n * @returns The pg-boss instance\n *\n * @example\n * ```typescript\n * const boss = await initBoss({\n * connectionString: process.env.DATABASE_URL!,\n * schema: 'spfn_queue',\n * });\n * ```\n */\nexport async function initBoss(options: BossOptions): Promise<PgBoss>\n{\n const existing = getBossInstance();\n if (existing)\n {\n jobLogger.warn('pg-boss already initialized, returning existing instance');\n\n return existing;\n }\n\n jobLogger.info('Initializing pg-boss...');\n\n setBossConfig(options);\n\n const needsSSL = requiresSSLWithoutVerification(options.connectionString);\n\n const pgBossOptions: PgBoss.ConstructorOptions = {\n // pg 드라이버가 URL의 sslmode=require를 verify-full로 해석해서\n // ssl 옵션을 무시하므로, URL에서 sslmode를 빼고 ssl 객체만 전달\n connectionString: needsSSL\n ? stripSslModeFromUrl(options.connectionString)\n : options.connectionString,\n schema: options.schema ?? 'spfn_queue',\n maintenanceIntervalSeconds: options.maintenanceIntervalSeconds ?? 120,\n };\n\n if (needsSSL)\n {\n pgBossOptions.ssl = { rejectUnauthorized: false };\n }\n\n // Only set monitorIntervalSeconds if explicitly provided (must be >= 1)\n if (options.monitorIntervalSeconds !== undefined && options.monitorIntervalSeconds >= 1)\n {\n pgBossOptions.monitorIntervalSeconds = options.monitorIntervalSeconds;\n }\n\n const boss = new PgBoss(pgBossOptions);\n\n // Event handlers\n boss.on('error', (error) =>\n {\n jobLogger.error('pg-boss error:', error);\n });\n\n await boss.start();\n\n setBossInstance(boss);\n\n jobLogger.info('pg-boss started successfully');\n\n return boss;\n}\n\n/**\n * Get the current pg-boss instance\n */\nexport function getBoss(): PgBoss | null\n{\n return getBossInstance();\n}\n\n/**\n * Stop pg-boss gracefully\n */\nexport async function stopBoss(): Promise<void>\n{\n const boss = getBossInstance();\n if (!boss)\n {\n return;\n }\n\n jobLogger.info('Stopping pg-boss...');\n\n try\n {\n await boss.stop({ graceful: true, timeout: 30000 });\n jobLogger.info('pg-boss stopped gracefully');\n }\n catch (error)\n {\n jobLogger.error('Error stopping pg-boss:', error);\n throw error;\n }\n finally\n {\n setBossInstance(null);\n setBossConfig(null);\n }\n}\n\n/**\n * Check if pg-boss is initialized and running\n */\nexport function isBossRunning(): boolean\n{\n return getBossInstance() !== null;\n}\n\n/**\n * Check if jobs should be cleared on start\n */\nexport function shouldClearOnStart(): boolean\n{\n return getBossConfig()?.clearOnStart ?? false;\n}\n\n/**\n * Check if orphan cron schedules should be swept after registration\n */\nexport function shouldSweepOrphanSchedules(): boolean\n{\n return getBossConfig()?.sweepOrphanSchedules ?? false;\n}\n","/**\n * Queue Policy Resolution\n *\n * Lives apart from register-jobs.ts and job-builder.ts so both can read the\n * policy of a job definition without importing each other.\n */\n\nimport type { JobOptions, JobQueuePolicy } from './types';\n\n/**\n * The part of a job definition that decides its queue policy\n *\n * `JobDef` satisfies this; the builder can also pass its own fields before the\n * definition object exists.\n */\nexport interface QueuePolicySource\n{\n readonly runOnce?: boolean;\n readonly options?: JobOptions;\n}\n\n/**\n * Resolve the pg-boss queue policy for a job definition\n *\n * An explicit `.options({ policy })` always wins. Otherwise a job that asks\n * for deduplication — `.runOnce()` or an `options.singletonKey` — gets\n * `exclusive`, the only policy under which a second enqueue is dropped while\n * the first is still pending or running.\n *\n * Everything else stays `standard`: `exclusive` without a `singletonKey`\n * deduplicates on `COALESCE(singleton_key, '')` and would collapse a plain\n * queue to a single pending job.\n */\nexport function resolveQueuePolicy(job: QueuePolicySource): JobQueuePolicy\n{\n if (job.options?.policy)\n {\n return job.options.policy;\n }\n\n if (job.runOnce || job.options?.singletonKey)\n {\n return 'exclusive';\n }\n\n return 'standard';\n}\n","/**\n * Retry options, translated from the job API's units to pg-boss's\n */\n\nimport type { JobOptions } from './types';\n\nexport interface PgBossRetryOptions\n{\n retryLimit: number;\n retryDelay: number;\n retryBackoff: boolean;\n}\n\n/**\n * The retry options every enqueue path hands to pg-boss\n *\n * `JobOptions.retryDelay` is in milliseconds; pg-boss reads `retryDelay` in\n * seconds. Passing it through unchanged turned a documented 5-second retry\n * into 5000 seconds (83 minutes). A delay under one second rounds up to one,\n * so a non-zero delay never becomes an immediate retry.\n */\nexport function toPgBossRetryOptions(options?: JobOptions): PgBossRetryOptions\n{\n return {\n retryLimit: options?.retryLimit ?? 3,\n retryDelay: Math.ceil((options?.retryDelay ?? 1000) / 1000),\n // Exponential backoff by default — a failed cohort (e.g. a provider 429)\n // would otherwise all retry at the same fixed offset (thundering herd).\n retryBackoff: options?.retryBackoff ?? true,\n };\n}\n","/**\n * Job Builder\n *\n * Fluent API for defining jobs, similar to route builder pattern\n */\n\nimport type { Static, TSchema } from '@sinclair/typebox';\nimport type { CompensateHandler, JobDef, JobHandler, JobOptions, JobSendOptions } from './types';\nimport type { EventDef, InferEventPayload } from '@spfn/core/event';\nimport { logger } from '@spfn/core/logger';\nimport { getBoss } from './boss';\nimport { resolveQueuePolicy, type QueuePolicySource } from './queue-policy';\nimport { toPgBossRetryOptions } from './retry-options';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n// Job names whose ignored singletonKey has already been reported. A send-time\n// key is usually inside a loop, so warning per call would flood the log.\nconst warnedIgnoredSingletonKeys = new Set<string>();\n\n/**\n * Reset the singletonKey warn-once state\n *\n * @internal test-only\n */\nexport function resetSingletonKeyWarnings(): void\n{\n warnedIgnoredSingletonKeys.clear();\n}\n\n/**\n * Warn once when a send-time singletonKey lands on a `standard` queue\n *\n * pg-boss stores the key and never conflicts on it there, so the send is\n * accepted and the deduplication the caller asked for silently does not\n * happen. The policy lives on the definition, not on the send, so the fix is\n * on the definition too.\n */\nfunction warnIfSingletonKeyIgnored(name: string, source: QueuePolicySource): void\n{\n if (resolveQueuePolicy(source) !== 'standard' || warnedIgnoredSingletonKeys.has(name))\n {\n return;\n }\n\n warnedIgnoredSingletonKeys.add(name);\n\n jobLogger.warn(\n `[Job:${name}] singletonKey ignored: queue policy is \"standard\"; ` +\n \"set .options({ policy: 'exclusive' }) (or another non-standard policy) to deduplicate\",\n );\n}\n\n/**\n * Build pg-boss options from job defaults and send options\n */\nfunction buildPgBossOptions(\n defaults?: JobOptions,\n sendOptions?: JobSendOptions,\n): Record<string, unknown>\n{\n const options: Record<string, unknown> = { ...toPgBossRetryOptions(defaults) };\n\n // Send options (per-job invocation)\n if (sendOptions?.startAfter)\n {\n options.startAfter = sendOptions.startAfter;\n }\n if (sendOptions?.singletonKey)\n {\n options.singletonKey = sendOptions.singletonKey;\n }\n if (sendOptions?.priority !== undefined)\n {\n options.priority = sendOptions.priority;\n }\n\n // Default options (from job definition)\n if (defaults?.expireInSeconds !== undefined)\n {\n options.expireInSeconds = defaults.expireInSeconds;\n }\n if (defaults?.priority !== undefined && sendOptions?.priority === undefined)\n {\n options.priority = defaults.priority;\n }\n if (defaults?.singletonKey && !sendOptions?.singletonKey)\n {\n options.singletonKey = defaults.singletonKey;\n }\n if (defaults?.retentionSeconds !== undefined)\n {\n options.retentionSeconds = defaults.retentionSeconds;\n }\n\n return options;\n}\n\n/**\n * Job builder class with fluent API\n */\nexport class JobBuilder<TInput = void, TOutput = void>\n{\n private readonly _name: string;\n private _inputSchema?: TSchema;\n private _outputSchema?: TSchema;\n private _cronExpression?: string;\n private _runOnce?: boolean;\n private _subscribedEvent?: string;\n private _subscribedEventDef?: EventDef<any>;\n private _options?: JobOptions;\n private _handler?: JobHandler<TInput, TOutput>;\n private _compensate?: CompensateHandler<TInput, TOutput>;\n\n constructor(name: string)\n {\n this._name = name;\n }\n\n /**\n * Define input schema with TypeBox\n */\n input<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<Static<TSchema>, TOutput>\n {\n const builder = new JobBuilder<Static<TSchema>, TOutput>(this._name);\n builder._inputSchema = schema;\n builder._outputSchema = this._outputSchema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Define output schema with TypeBox (for workflow integration)\n */\n output<TSchema extends import('@sinclair/typebox').TSchema>(\n schema: TSchema,\n ): JobBuilder<TInput, Static<TSchema>>\n {\n const builder = new JobBuilder<TInput, Static<TSchema>>(this._name);\n builder._inputSchema = this._inputSchema;\n builder._outputSchema = schema;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._subscribedEvent = this._subscribedEvent;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Subscribe to an event (decoupled triggering)\n *\n * @example\n * ```typescript\n * const userCreated = defineEvent('user.created', Type.Object({\n * userId: Type.String(),\n * }));\n *\n * const sendWelcomeEmail = job('send-welcome-email')\n * .on(userCreated)\n * .handler(async (payload) => {\n * // payload is typed as { userId: string }\n * });\n * ```\n */\n on<TEvent extends EventDef<any>>(\n event: TEvent,\n ): JobBuilder<InferEventPayload<TEvent>, TOutput>\n {\n const builder = new JobBuilder<InferEventPayload<TEvent>, TOutput>(this._name);\n builder._inputSchema = event.schema;\n builder._outputSchema = this._outputSchema;\n builder._subscribedEvent = event.name;\n builder._subscribedEventDef = event;\n builder._cronExpression = this._cronExpression;\n builder._runOnce = this._runOnce;\n builder._options = this._options;\n\n return builder;\n }\n\n /**\n * Set cron expression for scheduled execution\n */\n cron(expression: string): this\n {\n this._cronExpression = expression;\n\n return this;\n }\n\n /**\n * Mark job to run once on server start\n */\n runOnce(): this\n {\n this._runOnce = true;\n\n return this;\n }\n\n /**\n * Set job options (retry, expiration, etc.)\n */\n options(options: JobOptions): this\n {\n this._options = options;\n\n return this;\n }\n\n /**\n * Set job timeout in milliseconds\n * (Converts to expireInSeconds for pg-boss)\n */\n timeout(ms: number): this\n {\n this._options = {\n ...this._options,\n expireInSeconds: Math.ceil(ms / 1000),\n };\n\n return this;\n }\n\n /**\n * Define compensate handler for rollback (workflow integration)\n */\n compensate(fn: CompensateHandler<TInput, TOutput>): this\n {\n this._compensate = fn;\n\n return this;\n }\n\n /**\n * Define the job handler and finalize the job definition\n */\n handler(fn: JobHandler<TInput, TOutput>): JobDef<TInput, TOutput>\n {\n this._handler = fn;\n\n const name = this._name;\n const inputSchema = this._inputSchema;\n const outputSchema = this._outputSchema;\n const cronExpression = this._cronExpression;\n const runOnce = this._runOnce;\n const subscribedEvent = this._subscribedEvent;\n const subscribedEventDef = this._subscribedEventDef;\n const options = this._options;\n const handler = this._handler;\n const compensate = this._compensate;\n\n // Create send function\n const send = async (\n inputOrOptions?: TInput | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<string | null> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n // Determine input and options based on whether job has input schema\n const [input, sendOptions] = inputSchema\n ? [inputOrOptions as TInput, maybeOptions]\n : [undefined, inputOrOptions as JobSendOptions | undefined];\n\n if (sendOptions?.singletonKey)\n {\n warnIfSingletonKeyIgnored(name, { runOnce, options });\n }\n\n return await boss.send(\n name,\n input ?? {},\n buildPgBossOptions(options, sendOptions),\n );\n };\n\n // Create run function (synchronous execution)\n const run = async (input?: TInput): Promise<TOutput> =>\n {\n if (inputSchema)\n {\n return await (handler as (input: TInput) => Promise<TOutput>)(input as TInput);\n }\n else\n {\n return await (handler as () => Promise<TOutput>)();\n }\n };\n\n // Create sendBatch function (bulk insert via pg-boss)\n const sendBatch = async (\n inputsOrOptions?: TInput[] | JobSendOptions,\n maybeOptions?: JobSendOptions,\n ): Promise<void> =>\n {\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n `[Job:${name}] pg-boss not initialized. ` +\n 'Ensure jobs are registered with defineServerConfig().jobs()',\n );\n }\n\n const [inputs, sendOptions] = inputSchema\n ? [inputsOrOptions as TInput[], maybeOptions]\n : [undefined, inputsOrOptions as JobSendOptions | undefined];\n\n if (sendOptions?.singletonKey)\n {\n warnIfSingletonKeyIgnored(name, { runOnce, options });\n }\n\n const pgBossOptions = buildPgBossOptions(options, sendOptions);\n\n const jobs = (inputs ?? [{}]).map((data) => ({\n name,\n ...pgBossOptions,\n data: data as object,\n }));\n\n await boss.insert(name, jobs);\n };\n\n return {\n name,\n inputSchema,\n outputSchema,\n cronExpression,\n runOnce,\n subscribedEvent,\n _subscribedEventDef: subscribedEventDef,\n options,\n handler,\n compensate,\n send: send as JobDef<TInput, TOutput>['send'],\n sendBatch: sendBatch as JobDef<TInput, TOutput>['sendBatch'],\n run: run as JobDef<TInput, TOutput>['run'],\n _input: undefined as unknown as TInput,\n _output: undefined as unknown as TOutput,\n };\n }\n}\n\n/**\n * Create a new job definition\n *\n * @example\n * ```typescript\n * // Simple job without input\n * export const cleanupJob = job('cleanup')\n * .handler(async () => {\n * await db.cleanup();\n * });\n *\n * // Job with typed input\n * export const sendEmailJob = job('send-email')\n * .input(Type.Object({\n * to: Type.String(),\n * subject: Type.String(),\n * body: Type.String(),\n * }))\n * .handler(async (input) => {\n * await emailService.send(input.to, input.subject, input.body);\n * });\n *\n * // Cron job\n * export const dailyReportJob = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => {\n * await reportService.generateDaily();\n * });\n *\n * // Run once on server start\n * export const initCacheJob = job('init-cache')\n * .runOnce()\n * .handler(async () => {\n * await cache.warmup();\n * });\n *\n * // With options\n * export const importantJob = job('important-task')\n * .input(Type.Object({ id: Type.String() }))\n * .options({\n * retryLimit: 5,\n * retryDelay: 5000,\n * priority: 10,\n * })\n * .handler(async (input) => {\n * await processImportant(input.id);\n * });\n * ```\n */\nexport function job(name: string): JobBuilder<void>\n{\n return new JobBuilder(name);\n}\n","/**\n * Job Router\n *\n * Groups job definitions for registration with the server\n */\n\nimport type { JobDef, JobRouter, JobRouterEntry } from './types';\n\n/**\n * Type guard to check if value is a JobDef\n */\nexport function isJobDef(value: unknown): value is JobDef<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'name' in value &&\n 'handler' in value &&\n 'send' in value &&\n 'run' in value\n );\n}\n\n/**\n * Type guard to check if value is a JobRouter\n */\nexport function isJobRouter(value: unknown): value is JobRouter<any>\n{\n return (\n value !== null &&\n typeof value === 'object' &&\n 'jobs' in value &&\n '_jobs' in value\n );\n}\n\n/**\n * Define a job router to group jobs together\n *\n * @example\n * ```typescript\n * // Flat structure\n * export const jobRouter = defineJobRouter({\n * sendWelcomeEmail,\n * dailyReport,\n * initCache,\n * });\n *\n * // Nested structure\n * export const jobRouter = defineJobRouter({\n * email: defineJobRouter({\n * sendWelcome: sendWelcomeEmailJob,\n * sendReset: sendResetPasswordJob,\n * }),\n * reports: defineJobRouter({\n * daily: dailyReportJob,\n * weekly: weeklyReportJob,\n * }),\n * });\n *\n * // Mixed\n * export const jobRouter = defineJobRouter({\n * initCache, // flat\n * email: defineJobRouter({ ... }), // nested\n * });\n * ```\n */\nexport function defineJobRouter<\n TJobs extends Record<string, JobRouterEntry>,\n>(jobs: TJobs): JobRouter<TJobs>\n{\n return {\n jobs,\n _jobs: jobs,\n };\n}\n\n/**\n * Merge two job routers into a new one\n *\n * `defineServerConfig().jobs()` may be called once per domain router, the way\n * `.routes()` is; each call merges into what is already registered instead of\n * replacing it. Keys are the router's own namespace, so a key present in both\n * routers is a mistake the app cannot recover from silently — one of the two\n * would disappear.\n *\n * Neither input is mutated: a fresh router is returned.\n *\n * @throws if a top-level key exists in both routers\n */\nexport function mergeJobRouters(base: JobRouter<any>, extra: JobRouter<any>): JobRouter<any>\n{\n for (const key of Object.keys(extra.jobs))\n {\n if (key in base.jobs)\n {\n throw new Error(\n `jobs(): key \"${key}\" is already registered by an earlier .jobs() call; ` +\n 'rename it or nest it under a domain router',\n );\n }\n }\n\n return defineJobRouter({ ...base.jobs, ...extra.jobs });\n}\n\n/**\n * A collected job together with the router key path that reached it\n *\n * The key path (`email.sendWelcome`) is what an app author recognises, so it\n * is what error messages about a job name should quote.\n */\nexport interface JobEntry\n{\n readonly key: string;\n readonly job: JobDef<any>;\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested) with their key paths\n */\nexport function collectJobEntries(\n router: JobRouter<any>,\n prefix = '',\n): JobEntry[]\n{\n const entries: JobEntry[] = [];\n\n for (const [key, value] of Object.entries(router.jobs))\n {\n const path = prefix ? `${prefix}.${key}` : key;\n\n if (isJobRouter(value))\n {\n // Nested router - recurse\n entries.push(...collectJobEntries(value, path));\n }\n else if (isJobDef(value))\n {\n entries.push({ key: path, job: value });\n }\n }\n\n return entries;\n}\n\n/**\n * Collect all JobDefs from a JobRouter (including nested)\n */\nexport function collectJobs(\n router: JobRouter<any>,\n prefix = '',\n): JobDef<any>[]\n{\n return collectJobEntries(router, prefix).map((entry) => entry.job);\n}\n","/**\n * Job Registration\n *\n * Registers jobs with pg-boss\n */\n\nimport type PgBoss from 'pg-boss';\nimport { logger } from '@spfn/core/logger';\nimport { env } from '@spfn/core/config';\nimport type { JobDef, JobOptions, JobQueuePolicy, JobRouter } from './types';\nimport type { EventDef } from '@spfn/core/event';\nimport { collectJobEntries, collectJobs } from './job-router';\nimport { resolveQueuePolicy } from './queue-policy';\nimport { toPgBossRetryOptions } from './retry-options';\nimport { getBoss, shouldClearOnStart, shouldSweepOrphanSchedules } from './boss';\n\nconst jobLogger = logger.child('@spfn/core:job');\n\n/**\n * Get the pg-boss queue name for an event\n */\nexport function getEventQueueName(eventName: string): string\n{\n return `event:${eventName}`;\n}\n\n/**\n * Build default pg-boss options for a job\n */\nfunction getDefaultJobOptions(options?: JobOptions): PgBoss.SendOptions\n{\n return {\n ...toPgBossRetryOptions(options),\n expireInSeconds: options?.expireInSeconds ?? 300,\n };\n}\n\n/**\n * Register all jobs from a JobRouter with pg-boss\n *\n * This function:\n * 1. Collects all jobs from the router (including nested routers)\n * 2. Optionally clears existing jobs (if clearOnStart is enabled)\n * 3. Registers each job's worker handler with pg-boss\n * 4. Sets up cron schedules for scheduled jobs\n * 5. Queues runOnce jobs\n * 6. Connects event subscriptions to job queues\n *\n * @param router - JobRouter containing job definitions\n *\n * @example\n * ```typescript\n * // Define jobs\n * const sendEmail = job('send-email')\n * .input(Type.Object({ to: Type.String() }))\n * .handler(async (input) => { ... });\n *\n * const dailyReport = job('daily-report')\n * .cron('0 9 * * *')\n * .handler(async () => { ... });\n *\n * // Create router\n * const jobRouter = defineJobRouter({ sendEmail, dailyReport });\n *\n * // Initialize pg-boss first\n * await initBoss({ connectionString: process.env.DATABASE_URL! });\n *\n * // Register jobs\n * await registerJobs(jobRouter);\n * ```\n */\nexport async function registerJobs(router: JobRouter<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error(\n 'pg-boss not initialized. Call initBoss() before registerJobs()',\n );\n }\n\n assertUniqueJobNames(router);\n\n const jobs = collectJobs(router);\n const clearOnStart = shouldClearOnStart();\n\n jobLogger.info(`Registering ${jobs.length} job(s)...`);\n\n // Clear existing jobs if requested (useful for development)\n if (clearOnStart)\n {\n jobLogger.info('Clearing existing jobs before registration...');\n for (const job of jobs)\n {\n // Clear job queue\n await boss.deleteAllJobs(job.name);\n\n // Also clear event queue if subscribed\n if (job.subscribedEvent)\n {\n const eventQueue = getEventQueueName(job.subscribedEvent);\n await boss.deleteAllJobs(eventQueue);\n }\n }\n jobLogger.info('Existing jobs cleared');\n }\n\n // Each job registers an independent pg-boss queue/worker, so register them\n // concurrently — sequential awaits made startup scale linearly with job count.\n await Promise.all(jobs.map((job) => registerJob(job)));\n\n for (const job of jobs)\n {\n if (job.cronExpression)\n {\n registeredCronNames.add(job.name);\n }\n }\n\n if (shouldSweepOrphanSchedules())\n {\n await sweepOrphanSchedules(boss);\n }\n\n jobLogger.info('All jobs registered successfully');\n}\n\n/**\n * Reject two job definitions that share a `name`\n *\n * A job name *is* its pg-boss queue name, so two jobs with the same name share\n * one queue and one worker: whichever registered last silently handles both.\n * Merged routers make this easy to hit, and it must fail before any queue or\n * worker exists.\n *\n * @throws if two collected jobs share a name\n */\nfunction assertUniqueJobNames(router: JobRouter<any>): void\n{\n const keysByName = new Map<string, string>();\n\n for (const { key, job } of collectJobEntries(router))\n {\n const existingKey = keysByName.get(job.name);\n\n if (existingKey)\n {\n throw new Error(\n `registerJobs(): job name \"${job.name}\" is defined twice ` +\n `(keys \"${existingKey}\" and \"${key}\"); ` +\n 'job names are pg-boss queue names and must be unique',\n );\n }\n\n keysByName.set(job.name, key);\n }\n}\n\n// Cron names accumulate across registerJobs() calls so a sweep in one call\n// never treats another router's schedules (registered earlier in the same\n// process) as orphans.\nconst registeredCronNames = new Set<string>();\n\n/**\n * Reset sweep state accumulated by registerJobs()\n *\n * @internal test-only\n */\nexport function resetOrphanSweepState(): void\n{\n registeredCronNames.clear();\n}\n\n/**\n * Unschedule pg-boss schedules that are no longer declared on any router\n * registered in this process (opt-in via `sweepOrphanSchedules`)\n *\n * The router is the only sanctioned way to create cron schedules, so any\n * schedule whose name is not a declared cron job is an orphan: pg-boss keeps\n * creating jobs for it on every cron tick, and without a worker they\n * accumulate forever. Unscheduling stops the pile-up; the queue and its\n * existing job rows are left untouched so nothing is destroyed if the name\n * belongs to another app, an ad-hoc getBoss() schedule, or a newer deploy.\n *\n * Skipped entirely when no cron job has been declared — an empty router says\n * nothing about which schedules are orphans. Runs once after registration;\n * failures are logged per orphan and never block startup.\n */\nasync function sweepOrphanSchedules(boss: PgBoss): Promise<void>\n{\n if (registeredCronNames.size === 0)\n {\n jobLogger.debug('No cron jobs declared; skipping orphan schedule sweep');\n\n return;\n }\n\n try\n {\n const schedules = await boss.getSchedules();\n const orphans = schedules.filter((schedule) => !registeredCronNames.has(schedule.name));\n\n if (orphans.length === 0)\n {\n jobLogger.debug('No orphan schedules found');\n\n return;\n }\n\n await Promise.all(orphans.map(async (orphan) =>\n {\n try\n {\n await boss.unschedule(orphan.name, orphan.key);\n jobLogger.info(`Removed orphan schedule: ${orphan.name}`);\n }\n catch (error)\n {\n jobLogger.error(`Failed to remove orphan schedule: ${orphan.name}`, {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }));\n }\n catch (error)\n {\n jobLogger.error('Orphan schedule sweep failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\n/**\n * Create the queue with the wanted policy, or report that it already differs\n *\n * pg-boss `create_queue` is `INSERT … ON CONFLICT DO NOTHING`, so a queue\n * created by an earlier version keeps its old policy and `createQueue` is a\n * silent no-op. `updateQueue` cannot change a policy either. Comparing against\n * `getQueue` is the only way an already-deployed app ever gets the fix.\n *\n * Recreating the queue drops its job rows, so that only happens under\n * `clearOnStart` (development). Otherwise the mismatch is logged and boot\n * continues — a queue that does not deduplicate is a degraded queue, not a\n * broken one.\n */\nasync function ensureQueue(boss: PgBoss, queueName: string, policy: JobQueuePolicy): Promise<void>\n{\n await boss.createQueue(queueName, { policy });\n\n // null = queue missing (nothing to compare); pg-boss defaults it to 'standard'\n const existing = await boss.getQueue(queueName);\n if (!existing || existing.policy === policy)\n {\n return;\n }\n\n if (shouldClearOnStart())\n {\n await boss.deleteQueue(queueName);\n await boss.createQueue(queueName, { policy });\n jobLogger.info(`[Queue:${queueName}] policy ${existing.policy} → ${policy} (recreated: clearOnStart)`);\n\n return;\n }\n\n jobLogger.error(\n `[Queue:${queueName}] policy mismatch: the queue exists as \"${existing.policy}\" but its jobs ` +\n `require \"${policy}\". Delete the queue in a quiet window ` +\n `(getBoss().deleteQueue('${queueName}') drops its job rows) or set clearOnStart in development; ` +\n 'until then singletonKey/runOnce on this queue do not deduplicate',\n );\n}\n\n/**\n * Build the once-per-queue ensure used by a single registerJob() call\n *\n * registerJob touches its queue up to three times (worker, cron, runOnce) and\n * all three want the same policy, so the queue is ensured on first use only —\n * otherwise the same mismatch would be created and logged three times over.\n */\nfunction createQueueEnsurer(boss: PgBoss, policy: JobQueuePolicy): (queueName: string) => Promise<void>\n{\n const ensured = new Set<string>();\n\n return async (queueName: string) =>\n {\n if (ensured.has(queueName))\n {\n return;\n }\n\n ensured.add(queueName);\n\n await ensureQueue(boss, queueName, policy);\n };\n}\n\n/**\n * Execute a single job handler with logging\n */\nasync function executeJobHandler(\n job: JobDef<any>,\n pgBossJob: PgBoss.Job<any>,\n): Promise<void>\n{\n jobLogger.debug(`[Job:${job.name}] Executing...`, { jobId: pgBossJob.id });\n\n const startTime = Date.now();\n\n try\n {\n if (job.inputSchema)\n {\n await (job.handler as (input: unknown) => Promise<void>)(pgBossJob.data);\n }\n else\n {\n await (job.handler as () => Promise<void>)();\n }\n\n const duration = Date.now() - startTime;\n jobLogger.info(`[Job:${job.name}] Completed in ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n });\n }\n catch (error)\n {\n const duration = Date.now() - startTime;\n jobLogger.error(`[Job:${job.name}] Failed after ${duration}ms`, {\n jobId: pgBossJob.id,\n duration,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n}\n\n/**\n * Register worker handler for a job\n *\n * When batchSize > 1, jobs are processed in parallel.\n * Failed jobs are individually marked via boss.fail() so pg-boss can retry them.\n * Successful jobs are auto-completed when the handler callback resolves.\n */\nasync function registerWorker(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n // Ensure queue exists before registering worker\n await ensureQueueOnce(queueName);\n\n const batchSize = job.options?.batchSize ?? 1;\n const pollingIntervalSeconds = job.options?.pollingIntervalSeconds ?? env.JOB_POLLING_INTERVAL_SECONDS;\n\n await boss.work(\n queueName,\n { batchSize, pollingIntervalSeconds },\n async (pgBossJobs) =>\n {\n if (batchSize <= 1)\n {\n // Single job — throw on error for pg-boss retry\n await executeJobHandler(job, pgBossJobs[0]);\n\n return;\n }\n\n // Batch — parallel execution with individual failure handling\n const results = await Promise.allSettled(\n pgBossJobs.map((pgBossJob) => executeJobHandler(job, pgBossJob)),\n );\n\n // Collect failed job IDs and mark them individually.\n // boss.fail() sets state = 'failed'; the subsequent auto-complete\n // from work() only affects jobs still in 'active' state, so\n // already-failed jobs are not overwritten.\n const failedIds: string[] = [];\n\n for (let i = 0; i < results.length; i++)\n {\n if (results[i].status === 'rejected')\n {\n failedIds.push(pgBossJobs[i].id);\n }\n }\n\n if (failedIds.length > 0)\n {\n await boss.fail(queueName, failedIds);\n }\n\n // Callback resolves → pg-boss auto-completes remaining 'active' jobs\n },\n );\n}\n\n/**\n * Connect event to pg-boss queue\n */\nfunction connectEventToQueue(\n boss: PgBoss,\n job: JobDef<any>,\n queueName: string,\n): void\n{\n if (!job._subscribedEventDef)\n {\n return;\n }\n\n const eventDef = job._subscribedEventDef as EventDef<any>;\n eventDef._registerJobQueue(queueName, async (queue, payload) =>\n {\n await boss.send(queue, payload as object, getDefaultJobOptions(job.options));\n });\n\n jobLogger.debug(`[Job:${job.name}] Connected to event: ${job.subscribedEvent}`);\n}\n\n/**\n * Register cron schedule for a job\n */\nasync function registerCronSchedule(\n boss: PgBoss,\n job: JobDef<any>,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n if (!job.cronExpression)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Scheduling cron: ${job.cronExpression}`);\n\n // Ensure queue exists for cron jobs (uses job.name as queue)\n await ensureQueueOnce(job.name);\n\n await boss.schedule(\n job.name,\n job.cronExpression,\n {},\n getDefaultJobOptions(job.options),\n );\n\n jobLogger.info(`[Job:${job.name}] Cron scheduled: ${job.cronExpression}`);\n}\n\n/**\n * Queue a runOnce job\n */\nasync function queueRunOnceJob(\n boss: PgBoss,\n job: JobDef<any>,\n ensureQueueOnce: (queueName: string) => Promise<void>,\n): Promise<void>\n{\n if (!job.runOnce)\n {\n return;\n }\n\n jobLogger.debug(`[Job:${job.name}] Queuing runOnce job`);\n\n // Ensure queue exists for runOnce jobs (uses job.name as queue)\n await ensureQueueOnce(job.name);\n\n await boss.send(\n job.name,\n {},\n {\n ...getDefaultJobOptions(job.options),\n singletonKey: `runOnce:${job.name}`,\n },\n );\n\n jobLogger.info(`[Job:${job.name}] runOnce job queued`);\n}\n\n/**\n * Register a single job with pg-boss\n */\nasync function registerJob(job: JobDef<any>): Promise<void>\n{\n const boss = getBoss();\n if (!boss)\n {\n throw new Error('pg-boss not initialized');\n }\n\n const queueName = job.subscribedEvent\n ? getEventQueueName(job.subscribedEvent)\n : job.name;\n\n jobLogger.debug(`Registering job: ${job.name}`, {\n queueName,\n subscribedEvent: job.subscribedEvent,\n });\n\n const ensureQueueOnce = createQueueEnsurer(boss, resolveQueuePolicy(job));\n\n await registerWorker(boss, job, queueName, ensureQueueOnce);\n connectEventToQueue(boss, job, queueName);\n await registerCronSchedule(boss, job, ensureQueueOnce);\n await queueRunOnceJob(boss, job, ensureQueueOnce);\n\n jobLogger.debug(`Job registered: ${job.name}`);\n}\n"]}
|
package/dist/logger/index.js
CHANGED
|
@@ -341,17 +341,6 @@ function getConsoleConfig() {
|
|
|
341
341
|
// Dev: colored output, Production: plain text
|
|
342
342
|
};
|
|
343
343
|
}
|
|
344
|
-
function validateEnvironment() {
|
|
345
|
-
const nodeEnv = process.env.NODE_ENV;
|
|
346
|
-
if (!nodeEnv) {
|
|
347
|
-
process.stderr.write(
|
|
348
|
-
"[Logger] Warning: NODE_ENV is not set. Defaulting to test environment.\n"
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
function validateConfig() {
|
|
353
|
-
validateEnvironment();
|
|
354
|
-
}
|
|
355
344
|
|
|
356
345
|
// src/logger/factory.ts
|
|
357
346
|
function initializeTransports() {
|
|
@@ -372,7 +361,6 @@ function getLogLevel() {
|
|
|
372
361
|
return "info";
|
|
373
362
|
}
|
|
374
363
|
function initializeLogger() {
|
|
375
|
-
validateConfig();
|
|
376
364
|
return new Logger({
|
|
377
365
|
level: getLogLevel(),
|
|
378
366
|
transports: initializeTransports()
|