@bitclaw/jobs 1.4.0 → 2.0.0
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/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/queue.d.ts +5 -1
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +130 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +21 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +39 -3
- package/dist/workflow.d.ts +36 -0
- package/dist/workflow.d.ts.map +1 -0
- package/dist/workflow.js +219 -0
- package/package.json +5 -1
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export { JobQueue } from './queue';
|
|
|
6
6
|
export { SlidingWindowRateLimiter } from './rate-limiter';
|
|
7
7
|
export { Scheduler } from './scheduler';
|
|
8
8
|
export { applyPragmas, initializeSchema } from './schema';
|
|
9
|
-
export type { AddJobOptions, AddScheduleOptions, BackoffConfig, BatchOptions, FailedJob, Job, JobBatch, JobContext, JobMap, JobStats, JobStatus, ListJobsOptions, PaginatedResult, PurgeOptions, RateLimit, Schedule, WorkerOptions } from './types';
|
|
9
|
+
export type { AddJobOptions, AddScheduleOptions, BackoffConfig, BatchOptions, FailedJob, Job, JobBatch, JobContext, JobGraphNode, JobMap, JobStats, JobStatus, ListJobsOptions, MiddlewareFn, PaginatedResult, PurgeOptions, RateLimit, Schedule, WorkerOptions, WorkflowExecution, WorkflowExecutionStatus, WorkflowReconcileResult, WorkflowRunResult } from './types';
|
|
10
10
|
export { NonRetryableError } from './types';
|
|
11
11
|
export { JobWorker } from './worker';
|
|
12
|
+
export { WorkflowBuilder, WorkflowEngine } from './workflow';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,UAAU,EACV,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,eAAe,EACf,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1D,YAAY,EACV,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,YAAY,EACZ,SAAS,EACT,GAAG,EACH,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,SAAS,EACT,eAAe,EACf,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,aAAa,EACb,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/queue.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
|
2
2
|
import { JobQueueEmitter } from './events';
|
|
3
|
-
import type { AddJobOptions, BatchOptions, FailedJob, Job, JobBatch, JobMap, JobStats, ListJobsOptions, PaginatedResult, PurgeOptions, WorkerOptions } from './types';
|
|
3
|
+
import type { AddJobOptions, BatchOptions, FailedJob, Job, JobBatch, JobGraphNode, JobMap, JobStats, ListJobsOptions, MiddlewareFn, PaginatedResult, PurgeOptions, WorkerOptions } from './types';
|
|
4
4
|
import { JobWorker } from './worker';
|
|
5
5
|
export declare class JobQueue<TMap extends JobMap = Record<string, unknown>> extends JobQueueEmitter {
|
|
6
6
|
readonly db: Database;
|
|
7
|
+
readonly middlewares: MiddlewareFn[];
|
|
7
8
|
private readonly insertJobStmt;
|
|
8
9
|
private readonly selectDedupedJobStmt;
|
|
9
10
|
private readonly insertDepStmt;
|
|
@@ -77,6 +78,9 @@ export declare class JobQueue<TMap extends JobMap = Record<string, unknown>> ext
|
|
|
77
78
|
cancelByUniqueKey(type: string, uniqueKey: string): boolean;
|
|
78
79
|
retryFailedJobsByType(type: string): number;
|
|
79
80
|
purgeExpiredJobs(): number;
|
|
81
|
+
use(fn: MiddlewareFn): void;
|
|
82
|
+
getJobGraph(rootId: number): JobGraphNode[];
|
|
83
|
+
mountAdminHandler(prefix?: string): (req: Request) => Promise<Response>;
|
|
80
84
|
close(): void;
|
|
81
85
|
private unblockDependents;
|
|
82
86
|
private handleBatchJobComplete;
|
package/dist/queue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EACV,aAAa,EAEb,YAAY,EACZ,SAAS,EAET,GAAG,EACH,QAAQ,EAER,MAAM,EAEN,QAAQ,
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
package/dist/queue.js
CHANGED
|
@@ -63,6 +63,7 @@ function toFailedJob(row) {
|
|
|
63
63
|
}
|
|
64
64
|
export class JobQueue extends JobQueueEmitter {
|
|
65
65
|
db;
|
|
66
|
+
middlewares = [];
|
|
66
67
|
insertJobStmt;
|
|
67
68
|
selectDedupedJobStmt;
|
|
68
69
|
insertDepStmt;
|
|
@@ -359,9 +360,19 @@ export class JobQueue extends JobQueueEmitter {
|
|
|
359
360
|
}
|
|
360
361
|
markJobDone(id, result) {
|
|
361
362
|
let doneJob = null;
|
|
363
|
+
// Use container object so TypeScript tracks mutation across the closure
|
|
364
|
+
const wh = { config: null };
|
|
362
365
|
this.db.transaction(() => {
|
|
363
366
|
const now = nowISO();
|
|
364
367
|
const row = this.selectJobStmt.get({ $id: id });
|
|
368
|
+
if (row?.webhook_config) {
|
|
369
|
+
try {
|
|
370
|
+
wh.config = JSON.parse(row.webhook_config);
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// ignore malformed config
|
|
374
|
+
}
|
|
375
|
+
}
|
|
365
376
|
this.markDoneStmt.run({
|
|
366
377
|
$id: id,
|
|
367
378
|
$now: now,
|
|
@@ -371,13 +382,21 @@ export class JobQueue extends JobQueueEmitter {
|
|
|
371
382
|
if (row?.batch_id) {
|
|
372
383
|
this.handleBatchJobComplete(row.batch_id);
|
|
373
384
|
}
|
|
374
|
-
// capture for post-tx emit
|
|
375
385
|
const updatedRow = this.selectJobStmt.get({ $id: id });
|
|
376
386
|
if (updatedRow)
|
|
377
387
|
doneJob = toJob(updatedRow);
|
|
378
388
|
})();
|
|
379
389
|
if (doneJob)
|
|
380
390
|
this.emit('job:done', doneJob);
|
|
391
|
+
if (wh.config && doneJob) {
|
|
392
|
+
const cfg = wh.config;
|
|
393
|
+
const payload = doneJob;
|
|
394
|
+
void fetch(cfg.url, {
|
|
395
|
+
method: cfg.method ?? 'POST',
|
|
396
|
+
headers: { 'Content-Type': 'application/json', ...(cfg.headers ?? {}) },
|
|
397
|
+
body: JSON.stringify({ job: payload, result })
|
|
398
|
+
}).catch(() => { });
|
|
399
|
+
}
|
|
381
400
|
}
|
|
382
401
|
markJobDead(id, error) {
|
|
383
402
|
let deadJob = null;
|
|
@@ -671,6 +690,116 @@ export class JobQueue extends JobQueueEmitter {
|
|
|
671
690
|
.run({ $now: nowISO() });
|
|
672
691
|
return result.changes;
|
|
673
692
|
}
|
|
693
|
+
use(fn) {
|
|
694
|
+
this.middlewares.push(fn);
|
|
695
|
+
}
|
|
696
|
+
getJobGraph(rootId) {
|
|
697
|
+
const relatedRows = this.db
|
|
698
|
+
.query(`WITH RECURSIVE
|
|
699
|
+
ancestors(id) AS (
|
|
700
|
+
SELECT depends_on_id FROM job_dependencies WHERE job_id = $root
|
|
701
|
+
UNION
|
|
702
|
+
SELECT jd.depends_on_id FROM job_dependencies jd JOIN ancestors a ON jd.job_id = a.id
|
|
703
|
+
),
|
|
704
|
+
descendants(id) AS (
|
|
705
|
+
SELECT job_id FROM job_dependencies WHERE depends_on_id = $root
|
|
706
|
+
UNION
|
|
707
|
+
SELECT jd.job_id FROM job_dependencies jd JOIN descendants d ON jd.depends_on_id = d.id
|
|
708
|
+
)
|
|
709
|
+
SELECT * FROM jobs
|
|
710
|
+
WHERE id IN (SELECT id FROM ancestors UNION SELECT $root UNION SELECT id FROM descendants)`)
|
|
711
|
+
.all({ $root: rootId });
|
|
712
|
+
if (relatedRows.length === 0)
|
|
713
|
+
return [];
|
|
714
|
+
const ids = relatedRows.map(r => r.id);
|
|
715
|
+
const namedParams = {};
|
|
716
|
+
for (let i = 0; i < ids.length; i++)
|
|
717
|
+
namedParams[`$id${i}`] = ids[i];
|
|
718
|
+
const ph = ids.map((_, i) => `$id${i}`).join(',');
|
|
719
|
+
const edges = this.db
|
|
720
|
+
.query(`SELECT job_id, depends_on_id FROM job_dependencies
|
|
721
|
+
WHERE job_id IN (${ph}) OR depends_on_id IN (${ph})`)
|
|
722
|
+
.all(namedParams);
|
|
723
|
+
return relatedRows.map(row => ({
|
|
724
|
+
id: row.id,
|
|
725
|
+
type: row.type,
|
|
726
|
+
status: row.status,
|
|
727
|
+
result: row.result ? JSON.parse(row.result) : null,
|
|
728
|
+
dependsOn: edges
|
|
729
|
+
.filter(e => e.job_id === row.id)
|
|
730
|
+
.map(e => e.depends_on_id),
|
|
731
|
+
dependents: edges
|
|
732
|
+
.filter(e => e.depends_on_id === row.id)
|
|
733
|
+
.map(e => e.job_id)
|
|
734
|
+
}));
|
|
735
|
+
}
|
|
736
|
+
mountAdminHandler(prefix = '') {
|
|
737
|
+
const base = prefix.replace(/\/$/, '');
|
|
738
|
+
return async (req) => {
|
|
739
|
+
const url = new URL(req.url);
|
|
740
|
+
const path = url.pathname.slice(base.length).replace(/^\//, '');
|
|
741
|
+
const parts = path.split('/').filter(Boolean);
|
|
742
|
+
const method = req.method.toUpperCase();
|
|
743
|
+
try {
|
|
744
|
+
if (method === 'GET' && parts[0] === 'stats' && parts.length === 1) {
|
|
745
|
+
return Response.json(this.getStats());
|
|
746
|
+
}
|
|
747
|
+
if (method === 'GET' && parts[0] === 'jobs' && parts[1] === 'types') {
|
|
748
|
+
return Response.json(this.getJobTypes());
|
|
749
|
+
}
|
|
750
|
+
if (method === 'GET' && parts[0] === 'jobs' && parts.length === 1) {
|
|
751
|
+
const status = url.searchParams.get('status');
|
|
752
|
+
const type = url.searchParams.get('type') ?? undefined;
|
|
753
|
+
const limit = Number(url.searchParams.get('limit') ?? 50);
|
|
754
|
+
const offset = Number(url.searchParams.get('offset') ?? 0);
|
|
755
|
+
return Response.json(this.listJobs({ status: status ?? undefined, type, limit, offset }));
|
|
756
|
+
}
|
|
757
|
+
if (method === 'GET' && parts[0] === 'jobs' && parts.length === 2) {
|
|
758
|
+
const id = Number(parts[1]);
|
|
759
|
+
const job = this.getJob(id);
|
|
760
|
+
if (!job)
|
|
761
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
762
|
+
return Response.json(job);
|
|
763
|
+
}
|
|
764
|
+
if (method === 'GET' && parts[0] === 'jobs' && parts[2] === 'graph') {
|
|
765
|
+
return Response.json(this.getJobGraph(Number(parts[1])));
|
|
766
|
+
}
|
|
767
|
+
if (method === 'POST' && parts[0] === 'jobs' && parts[2] === 'cancel') {
|
|
768
|
+
return Response.json({ ok: this.cancelJob(Number(parts[1])) });
|
|
769
|
+
}
|
|
770
|
+
if (method === 'POST' &&
|
|
771
|
+
parts[0] === 'jobs' &&
|
|
772
|
+
parts[2] === 'force-retry') {
|
|
773
|
+
return Response.json({ ok: this.forceRetryJob(Number(parts[1])) });
|
|
774
|
+
}
|
|
775
|
+
if (method === 'GET' && parts[0] === 'failed' && parts.length === 1) {
|
|
776
|
+
const type = url.searchParams.get('type') ?? undefined;
|
|
777
|
+
const limit = Number(url.searchParams.get('limit') ?? 50);
|
|
778
|
+
const offset = Number(url.searchParams.get('offset') ?? 0);
|
|
779
|
+
return Response.json(this.getFailedJobs({ type, limit, offset }));
|
|
780
|
+
}
|
|
781
|
+
if (method === 'POST' &&
|
|
782
|
+
parts[0] === 'failed' &&
|
|
783
|
+
parts[1] === 'retry-by-type') {
|
|
784
|
+
const body = (await req.json());
|
|
785
|
+
if (!body.type)
|
|
786
|
+
return Response.json({ error: 'type required' }, { status: 400 });
|
|
787
|
+
return Response.json({
|
|
788
|
+
count: this.retryFailedJobsByType(body.type)
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
if (method === 'POST' &&
|
|
792
|
+
parts[0] === 'failed' &&
|
|
793
|
+
parts[2] === 'retry') {
|
|
794
|
+
return Response.json({ id: this.retryFailedJob(Number(parts[1])) });
|
|
795
|
+
}
|
|
796
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
797
|
+
}
|
|
798
|
+
catch (err) {
|
|
799
|
+
return Response.json({ error: err instanceof Error ? err.message : 'Internal error' }, { status: 500 });
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
}
|
|
674
803
|
close() {
|
|
675
804
|
try {
|
|
676
805
|
if (this.db.filename !== ':memory:' && this.db.filename !== '') {
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA6H3C,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAY/C;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI,CAoCnD"}
|
package/dist/schema.js
CHANGED
|
@@ -90,6 +90,25 @@ CREATE TABLE IF NOT EXISTS schedules (
|
|
|
90
90
|
const SCHEDULES_NEXT_RUN_INDEX = `
|
|
91
91
|
CREATE INDEX IF NOT EXISTS idx_schedules_next_run
|
|
92
92
|
ON schedules (enabled, next_run_at)`;
|
|
93
|
+
const WORKFLOW_EXECUTIONS_TABLE = `
|
|
94
|
+
CREATE TABLE IF NOT EXISTS workflow_executions (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
name TEXT NOT NULL,
|
|
97
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
98
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
99
|
+
completed_at TEXT
|
|
100
|
+
)`;
|
|
101
|
+
const WORKFLOW_STEPS_TABLE = `
|
|
102
|
+
CREATE TABLE IF NOT EXISTS workflow_steps (
|
|
103
|
+
execution_id TEXT NOT NULL REFERENCES workflow_executions(id),
|
|
104
|
+
step_name TEXT NOT NULL,
|
|
105
|
+
job_id INTEGER NOT NULL,
|
|
106
|
+
compensate_type TEXT,
|
|
107
|
+
compensate_data TEXT,
|
|
108
|
+
compensate_job_id INTEGER,
|
|
109
|
+
step_order INTEGER NOT NULL,
|
|
110
|
+
PRIMARY KEY (execution_id, step_name)
|
|
111
|
+
)`;
|
|
93
112
|
export function applyPragmas(db) {
|
|
94
113
|
if (db.filename !== ':memory:' && db.filename !== '') {
|
|
95
114
|
db.run('PRAGMA journal_mode = WAL');
|
|
@@ -112,6 +131,8 @@ export function initializeSchema(db) {
|
|
|
112
131
|
db.run(FAILED_JOBS_TABLE);
|
|
113
132
|
db.run(SCHEDULES_TABLE);
|
|
114
133
|
db.run(SCHEDULES_NEXT_RUN_INDEX);
|
|
134
|
+
db.run(WORKFLOW_EXECUTIONS_TABLE);
|
|
135
|
+
db.run(WORKFLOW_STEPS_TABLE);
|
|
115
136
|
// Migrations for columns added after initial schema creation
|
|
116
137
|
const cols = db.prepare('PRAGMA table_info(jobs)').all();
|
|
117
138
|
if (!cols.some(c => c.name === 'unique_key')) {
|
package/dist/types.d.ts
CHANGED
|
@@ -83,6 +83,15 @@ export type RateLimit = {
|
|
|
83
83
|
count: number;
|
|
84
84
|
windowMs: number;
|
|
85
85
|
};
|
|
86
|
+
export type MiddlewareFn<T = unknown> = (job: Job<T>, next: () => Promise<unknown>) => Promise<unknown>;
|
|
87
|
+
export type JobGraphNode = {
|
|
88
|
+
readonly id: number;
|
|
89
|
+
readonly type: string;
|
|
90
|
+
readonly status: JobStatus;
|
|
91
|
+
readonly result: unknown | null;
|
|
92
|
+
readonly dependsOn: number[];
|
|
93
|
+
readonly dependents: number[];
|
|
94
|
+
};
|
|
86
95
|
export type WorkerOptions<T = unknown> = {
|
|
87
96
|
type: string;
|
|
88
97
|
handler: (job: Job<T>, ctx: JobContext) => Promise<unknown>;
|
|
@@ -232,4 +241,21 @@ export type AddScheduleOptions = {
|
|
|
232
241
|
overlap?: boolean;
|
|
233
242
|
maxRetries?: number;
|
|
234
243
|
};
|
|
244
|
+
export type WorkflowExecutionStatus = 'running' | 'completed' | 'compensating' | 'failed';
|
|
245
|
+
export type WorkflowExecution = {
|
|
246
|
+
readonly id: string;
|
|
247
|
+
readonly name: string;
|
|
248
|
+
readonly status: WorkflowExecutionStatus;
|
|
249
|
+
readonly createdAt: string;
|
|
250
|
+
readonly completedAt: string | null;
|
|
251
|
+
};
|
|
252
|
+
export type WorkflowRunResult<TStepNames extends string> = {
|
|
253
|
+
instanceId: string;
|
|
254
|
+
jobIds: Record<TStepNames, number>;
|
|
255
|
+
};
|
|
256
|
+
export type WorkflowReconcileResult = {
|
|
257
|
+
completed: number;
|
|
258
|
+
compensated: number;
|
|
259
|
+
failed: number;
|
|
260
|
+
};
|
|
235
261
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,OAAO,KAAK,EAGV,MAAM,EAEN,aAAa,EACd,MAAM,SAAS,CAAC;AAIjB,qBAAa,SAAS,CACpB,IAAI,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7C,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,IAAI;IAEnD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,OAAO,CAAuC;IACtD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,WAAW,CAA6B;gBAG9C,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,CAAC,CAAA;KAAE;IAY/C,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI;IAOP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB3B,KAAK,IAAI,IAAI;IAIb,MAAM,IAAI,IAAI;IAYd,OAAO,CAAC,YAAY;YAMN,IAAI;YAoDJ,MAAM;CAwErB"}
|
package/dist/worker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SlidingWindowRateLimiter } from './rate-limiter';
|
|
2
2
|
import { NonRetryableError } from './types';
|
|
3
|
+
import { nowISO } from './utils';
|
|
3
4
|
export class JobWorker {
|
|
4
5
|
queue;
|
|
5
6
|
options;
|
|
@@ -85,6 +86,29 @@ export class JobWorker {
|
|
|
85
86
|
this.activeCount++;
|
|
86
87
|
void this.runJob(job);
|
|
87
88
|
}
|
|
89
|
+
// Apply priority aging if configured
|
|
90
|
+
if (this.options.aging) {
|
|
91
|
+
const { boostPerMinute, maxBoost } = this.options.aging;
|
|
92
|
+
const pollIntervalMs = this.options.pollIntervalMs ?? 1000;
|
|
93
|
+
const boostPerTick = (boostPerMinute * pollIntervalMs) / 60_000;
|
|
94
|
+
if (boostPerTick > 0) {
|
|
95
|
+
const cutoff = new Date(Date.now() - pollIntervalMs).toISOString();
|
|
96
|
+
this.queue.db.run(`UPDATE jobs
|
|
97
|
+
SET priority = MIN(priority + ?, ?),
|
|
98
|
+
updated_at = ?
|
|
99
|
+
WHERE status = 'pending'
|
|
100
|
+
AND type = ?
|
|
101
|
+
AND created_at < ?
|
|
102
|
+
AND priority < ?`, [
|
|
103
|
+
boostPerTick,
|
|
104
|
+
maxBoost,
|
|
105
|
+
nowISO(),
|
|
106
|
+
this.options.type,
|
|
107
|
+
cutoff,
|
|
108
|
+
maxBoost
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
88
112
|
this.scheduleNext();
|
|
89
113
|
}
|
|
90
114
|
async runJob(job) {
|
|
@@ -98,17 +122,29 @@ export class JobWorker {
|
|
|
98
122
|
},
|
|
99
123
|
signal: this.abortController.signal
|
|
100
124
|
};
|
|
101
|
-
const
|
|
125
|
+
const handler = this.options.handler;
|
|
126
|
+
const middlewares = this.queue.middlewares;
|
|
127
|
+
const chain = [...middlewares, (j) => handler(j, ctx)];
|
|
128
|
+
const execute = () => {
|
|
129
|
+
let i = 0;
|
|
130
|
+
const run = () => {
|
|
131
|
+
const mw = chain[i++];
|
|
132
|
+
if (!mw)
|
|
133
|
+
return Promise.resolve(undefined);
|
|
134
|
+
return mw(job, run);
|
|
135
|
+
};
|
|
136
|
+
return run();
|
|
137
|
+
};
|
|
102
138
|
let handlerResult;
|
|
103
139
|
if (this.options.timeoutMs) {
|
|
104
140
|
const timeoutMs = this.options.timeoutMs;
|
|
105
141
|
handlerResult = await Promise.race([
|
|
106
|
-
|
|
142
|
+
execute(),
|
|
107
143
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`Job timed out after ${timeoutMs}ms`)), timeoutMs))
|
|
108
144
|
]);
|
|
109
145
|
}
|
|
110
146
|
else {
|
|
111
|
-
handlerResult = await
|
|
147
|
+
handlerResult = await execute();
|
|
112
148
|
}
|
|
113
149
|
this.queue.markJobDone(job.id, handlerResult);
|
|
114
150
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { JobQueue } from './queue';
|
|
2
|
+
import type { AddJobOptions, BackoffConfig, JobMap, WorkflowExecution, WorkflowExecutionStatus, WorkflowReconcileResult, WorkflowRunResult } from './types';
|
|
3
|
+
export type { WorkflowExecution, WorkflowExecutionStatus, WorkflowReconcileResult, WorkflowRunResult };
|
|
4
|
+
type StepStepOptions = Omit<AddJobOptions, 'dependsOn'> & {
|
|
5
|
+
dependsOn?: string[];
|
|
6
|
+
backoff?: BackoffConfig;
|
|
7
|
+
};
|
|
8
|
+
export declare class WorkflowBuilder<TMap extends JobMap, TStepNames extends string = never> {
|
|
9
|
+
private readonly queue;
|
|
10
|
+
private readonly name;
|
|
11
|
+
private readonly specs;
|
|
12
|
+
private readonly compensations;
|
|
13
|
+
constructor(queue: JobQueue<TMap>, name: string);
|
|
14
|
+
step<SName extends string, K extends string & keyof TMap>(stepName: SName, jobType: K, data: TMap[K], options?: StepStepOptions): WorkflowBuilder<TMap, TStepNames | SName>;
|
|
15
|
+
onFail<K extends string & keyof TMap>(stepName: TStepNames, compensation: {
|
|
16
|
+
compensate: K;
|
|
17
|
+
compensateData: TMap[K];
|
|
18
|
+
}): this;
|
|
19
|
+
run(instanceId?: string): WorkflowRunResult<TStepNames>;
|
|
20
|
+
}
|
|
21
|
+
export declare class WorkflowEngine<TMap extends JobMap> {
|
|
22
|
+
private readonly queue;
|
|
23
|
+
constructor(queue: JobQueue<TMap>);
|
|
24
|
+
workflow<TStepNames extends string = never>(name: string): WorkflowBuilder<TMap, TStepNames>;
|
|
25
|
+
getExecution(instanceId: string): WorkflowExecution | null;
|
|
26
|
+
getStepResult<T>(instanceId: string, stepName: string): T | null;
|
|
27
|
+
listExecutions(opts?: {
|
|
28
|
+
status?: WorkflowExecutionStatus;
|
|
29
|
+
name?: string;
|
|
30
|
+
limit?: number;
|
|
31
|
+
offset?: number;
|
|
32
|
+
}): WorkflowExecution[];
|
|
33
|
+
reconcile(): WorkflowReconcileResult;
|
|
34
|
+
private reconcileExecution;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=workflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow.d.ts","sourceRoot":"","sources":["../src/workflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,MAAM,EACN,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,uBAAuB,EACvB,iBAAiB,EAClB,CAAC;AAEF,KAAK,eAAe,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG;IACxD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AA6DF,qBAAa,eAAe,CAC1B,IAAI,SAAS,MAAM,EACnB,UAAU,SAAS,MAAM,GAAG,KAAK;IAS/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IARvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAG1B;gBAGe,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,IAAI,EAAE,MAAM;IAG/B,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,EACtD,QAAQ,EAAE,KAAK,EACf,OAAO,EAAE,CAAC,EACV,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACb,OAAO,CAAC,EAAE,eAAe,GACxB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC;IAY5C,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,EAClC,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE;QAAE,UAAU,EAAE,CAAC,CAAC;QAAC,cAAc,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;KAAE,GACvD,IAAI;IAQP,GAAG,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC;CA4DxD;AAID,qBAAa,cAAc,CAAC,IAAI,SAAS,MAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC;IAElD,QAAQ,CAAC,UAAU,SAAS,MAAM,GAAG,KAAK,EACxC,IAAI,EAAE,MAAM,GACX,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC;IAIpC,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAO1D,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAUhE,cAAc,CACZ,IAAI,GAAE;QACJ,MAAM,CAAC,EAAE,uBAAuB,CAAC;QACjC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ,GACL,iBAAiB,EAAE;IAgCtB,SAAS,IAAI,uBAAuB;IAoBpC,OAAO,CAAC,kBAAkB;CA2F3B"}
|
package/dist/workflow.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { nowISO } from './utils';
|
|
2
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
3
|
+
function topologicalSort(specs) {
|
|
4
|
+
const nameToSpec = new Map(specs.map(s => [s.stepName, s]));
|
|
5
|
+
const visited = new Set();
|
|
6
|
+
const result = [];
|
|
7
|
+
const visit = (name) => {
|
|
8
|
+
if (visited.has(name))
|
|
9
|
+
return;
|
|
10
|
+
visited.add(name);
|
|
11
|
+
const spec = nameToSpec.get(name);
|
|
12
|
+
if (!spec)
|
|
13
|
+
throw new Error(`Unknown step '${name}' in dependsOn`);
|
|
14
|
+
for (const dep of spec.dependsOn)
|
|
15
|
+
visit(dep);
|
|
16
|
+
result.push(spec);
|
|
17
|
+
};
|
|
18
|
+
for (const spec of specs)
|
|
19
|
+
visit(spec.stepName);
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
function toWorkflowExecution(row) {
|
|
23
|
+
return {
|
|
24
|
+
id: row.id,
|
|
25
|
+
name: row.name,
|
|
26
|
+
status: row.status,
|
|
27
|
+
createdAt: row.created_at,
|
|
28
|
+
completedAt: row.completed_at
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
// ─── WorkflowBuilder ────────────────────────────────────────────────────────
|
|
32
|
+
export class WorkflowBuilder {
|
|
33
|
+
queue;
|
|
34
|
+
name;
|
|
35
|
+
specs = [];
|
|
36
|
+
compensations = new Map();
|
|
37
|
+
constructor(queue, name) {
|
|
38
|
+
this.queue = queue;
|
|
39
|
+
this.name = name;
|
|
40
|
+
}
|
|
41
|
+
step(stepName, jobType, data, options) {
|
|
42
|
+
this.specs.push({
|
|
43
|
+
stepName,
|
|
44
|
+
jobType: jobType,
|
|
45
|
+
data,
|
|
46
|
+
dependsOn: (options?.dependsOn ?? []),
|
|
47
|
+
options: options ?? {},
|
|
48
|
+
compensation: null
|
|
49
|
+
});
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
onFail(stepName, compensation) {
|
|
53
|
+
this.compensations.set(stepName, {
|
|
54
|
+
type: compensation.compensate,
|
|
55
|
+
data: compensation.compensateData
|
|
56
|
+
});
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
run(instanceId) {
|
|
60
|
+
const id = instanceId ?? crypto.randomUUID();
|
|
61
|
+
const jobIds = {};
|
|
62
|
+
this.queue.db.transaction(() => {
|
|
63
|
+
this.queue.db.run(`INSERT INTO workflow_executions (id, name, status, created_at)
|
|
64
|
+
VALUES (?, ?, 'running', ?)`, [id, this.name, nowISO()]);
|
|
65
|
+
const sorted = topologicalSort(this.specs);
|
|
66
|
+
for (let order = 0; order < sorted.length; order++) {
|
|
67
|
+
const spec = sorted[order];
|
|
68
|
+
const depJobIds = spec.dependsOn.map(depName => {
|
|
69
|
+
const depId = jobIds[depName];
|
|
70
|
+
if (depId === undefined)
|
|
71
|
+
throw new Error(`Step '${depName}' not enqueued yet — check dependsOn`);
|
|
72
|
+
return depId;
|
|
73
|
+
});
|
|
74
|
+
const addOpts = {
|
|
75
|
+
priority: spec.options.priority,
|
|
76
|
+
maxRetries: spec.options.maxRetries,
|
|
77
|
+
backoff: spec.options.backoff,
|
|
78
|
+
runAt: spec.options.runAt,
|
|
79
|
+
uniqueKey: spec.options.uniqueKey,
|
|
80
|
+
...(depJobIds.length > 0 ? { dependsOn: depJobIds } : {})
|
|
81
|
+
};
|
|
82
|
+
const jobId = this.queue.add(spec.jobType, spec.data, addOpts);
|
|
83
|
+
jobIds[spec.stepName] = jobId;
|
|
84
|
+
const comp = this.compensations.get(spec.stepName) ?? null;
|
|
85
|
+
this.queue.db.run(`INSERT INTO workflow_steps
|
|
86
|
+
(execution_id, step_name, job_id, compensate_type, compensate_data, step_order)
|
|
87
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
88
|
+
id,
|
|
89
|
+
spec.stepName,
|
|
90
|
+
jobId,
|
|
91
|
+
comp?.type ?? null,
|
|
92
|
+
comp ? JSON.stringify(comp.data) : null,
|
|
93
|
+
order
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
return { instanceId: id, jobIds: jobIds };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ─── WorkflowEngine ─────────────────────────────────────────────────────────
|
|
101
|
+
export class WorkflowEngine {
|
|
102
|
+
queue;
|
|
103
|
+
constructor(queue) {
|
|
104
|
+
this.queue = queue;
|
|
105
|
+
}
|
|
106
|
+
workflow(name) {
|
|
107
|
+
return new WorkflowBuilder(this.queue, name);
|
|
108
|
+
}
|
|
109
|
+
getExecution(instanceId) {
|
|
110
|
+
const row = this.queue.db
|
|
111
|
+
.query('SELECT * FROM workflow_executions WHERE id = ?')
|
|
112
|
+
.get(instanceId);
|
|
113
|
+
return row ? toWorkflowExecution(row) : null;
|
|
114
|
+
}
|
|
115
|
+
getStepResult(instanceId, stepName) {
|
|
116
|
+
const step = this.queue.db
|
|
117
|
+
.query('SELECT job_id FROM workflow_steps WHERE execution_id = ? AND step_name = ?')
|
|
118
|
+
.get(instanceId, stepName);
|
|
119
|
+
if (!step)
|
|
120
|
+
return null;
|
|
121
|
+
return this.queue.getJobResult(step.job_id);
|
|
122
|
+
}
|
|
123
|
+
listExecutions(opts = {}) {
|
|
124
|
+
const conditions = [];
|
|
125
|
+
const params = [];
|
|
126
|
+
if (opts.status) {
|
|
127
|
+
conditions.push('status = ?');
|
|
128
|
+
params.push(opts.status);
|
|
129
|
+
}
|
|
130
|
+
if (opts.name) {
|
|
131
|
+
conditions.push('name = ?');
|
|
132
|
+
params.push(opts.name);
|
|
133
|
+
}
|
|
134
|
+
const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
|
|
135
|
+
const limit = opts.limit ?? 50;
|
|
136
|
+
const offset = opts.offset ?? 0;
|
|
137
|
+
const rows = this.queue.db
|
|
138
|
+
.query(`SELECT * FROM workflow_executions ${where}
|
|
139
|
+
ORDER BY created_at DESC LIMIT ? OFFSET ?`)
|
|
140
|
+
.all(...params, limit, offset);
|
|
141
|
+
return rows.map(toWorkflowExecution);
|
|
142
|
+
}
|
|
143
|
+
reconcile() {
|
|
144
|
+
const stats = {
|
|
145
|
+
completed: 0,
|
|
146
|
+
compensated: 0,
|
|
147
|
+
failed: 0
|
|
148
|
+
};
|
|
149
|
+
const running = this.queue.db
|
|
150
|
+
.query("SELECT * FROM workflow_executions WHERE status IN ('running', 'compensating')")
|
|
151
|
+
.all();
|
|
152
|
+
for (const exec of running) {
|
|
153
|
+
this.reconcileExecution(exec, stats);
|
|
154
|
+
}
|
|
155
|
+
return stats;
|
|
156
|
+
}
|
|
157
|
+
reconcileExecution(exec, stats) {
|
|
158
|
+
const steps = this.queue.db
|
|
159
|
+
.query('SELECT * FROM workflow_steps WHERE execution_id = ? ORDER BY step_order ASC')
|
|
160
|
+
.all(exec.id);
|
|
161
|
+
if (exec.status === 'compensating') {
|
|
162
|
+
// If all comp jobs done (or no comp jobs), mark failed
|
|
163
|
+
const pendingComp = steps.filter(s => {
|
|
164
|
+
if (!s.compensate_job_id)
|
|
165
|
+
return false;
|
|
166
|
+
const job = this.queue.getJob(s.compensate_job_id);
|
|
167
|
+
return job !== null && job.status !== 'done';
|
|
168
|
+
});
|
|
169
|
+
if (pendingComp.length === 0) {
|
|
170
|
+
this.queue.db.run("UPDATE workflow_executions SET status = 'failed', completed_at = ? WHERE id = ?", [nowISO(), exec.id]);
|
|
171
|
+
stats.failed++;
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Build step status map
|
|
176
|
+
const stepStatuses = steps.map(step => {
|
|
177
|
+
const job = this.queue.getJob(step.job_id);
|
|
178
|
+
if (!job) {
|
|
179
|
+
// Job deleted from jobs table — check failed_jobs
|
|
180
|
+
const dead = this.queue.db
|
|
181
|
+
.query('SELECT id FROM failed_jobs WHERE original_job_id = ? LIMIT 1')
|
|
182
|
+
.get(step.job_id);
|
|
183
|
+
return { step, status: dead ? 'dead' : 'done' };
|
|
184
|
+
}
|
|
185
|
+
return { step, status: job.status };
|
|
186
|
+
});
|
|
187
|
+
// Any step dead → start compensation
|
|
188
|
+
const deadStep = stepStatuses.find(s => s.status === 'dead');
|
|
189
|
+
if (deadStep) {
|
|
190
|
+
const stepsWithComp = stepStatuses
|
|
191
|
+
.filter(s => s.status === 'done' && s.step.compensate_type)
|
|
192
|
+
.reverse();
|
|
193
|
+
this.queue.db.transaction(() => {
|
|
194
|
+
if (stepsWithComp.length > 0) {
|
|
195
|
+
this.queue.db.run("UPDATE workflow_executions SET status = 'compensating' WHERE id = ?", [exec.id]);
|
|
196
|
+
for (const { step } of stepsWithComp) {
|
|
197
|
+
const compData = step.compensate_data
|
|
198
|
+
? JSON.parse(step.compensate_data)
|
|
199
|
+
: {};
|
|
200
|
+
const compJobId = this.queue.add(step.compensate_type, compData, {});
|
|
201
|
+
this.queue.db.run('UPDATE workflow_steps SET compensate_job_id = ? WHERE execution_id = ? AND step_name = ?', [compJobId, exec.id, step.step_name]);
|
|
202
|
+
}
|
|
203
|
+
stats.compensated++;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// No compensation needed — immediately fail
|
|
207
|
+
this.queue.db.run("UPDATE workflow_executions SET status = 'failed', completed_at = ? WHERE id = ?", [nowISO(), exec.id]);
|
|
208
|
+
stats.failed++;
|
|
209
|
+
}
|
|
210
|
+
})();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// All done → mark completed
|
|
214
|
+
if (stepStatuses.every(s => s.status === 'done')) {
|
|
215
|
+
this.queue.db.run("UPDATE workflow_executions SET status = 'completed', completed_at = ? WHERE id = ?", [nowISO(), exec.id]);
|
|
216
|
+
stats.completed++;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitclaw/jobs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "SQLite-backed background job queue using bun:sqlite",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
"./scheduler": {
|
|
39
39
|
"types": "./dist/scheduler.d.ts",
|
|
40
40
|
"default": "./dist/scheduler.js"
|
|
41
|
+
},
|
|
42
|
+
"./workflow": {
|
|
43
|
+
"types": "./dist/workflow.d.ts",
|
|
44
|
+
"default": "./dist/workflow.js"
|
|
41
45
|
}
|
|
42
46
|
},
|
|
43
47
|
"scripts": {
|