@happyvertical/jobs 0.80.0 → 0.80.2
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/adapters/bull.js +343 -346
- package/dist/adapters/bull.js.map +1 -1
- package/dist/adapters/bullmq.js +351 -388
- package/dist/adapters/bullmq.js.map +1 -1
- package/dist/adapters/cloud-tasks.js +307 -333
- package/dist/adapters/cloud-tasks.js.map +1 -1
- package/dist/adapters/postgres.js +288 -328
- package/dist/adapters/postgres.js.map +1 -1
- package/dist/adapters/sqlite.js +236 -258
- package/dist/adapters/sqlite.js.map +1 -1
- package/dist/adapters/sqs.js +363 -408
- package/dist/adapters/sqs.js.map +1 -1
- package/dist/chunks/base-store-DIasEzL0.js +386 -0
- package/dist/chunks/base-store-DIasEzL0.js.map +1 -0
- package/dist/cli/claude-context.js +17 -17
- package/dist/cli/claude-context.js.map +1 -1
- package/dist/index.js +219 -242
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/dist/chunks/base-store-DlNksWvQ.js +0 -324
- package/dist/chunks/base-store-DlNksWvQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,252 +1,229 @@
|
|
|
1
|
+
import { a as custom, c as linear, i as DEFAULT_RETRY_STRATEGY, l as noRetry, n as priorityToNumber, o as exponential, s as fromConfig, t as BaseJobStore } from "./chunks/base-store-DIasEzL0.js";
|
|
1
2
|
import { BullJobStore } from "./adapters/bull.js";
|
|
2
3
|
import { BullMQJobStore } from "./adapters/bullmq.js";
|
|
3
4
|
import { CloudTasksJobStore } from "./adapters/cloud-tasks.js";
|
|
4
5
|
import { PostgresJobStore } from "./adapters/postgres.js";
|
|
5
6
|
import { SqliteJobStore } from "./adapters/sqlite.js";
|
|
6
7
|
import { SQSJobStore } from "./adapters/sqs.js";
|
|
7
|
-
import { f as fromConfig } from "./chunks/base-store-DlNksWvQ.js";
|
|
8
|
-
import { B, D, c, e, l, n, p } from "./chunks/base-store-DlNksWvQ.js";
|
|
9
|
-
import { EventEmitter } from "node:events";
|
|
10
8
|
import { createId } from "@happyvertical/utils";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
import { EventEmitter } from "node:events";
|
|
10
|
+
//#region src/worker.ts
|
|
11
|
+
/**
|
|
12
|
+
* Default worker configuration
|
|
13
|
+
*/
|
|
14
|
+
var DEFAULT_CONFIG = {
|
|
15
|
+
id: "",
|
|
16
|
+
concurrency: 5,
|
|
17
|
+
queues: ["default"],
|
|
18
|
+
pollInterval: 1e3,
|
|
19
|
+
heartbeatInterval: 3e4,
|
|
20
|
+
shutdownTimeout: 3e4
|
|
18
21
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}, 100);
|
|
221
|
-
const timeout = setTimeout(() => {
|
|
222
|
-
clearInterval(checkInterval);
|
|
223
|
-
console.warn(
|
|
224
|
-
`Shutdown timeout: ${this.activeJobs.size} jobs still active`
|
|
225
|
-
);
|
|
226
|
-
resolve();
|
|
227
|
-
}, this.config.shutdownTimeout);
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
}
|
|
22
|
+
/**
|
|
23
|
+
* Job worker that processes jobs from a store
|
|
24
|
+
*/
|
|
25
|
+
var JobWorker = class extends EventEmitter {
|
|
26
|
+
id;
|
|
27
|
+
store;
|
|
28
|
+
handler;
|
|
29
|
+
config;
|
|
30
|
+
running = false;
|
|
31
|
+
activeJobs = /* @__PURE__ */ new Map();
|
|
32
|
+
pollTimer = null;
|
|
33
|
+
heartbeatTimer = null;
|
|
34
|
+
shutdownPromise = null;
|
|
35
|
+
constructor(store, handler, config = {}) {
|
|
36
|
+
super();
|
|
37
|
+
this.store = store;
|
|
38
|
+
this.handler = handler;
|
|
39
|
+
this.config = {
|
|
40
|
+
...DEFAULT_CONFIG,
|
|
41
|
+
...config,
|
|
42
|
+
id: config.id || `worker_${createId().slice(0, 8)}`
|
|
43
|
+
};
|
|
44
|
+
this.id = this.config.id;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Start processing jobs
|
|
48
|
+
*/
|
|
49
|
+
async start() {
|
|
50
|
+
if (this.running) return;
|
|
51
|
+
this.running = true;
|
|
52
|
+
this.store.subscribe(async (event) => {
|
|
53
|
+
if (event.type === "job.ready" && this.running) await this.poll();
|
|
54
|
+
});
|
|
55
|
+
this.startPolling();
|
|
56
|
+
this.startHeartbeat();
|
|
57
|
+
this.emit("worker:started");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stop processing jobs (graceful shutdown)
|
|
61
|
+
*/
|
|
62
|
+
async stop() {
|
|
63
|
+
if (!this.running) return;
|
|
64
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
65
|
+
this.running = false;
|
|
66
|
+
if (this.pollTimer) {
|
|
67
|
+
clearTimeout(this.pollTimer);
|
|
68
|
+
this.pollTimer = null;
|
|
69
|
+
}
|
|
70
|
+
if (this.heartbeatTimer) {
|
|
71
|
+
clearInterval(this.heartbeatTimer);
|
|
72
|
+
this.heartbeatTimer = null;
|
|
73
|
+
}
|
|
74
|
+
this.shutdownPromise = this.waitForActiveJobs();
|
|
75
|
+
try {
|
|
76
|
+
await this.shutdownPromise;
|
|
77
|
+
} finally {
|
|
78
|
+
this.shutdownPromise = null;
|
|
79
|
+
this.emit("worker:stopped");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if worker is running
|
|
84
|
+
*/
|
|
85
|
+
isRunning() {
|
|
86
|
+
return this.running;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get count of active jobs
|
|
90
|
+
*/
|
|
91
|
+
activeJobCount() {
|
|
92
|
+
return this.activeJobs.size;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Start the polling loop
|
|
96
|
+
*/
|
|
97
|
+
startPolling() {
|
|
98
|
+
const scheduleNextPoll = () => {
|
|
99
|
+
if (!this.running) return;
|
|
100
|
+
if (!this.store.waitForUpdate) {
|
|
101
|
+
this.pollTimer = setTimeout(poll, this.config.pollInterval);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.pollTimer = setTimeout(() => {
|
|
105
|
+
waitThenPoll();
|
|
106
|
+
}, 0);
|
|
107
|
+
};
|
|
108
|
+
const waitThenPoll = async () => {
|
|
109
|
+
try {
|
|
110
|
+
await this.store.waitForUpdate?.(this.config.pollInterval);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.emit("worker:error", error);
|
|
113
|
+
}
|
|
114
|
+
if (this.running) await poll();
|
|
115
|
+
};
|
|
116
|
+
const poll = async () => {
|
|
117
|
+
if (!this.running) return;
|
|
118
|
+
try {
|
|
119
|
+
await this.poll();
|
|
120
|
+
} catch (error) {
|
|
121
|
+
this.emit("worker:error", error);
|
|
122
|
+
}
|
|
123
|
+
scheduleNextPoll();
|
|
124
|
+
};
|
|
125
|
+
poll();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Poll for and process jobs
|
|
129
|
+
*/
|
|
130
|
+
async poll() {
|
|
131
|
+
const available = this.config.concurrency - this.activeJobs.size;
|
|
132
|
+
if (available <= 0) return;
|
|
133
|
+
const jobs = await this.store.dequeue(this.config.queues, available, this.id);
|
|
134
|
+
for (const job of jobs) this.processJob(job);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Process a single job
|
|
138
|
+
*/
|
|
139
|
+
async processJob(job) {
|
|
140
|
+
this.activeJobs.set(job.id, job);
|
|
141
|
+
this.emit("job:started", job);
|
|
142
|
+
let timeoutId = null;
|
|
143
|
+
try {
|
|
144
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
145
|
+
timeoutId = setTimeout(() => {
|
|
146
|
+
reject(/* @__PURE__ */ new Error(`Job timeout after ${job.timeout}ms`));
|
|
147
|
+
}, job.timeout);
|
|
148
|
+
});
|
|
149
|
+
const result = await Promise.race([this.handler(job), timeoutPromise]);
|
|
150
|
+
await this.store.update(job.id, {
|
|
151
|
+
status: "completed",
|
|
152
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
153
|
+
resultPointer: result.resultPointer ?? null
|
|
154
|
+
});
|
|
155
|
+
this.emit("job:completed", job, result.result);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
await this.handleJobError(job, error);
|
|
158
|
+
} finally {
|
|
159
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
160
|
+
this.activeJobs.delete(job.id);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Handle job execution error
|
|
165
|
+
*/
|
|
166
|
+
async handleJobError(job, error) {
|
|
167
|
+
const decision = fromConfig(job.retryStrategy).shouldRetry(job.attempts, error);
|
|
168
|
+
if (decision.shouldRetry && job.attempts < job.maxAttempts) {
|
|
169
|
+
const nextRunAt = new Date(Date.now() + decision.delay);
|
|
170
|
+
await this.store.update(job.id, {
|
|
171
|
+
status: "pending",
|
|
172
|
+
lastError: error.message,
|
|
173
|
+
runAt: nextRunAt,
|
|
174
|
+
workerId: null,
|
|
175
|
+
workerHeartbeat: null
|
|
176
|
+
});
|
|
177
|
+
this.emit("job:retrying", job, error, decision.delay);
|
|
178
|
+
} else {
|
|
179
|
+
await this.store.update(job.id, {
|
|
180
|
+
status: "failed",
|
|
181
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
182
|
+
lastError: error.message
|
|
183
|
+
});
|
|
184
|
+
this.emit("job:failed", job, error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Start heartbeat loop to keep jobs alive
|
|
189
|
+
*/
|
|
190
|
+
startHeartbeat() {
|
|
191
|
+
this.heartbeatTimer = setInterval(async () => {
|
|
192
|
+
for (const [jobId] of this.activeJobs) try {
|
|
193
|
+
await this.store.heartbeat(jobId, this.id);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.warn(`Heartbeat failed for job ${jobId}:`, error);
|
|
196
|
+
}
|
|
197
|
+
}, this.config.heartbeatInterval);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Wait for active jobs to complete with timeout
|
|
201
|
+
*/
|
|
202
|
+
async waitForActiveJobs() {
|
|
203
|
+
if (this.activeJobs.size === 0) return;
|
|
204
|
+
return new Promise((resolve) => {
|
|
205
|
+
const checkInterval = setInterval(() => {
|
|
206
|
+
if (this.activeJobs.size === 0) {
|
|
207
|
+
clearInterval(checkInterval);
|
|
208
|
+
clearTimeout(timeout);
|
|
209
|
+
resolve();
|
|
210
|
+
}
|
|
211
|
+
}, 100);
|
|
212
|
+
const timeout = setTimeout(() => {
|
|
213
|
+
clearInterval(checkInterval);
|
|
214
|
+
console.warn(`Shutdown timeout: ${this.activeJobs.size} jobs still active`);
|
|
215
|
+
resolve();
|
|
216
|
+
}, this.config.shutdownTimeout);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Create a job worker
|
|
222
|
+
*/
|
|
231
223
|
function createWorker(store, handler, config) {
|
|
232
|
-
|
|
224
|
+
return new JobWorker(store, handler, config);
|
|
233
225
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
CloudTasksJobStore,
|
|
239
|
-
D as DEFAULT_RETRY_STRATEGY,
|
|
240
|
-
JobWorker,
|
|
241
|
-
PostgresJobStore,
|
|
242
|
-
SQSJobStore,
|
|
243
|
-
SqliteJobStore,
|
|
244
|
-
createWorker,
|
|
245
|
-
c as custom,
|
|
246
|
-
e as exponential,
|
|
247
|
-
fromConfig,
|
|
248
|
-
l as linear,
|
|
249
|
-
n as noRetry,
|
|
250
|
-
p as priorityToNumber
|
|
251
|
-
};
|
|
252
|
-
//# sourceMappingURL=index.js.map
|
|
226
|
+
//#endregion
|
|
227
|
+
export { BaseJobStore, BullJobStore, BullMQJobStore, CloudTasksJobStore, DEFAULT_RETRY_STRATEGY, JobWorker, PostgresJobStore, SQSJobStore, SqliteJobStore, createWorker, custom, exponential, fromConfig, linear, noRetry, priorityToNumber };
|
|
228
|
+
|
|
229
|
+
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/worker.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { createId } from '@happyvertical/utils';\nimport { fromConfig } from './retry.js';\nimport type {\n Worker as IWorker,\n Job,\n JobHandler,\n JobStore,\n WorkerConfig,\n} from './types.js';\n\n/**\n * Worker events\n */\nexport interface WorkerEvents {\n 'job:started': (job: Job) => void;\n 'job:completed': (job: Job, result: unknown) => void;\n 'job:failed': (job: Job, error: Error) => void;\n 'job:retrying': (job: Job, error: Error, delay: number) => void;\n 'worker:started': () => void;\n 'worker:stopped': () => void;\n 'worker:error': (error: Error) => void;\n}\n\n/**\n * Default worker configuration\n */\nconst DEFAULT_CONFIG: Required<WorkerConfig> = {\n id: '',\n concurrency: 5,\n queues: ['default'],\n pollInterval: 1000,\n heartbeatInterval: 30000,\n shutdownTimeout: 30000,\n};\n\n/**\n * Job worker that processes jobs from a store\n */\nexport class JobWorker extends EventEmitter implements IWorker {\n readonly id: string;\n private readonly store: JobStore;\n private readonly handler: JobHandler;\n private readonly config: Required<WorkerConfig>;\n private running = false;\n private activeJobs = new Map<string, Job>();\n private pollTimer: NodeJS.Timeout | null = null;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private shutdownPromise: Promise<void> | null = null;\n\n constructor(store: JobStore, handler: JobHandler, config: WorkerConfig = {}) {\n super();\n this.store = store;\n this.handler = handler;\n this.config = {\n ...DEFAULT_CONFIG,\n ...config,\n id: config.id || `worker_${createId().slice(0, 8)}`,\n };\n this.id = this.config.id;\n }\n\n /**\n * Start processing jobs\n */\n async start(): Promise<void> {\n if (this.running) return;\n\n this.running = true;\n\n // Subscribe to store events for push-based notifications\n this.store.subscribe(async (event) => {\n if (event.type === 'job.ready' && this.running) {\n // Immediately try to dequeue when a job becomes ready\n await this.poll();\n }\n });\n\n // Start polling loop\n this.startPolling();\n\n // Start heartbeat loop\n this.startHeartbeat();\n\n this.emit('worker:started');\n }\n\n /**\n * Stop processing jobs (graceful shutdown)\n */\n async stop(): Promise<void> {\n if (!this.running) return;\n if (this.shutdownPromise) return this.shutdownPromise;\n\n this.running = false;\n\n // Stop timers\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n // Wait for active jobs to complete (with timeout)\n this.shutdownPromise = this.waitForActiveJobs();\n\n try {\n await this.shutdownPromise;\n } finally {\n this.shutdownPromise = null;\n this.emit('worker:stopped');\n }\n }\n\n /**\n * Check if worker is running\n */\n isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get count of active jobs\n */\n activeJobCount(): number {\n return this.activeJobs.size;\n }\n\n /**\n * Start the polling loop\n */\n private startPolling(): void {\n const scheduleNextPoll = () => {\n if (!this.running) return;\n\n if (!this.store.waitForUpdate) {\n this.pollTimer = setTimeout(poll, this.config.pollInterval);\n return;\n }\n\n this.pollTimer = setTimeout(() => {\n void waitThenPoll();\n }, 0);\n };\n\n const waitThenPoll = async () => {\n try {\n await this.store.waitForUpdate?.(this.config.pollInterval);\n } catch (error) {\n this.emit('worker:error', error as Error);\n }\n\n if (this.running) {\n await poll();\n }\n };\n\n const poll = async () => {\n if (!this.running) return;\n\n try {\n await this.poll();\n } catch (error) {\n this.emit('worker:error', error as Error);\n }\n\n // Schedule next poll\n scheduleNextPoll();\n };\n\n // Start immediately\n poll();\n }\n\n /**\n * Poll for and process jobs\n */\n private async poll(): Promise<void> {\n // Calculate how many jobs we can take\n const available = this.config.concurrency - this.activeJobs.size;\n if (available <= 0) return;\n\n // Dequeue jobs\n const jobs = await this.store.dequeue(\n this.config.queues,\n available,\n this.id,\n );\n\n // Process each job concurrently\n for (const job of jobs) {\n this.processJob(job);\n }\n }\n\n /**\n * Process a single job\n */\n private async processJob(job: Job): Promise<void> {\n this.activeJobs.set(job.id, job);\n this.emit('job:started', job);\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n try {\n // Set up timeout with cleanup capability\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`Job timeout after ${job.timeout}ms`));\n }, job.timeout);\n });\n\n // Execute handler with timeout\n const result = await Promise.race([this.handler(job), timeoutPromise]);\n\n // Job completed successfully\n await this.store.update(job.id, {\n status: 'completed',\n completedAt: new Date(),\n resultPointer: result.resultPointer ?? null,\n });\n\n this.emit('job:completed', job, result.result);\n } catch (error) {\n await this.handleJobError(job, error as Error);\n } finally {\n // Clean up timeout timer to prevent memory leak\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n this.activeJobs.delete(job.id);\n }\n }\n\n /**\n * Handle job execution error\n */\n private async handleJobError(job: Job, error: Error): Promise<void> {\n const strategy = fromConfig(job.retryStrategy);\n const decision = strategy.shouldRetry(job.attempts, error);\n\n if (decision.shouldRetry && job.attempts < job.maxAttempts) {\n // Schedule retry\n const nextRunAt = new Date(Date.now() + decision.delay);\n\n await this.store.update(job.id, {\n status: 'pending',\n lastError: error.message,\n runAt: nextRunAt,\n workerId: null,\n workerHeartbeat: null,\n });\n\n this.emit('job:retrying', job, error, decision.delay);\n } else {\n // Job failed permanently\n await this.store.update(job.id, {\n status: 'failed',\n completedAt: new Date(),\n lastError: error.message,\n });\n\n this.emit('job:failed', job, error);\n }\n }\n\n /**\n * Start heartbeat loop to keep jobs alive\n */\n private startHeartbeat(): void {\n this.heartbeatTimer = setInterval(async () => {\n for (const [jobId] of this.activeJobs) {\n try {\n await this.store.heartbeat(jobId, this.id);\n } catch (error) {\n // Log heartbeat errors but don't interrupt job processing\n console.warn(`Heartbeat failed for job ${jobId}:`, error);\n }\n }\n }, this.config.heartbeatInterval);\n }\n\n /**\n * Wait for active jobs to complete with timeout\n */\n private async waitForActiveJobs(): Promise<void> {\n if (this.activeJobs.size === 0) return;\n\n return new Promise((resolve) => {\n const checkInterval = setInterval(() => {\n if (this.activeJobs.size === 0) {\n clearInterval(checkInterval);\n clearTimeout(timeout);\n resolve();\n }\n }, 100);\n\n const timeout = setTimeout(() => {\n clearInterval(checkInterval);\n console.warn(\n `Shutdown timeout: ${this.activeJobs.size} jobs still active`,\n );\n resolve();\n }, this.config.shutdownTimeout);\n });\n }\n}\n\n/**\n * Create a job worker\n */\nexport function createWorker(\n store: JobStore,\n handler: JobHandler,\n config?: WorkerConfig,\n): JobWorker {\n return new JobWorker(store, handler, config);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport { createId } from '@happyvertical/utils';\nimport { fromConfig } from './retry.js';\nimport type {\n Worker as IWorker,\n Job,\n JobHandler,\n JobStore,\n WorkerConfig,\n} from './types.js';\n\n/**\n * Worker events\n */\nexport interface WorkerEvents {\n 'job:started': (job: Job) => void;\n 'job:completed': (job: Job, result: unknown) => void;\n 'job:failed': (job: Job, error: Error) => void;\n 'job:retrying': (job: Job, error: Error, delay: number) => void;\n 'worker:started': () => void;\n 'worker:stopped': () => void;\n 'worker:error': (error: Error) => void;\n}\n\n/**\n * Default worker configuration\n */\nconst DEFAULT_CONFIG: Required<WorkerConfig> = {\n id: '',\n concurrency: 5,\n queues: ['default'],\n pollInterval: 1000,\n heartbeatInterval: 30000,\n shutdownTimeout: 30000,\n};\n\n/**\n * Job worker that processes jobs from a store\n */\nexport class JobWorker extends EventEmitter implements IWorker {\n readonly id: string;\n private readonly store: JobStore;\n private readonly handler: JobHandler;\n private readonly config: Required<WorkerConfig>;\n private running = false;\n private activeJobs = new Map<string, Job>();\n private pollTimer: NodeJS.Timeout | null = null;\n private heartbeatTimer: NodeJS.Timeout | null = null;\n private shutdownPromise: Promise<void> | null = null;\n\n constructor(store: JobStore, handler: JobHandler, config: WorkerConfig = {}) {\n super();\n this.store = store;\n this.handler = handler;\n this.config = {\n ...DEFAULT_CONFIG,\n ...config,\n id: config.id || `worker_${createId().slice(0, 8)}`,\n };\n this.id = this.config.id;\n }\n\n /**\n * Start processing jobs\n */\n async start(): Promise<void> {\n if (this.running) return;\n\n this.running = true;\n\n // Subscribe to store events for push-based notifications\n this.store.subscribe(async (event) => {\n if (event.type === 'job.ready' && this.running) {\n // Immediately try to dequeue when a job becomes ready\n await this.poll();\n }\n });\n\n // Start polling loop\n this.startPolling();\n\n // Start heartbeat loop\n this.startHeartbeat();\n\n this.emit('worker:started');\n }\n\n /**\n * Stop processing jobs (graceful shutdown)\n */\n async stop(): Promise<void> {\n if (!this.running) return;\n if (this.shutdownPromise) return this.shutdownPromise;\n\n this.running = false;\n\n // Stop timers\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n\n // Wait for active jobs to complete (with timeout)\n this.shutdownPromise = this.waitForActiveJobs();\n\n try {\n await this.shutdownPromise;\n } finally {\n this.shutdownPromise = null;\n this.emit('worker:stopped');\n }\n }\n\n /**\n * Check if worker is running\n */\n isRunning(): boolean {\n return this.running;\n }\n\n /**\n * Get count of active jobs\n */\n activeJobCount(): number {\n return this.activeJobs.size;\n }\n\n /**\n * Start the polling loop\n */\n private startPolling(): void {\n const scheduleNextPoll = () => {\n if (!this.running) return;\n\n if (!this.store.waitForUpdate) {\n this.pollTimer = setTimeout(poll, this.config.pollInterval);\n return;\n }\n\n this.pollTimer = setTimeout(() => {\n void waitThenPoll();\n }, 0);\n };\n\n const waitThenPoll = async () => {\n try {\n await this.store.waitForUpdate?.(this.config.pollInterval);\n } catch (error) {\n this.emit('worker:error', error as Error);\n }\n\n if (this.running) {\n await poll();\n }\n };\n\n const poll = async () => {\n if (!this.running) return;\n\n try {\n await this.poll();\n } catch (error) {\n this.emit('worker:error', error as Error);\n }\n\n // Schedule next poll\n scheduleNextPoll();\n };\n\n // Start immediately\n poll();\n }\n\n /**\n * Poll for and process jobs\n */\n private async poll(): Promise<void> {\n // Calculate how many jobs we can take\n const available = this.config.concurrency - this.activeJobs.size;\n if (available <= 0) return;\n\n // Dequeue jobs\n const jobs = await this.store.dequeue(\n this.config.queues,\n available,\n this.id,\n );\n\n // Process each job concurrently\n for (const job of jobs) {\n this.processJob(job);\n }\n }\n\n /**\n * Process a single job\n */\n private async processJob(job: Job): Promise<void> {\n this.activeJobs.set(job.id, job);\n this.emit('job:started', job);\n\n let timeoutId: NodeJS.Timeout | null = null;\n\n try {\n // Set up timeout with cleanup capability\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new Error(`Job timeout after ${job.timeout}ms`));\n }, job.timeout);\n });\n\n // Execute handler with timeout\n const result = await Promise.race([this.handler(job), timeoutPromise]);\n\n // Job completed successfully\n await this.store.update(job.id, {\n status: 'completed',\n completedAt: new Date(),\n resultPointer: result.resultPointer ?? null,\n });\n\n this.emit('job:completed', job, result.result);\n } catch (error) {\n await this.handleJobError(job, error as Error);\n } finally {\n // Clean up timeout timer to prevent memory leak\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n this.activeJobs.delete(job.id);\n }\n }\n\n /**\n * Handle job execution error\n */\n private async handleJobError(job: Job, error: Error): Promise<void> {\n const strategy = fromConfig(job.retryStrategy);\n const decision = strategy.shouldRetry(job.attempts, error);\n\n if (decision.shouldRetry && job.attempts < job.maxAttempts) {\n // Schedule retry\n const nextRunAt = new Date(Date.now() + decision.delay);\n\n await this.store.update(job.id, {\n status: 'pending',\n lastError: error.message,\n runAt: nextRunAt,\n workerId: null,\n workerHeartbeat: null,\n });\n\n this.emit('job:retrying', job, error, decision.delay);\n } else {\n // Job failed permanently\n await this.store.update(job.id, {\n status: 'failed',\n completedAt: new Date(),\n lastError: error.message,\n });\n\n this.emit('job:failed', job, error);\n }\n }\n\n /**\n * Start heartbeat loop to keep jobs alive\n */\n private startHeartbeat(): void {\n this.heartbeatTimer = setInterval(async () => {\n for (const [jobId] of this.activeJobs) {\n try {\n await this.store.heartbeat(jobId, this.id);\n } catch (error) {\n // Log heartbeat errors but don't interrupt job processing\n console.warn(`Heartbeat failed for job ${jobId}:`, error);\n }\n }\n }, this.config.heartbeatInterval);\n }\n\n /**\n * Wait for active jobs to complete with timeout\n */\n private async waitForActiveJobs(): Promise<void> {\n if (this.activeJobs.size === 0) return;\n\n return new Promise((resolve) => {\n const checkInterval = setInterval(() => {\n if (this.activeJobs.size === 0) {\n clearInterval(checkInterval);\n clearTimeout(timeout);\n resolve();\n }\n }, 100);\n\n const timeout = setTimeout(() => {\n clearInterval(checkInterval);\n console.warn(\n `Shutdown timeout: ${this.activeJobs.size} jobs still active`,\n );\n resolve();\n }, this.config.shutdownTimeout);\n });\n }\n}\n\n/**\n * Create a job worker\n */\nexport function createWorker(\n store: JobStore,\n handler: JobHandler,\n config?: WorkerConfig,\n): JobWorker {\n return new JobWorker(store, handler, config);\n}\n"],"mappings":";;;;;;;;;;;;;AA2BA,IAAM,iBAAyC;CAC7C,IAAI;CACJ,aAAa;CACb,QAAQ,CAAC,SAAS;CAClB,cAAc;CACd,mBAAmB;CACnB,iBAAiB;AACnB;;;;AAKA,IAAa,YAAb,cAA+B,aAAgC;CAC7D;CACA;CACA;CACA;CACA,UAAkB;CAClB,6BAAqB,IAAI,IAAiB;CAC1C,YAA2C;CAC3C,iBAAgD;CAChD,kBAAgD;CAEhD,YAAY,OAAiB,SAAqB,SAAuB,CAAC,GAAG;EAC3E,MAAM;EACN,KAAK,QAAQ;EACb,KAAK,UAAU;EACf,KAAK,SAAS;GACZ,GAAG;GACH,GAAG;GACH,IAAI,OAAO,MAAM,UAAU,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC;EAClD;EACA,KAAK,KAAK,KAAK,OAAO;CACxB;;;;CAKA,MAAM,QAAuB;EAC3B,IAAI,KAAK,SAAS;EAElB,KAAK,UAAU;EAGf,KAAK,MAAM,UAAU,OAAO,UAAU;GACpC,IAAI,MAAM,SAAS,eAAe,KAAK,SAErC,MAAM,KAAK,KAAK;EAEpB,CAAC;EAGD,KAAK,aAAa;EAGlB,KAAK,eAAe;EAEpB,KAAK,KAAK,gBAAgB;CAC5B;;;;CAKA,MAAM,OAAsB;EAC1B,IAAI,CAAC,KAAK,SAAS;EACnB,IAAI,KAAK,iBAAiB,OAAO,KAAK;EAEtC,KAAK,UAAU;EAGf,IAAI,KAAK,WAAW;GAClB,aAAa,KAAK,SAAS;GAC3B,KAAK,YAAY;EACnB;EACA,IAAI,KAAK,gBAAgB;GACvB,cAAc,KAAK,cAAc;GACjC,KAAK,iBAAiB;EACxB;EAGA,KAAK,kBAAkB,KAAK,kBAAkB;EAE9C,IAAI;GACF,MAAM,KAAK;EACb,UAAU;GACR,KAAK,kBAAkB;GACvB,KAAK,KAAK,gBAAgB;EAC5B;CACF;;;;CAKA,YAAqB;EACnB,OAAO,KAAK;CACd;;;;CAKA,iBAAyB;EACvB,OAAO,KAAK,WAAW;CACzB;;;;CAKA,eAA6B;EAC3B,MAAM,yBAAyB;GAC7B,IAAI,CAAC,KAAK,SAAS;GAEnB,IAAI,CAAC,KAAK,MAAM,eAAe;IAC7B,KAAK,YAAY,WAAW,MAAM,KAAK,OAAO,YAAY;IAC1D;GACF;GAEA,KAAK,YAAY,iBAAiB;IAChC,aAAkB;GACpB,GAAG,CAAC;EACN;EAEA,MAAM,eAAe,YAAY;GAC/B,IAAI;IACF,MAAM,KAAK,MAAM,gBAAgB,KAAK,OAAO,YAAY;GAC3D,SAAS,OAAO;IACd,KAAK,KAAK,gBAAgB,KAAc;GAC1C;GAEA,IAAI,KAAK,SACP,MAAM,KAAK;EAEf;EAEA,MAAM,OAAO,YAAY;GACvB,IAAI,CAAC,KAAK,SAAS;GAEnB,IAAI;IACF,MAAM,KAAK,KAAK;GAClB,SAAS,OAAO;IACd,KAAK,KAAK,gBAAgB,KAAc;GAC1C;GAGA,iBAAiB;EACnB;EAGA,KAAK;CACP;;;;CAKA,MAAc,OAAsB;EAElC,MAAM,YAAY,KAAK,OAAO,cAAc,KAAK,WAAW;EAC5D,IAAI,aAAa,GAAG;EAGpB,MAAM,OAAO,MAAM,KAAK,MAAM,QAC5B,KAAK,OAAO,QACZ,WACA,KAAK,EACP;EAGA,KAAK,MAAM,OAAO,MAChB,KAAK,WAAW,GAAG;CAEvB;;;;CAKA,MAAc,WAAW,KAAyB;EAChD,KAAK,WAAW,IAAI,IAAI,IAAI,GAAG;EAC/B,KAAK,KAAK,eAAe,GAAG;EAE5B,IAAI,YAAmC;EAEvC,IAAI;GAEF,MAAM,iBAAiB,IAAI,SAAgB,GAAG,WAAW;IACvD,YAAY,iBAAiB;KAC3B,uBAAO,IAAI,MAAM,qBAAqB,IAAI,QAAQ,GAAG,CAAC;IACxD,GAAG,IAAI,OAAO;GAChB,CAAC;GAGD,MAAM,SAAS,MAAM,QAAQ,KAAK,CAAC,KAAK,QAAQ,GAAG,GAAG,cAAc,CAAC;GAGrE,MAAM,KAAK,MAAM,OAAO,IAAI,IAAI;IAC9B,QAAQ;IACR,6BAAa,IAAI,KAAK;IACtB,eAAe,OAAO,iBAAiB;GACzC,CAAC;GAED,KAAK,KAAK,iBAAiB,KAAK,OAAO,MAAM;EAC/C,SAAS,OAAO;GACd,MAAM,KAAK,eAAe,KAAK,KAAc;EAC/C,UAAU;GAER,IAAI,WACF,aAAa,SAAS;GAExB,KAAK,WAAW,OAAO,IAAI,EAAE;EAC/B;CACF;;;;CAKA,MAAc,eAAe,KAAU,OAA6B;EAElE,MAAM,WADW,WAAW,IAAI,aACf,CAAA,CAAS,YAAY,IAAI,UAAU,KAAK;EAEzD,IAAI,SAAS,eAAe,IAAI,WAAW,IAAI,aAAa;GAE1D,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,KAAK;GAEtD,MAAM,KAAK,MAAM,OAAO,IAAI,IAAI;IAC9B,QAAQ;IACR,WAAW,MAAM;IACjB,OAAO;IACP,UAAU;IACV,iBAAiB;GACnB,CAAC;GAED,KAAK,KAAK,gBAAgB,KAAK,OAAO,SAAS,KAAK;EACtD,OAAO;GAEL,MAAM,KAAK,MAAM,OAAO,IAAI,IAAI;IAC9B,QAAQ;IACR,6BAAa,IAAI,KAAK;IACtB,WAAW,MAAM;GACnB,CAAC;GAED,KAAK,KAAK,cAAc,KAAK,KAAK;EACpC;CACF;;;;CAKA,iBAA+B;EAC7B,KAAK,iBAAiB,YAAY,YAAY;GAC5C,KAAK,MAAM,CAAC,UAAU,KAAK,YACzB,IAAI;IACF,MAAM,KAAK,MAAM,UAAU,OAAO,KAAK,EAAE;GAC3C,SAAS,OAAO;IAEd,QAAQ,KAAK,4BAA4B,MAAM,IAAI,KAAK;GAC1D;EAEJ,GAAG,KAAK,OAAO,iBAAiB;CAClC;;;;CAKA,MAAc,oBAAmC;EAC/C,IAAI,KAAK,WAAW,SAAS,GAAG;EAEhC,OAAO,IAAI,SAAS,YAAY;GAC9B,MAAM,gBAAgB,kBAAkB;IACtC,IAAI,KAAK,WAAW,SAAS,GAAG;KAC9B,cAAc,aAAa;KAC3B,aAAa,OAAO;KACpB,QAAQ;IACV;GACF,GAAG,GAAG;GAEN,MAAM,UAAU,iBAAiB;IAC/B,cAAc,aAAa;IAC3B,QAAQ,KACN,qBAAqB,KAAK,WAAW,KAAK,mBAC5C;IACA,QAAQ;GACV,GAAG,KAAK,OAAO,eAAe;EAChC,CAAC;CACH;AACF;;;;AAKA,SAAgB,aACd,OACA,SACA,QACW;CACX,OAAO,IAAI,UAAU,OAAO,SAAS,MAAM;AAC7C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/jobs",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.2",
|
|
4
4
|
"description": "Job queue abstraction with multiple backend adapters (SQLite, PostgreSQL, Bull, SQS)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"author": "willgriffin@gmail.com",
|
|
69
69
|
"license": "ISC",
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@happyvertical/sql": "0.80.
|
|
72
|
-
"@happyvertical/utils": "0.80.
|
|
71
|
+
"@happyvertical/sql": "0.80.2",
|
|
72
|
+
"@happyvertical/utils": "0.80.2"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@aws-sdk/client-sqs": "^3.1034.0",
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"@types/bull": "^4.10.4",
|
|
78
78
|
"@types/node": "25.0.10",
|
|
79
79
|
"bull": "^4.16.5",
|
|
80
|
-
"bullmq": "^5.
|
|
81
|
-
"typescript": "
|
|
82
|
-
"vite": "
|
|
80
|
+
"bullmq": "^5.80.4",
|
|
81
|
+
"typescript": "5.9.3",
|
|
82
|
+
"vite": "8.1.4",
|
|
83
83
|
"vite-plugin-dts": "4.5.4",
|
|
84
|
-
"vitest": "
|
|
84
|
+
"vitest": "4.1.10"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
87
|
"@aws-sdk/client-sqs": ">=3.1034.0",
|