@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.
@@ -1,26 +1,33 @@
1
+ import { r as validateTableName, t as BaseJobStore } from "../chunks/base-store-DIasEzL0.js";
1
2
  import { getDatabase, syncSchema } from "@happyvertical/sql";
2
- import { B as BaseJobStore, v as validateTableName } from "../chunks/base-store-DlNksWvQ.js";
3
- class SqliteJobStore extends BaseJobStore {
4
- db = null;
5
- url;
6
- externalDb;
7
- tableName;
8
- capabilities;
9
- constructor(config = {}) {
10
- super();
11
- this.url = config.url ?? ":memory:";
12
- this.externalDb = config.db ?? null;
13
- this.tableName = validateTableName(config.tableName ?? "_jobs");
14
- this.capabilities = config.capabilities;
15
- }
16
- async initialize() {
17
- if (this.initialized) return;
18
- this.db = this.externalDb ?? await getDatabase({
19
- type: "sqlite",
20
- url: this.url,
21
- capabilities: this.capabilities
22
- });
23
- const schema = `
3
+ //#region src/adapters/sqlite.ts
4
+ /**
5
+ * SQLite-based job store
6
+ *
7
+ * Uses SQLite for job persistence. Supports polling-based job retrieval.
8
+ * Good for single-instance deployments or development.
9
+ */
10
+ var SqliteJobStore = class extends BaseJobStore {
11
+ db = null;
12
+ url;
13
+ externalDb;
14
+ tableName;
15
+ capabilities;
16
+ constructor(config = {}) {
17
+ super();
18
+ this.url = config.url ?? ":memory:";
19
+ this.externalDb = config.db ?? null;
20
+ this.tableName = validateTableName(config.tableName ?? "_jobs");
21
+ this.capabilities = config.capabilities;
22
+ }
23
+ async initialize() {
24
+ if (this.initialized) return;
25
+ this.db = this.externalDb ?? await getDatabase({
26
+ type: "sqlite",
27
+ url: this.url,
28
+ capabilities: this.capabilities
29
+ });
30
+ const schema = `
24
31
  CREATE TABLE IF NOT EXISTS "${this.tableName}" (
25
32
  id TEXT PRIMARY KEY,
26
33
  queue TEXT NOT NULL DEFAULT 'default',
@@ -47,59 +54,60 @@ class SqliteJobStore extends BaseJobStore {
47
54
  CREATE INDEX IF NOT EXISTS "idx_${this.tableName}_created_at" ON "${this.tableName}" (created_at);
48
55
  CREATE INDEX IF NOT EXISTS "idx_${this.tableName}_queue" ON "${this.tableName}" (queue);
49
56
  `;
50
- await syncSchema({ db: this.db, schema });
51
- this.initialized = true;
52
- }
53
- async enqueue(options) {
54
- if (!this.db) throw new Error("Store not initialized");
55
- const job = this.createJobRecord(options);
56
- await this.db.insert(this.tableName, {
57
- id: job.id,
58
- queue: job.queue,
59
- payload: JSON.stringify(job.payload),
60
- status: job.status,
61
- priority: job.priority,
62
- attempts: job.attempts,
63
- max_attempts: job.maxAttempts,
64
- run_at: job.runAt.toISOString(),
65
- started_at: job.startedAt?.toISOString() ?? null,
66
- completed_at: job.completedAt?.toISOString() ?? null,
67
- timeout: job.timeout,
68
- timeout_behavior: job.timeoutBehavior,
69
- last_error: job.lastError,
70
- result_pointer: job.resultPointer,
71
- retry_strategy: JSON.stringify(job.retryStrategy),
72
- worker_id: job.workerId,
73
- worker_heartbeat: job.workerHeartbeat?.toISOString() ?? null,
74
- created_at: job.createdAt.toISOString(),
75
- updated_at: job.updatedAt.toISOString()
76
- });
77
- await this.emitEvent("job.created", job);
78
- if (job.runAt <= /* @__PURE__ */ new Date()) {
79
- await this.emitEvent("job.ready", job);
80
- }
81
- return job;
82
- }
83
- async dequeue(queues, limit, workerId) {
84
- if (!this.db) throw new Error("Store not initialized");
85
- const now = (/* @__PURE__ */ new Date()).toISOString();
86
- const queuePlaceholders = queues.map(() => "?").join(", ");
87
- const { rows } = await this.db.query(
88
- `
57
+ await syncSchema({
58
+ db: this.db,
59
+ schema
60
+ });
61
+ this.initialized = true;
62
+ }
63
+ async enqueue(options) {
64
+ if (!this.db) throw new Error("Store not initialized");
65
+ const job = this.createJobRecord(options);
66
+ await this.db.insert(this.tableName, {
67
+ id: job.id,
68
+ queue: job.queue,
69
+ payload: JSON.stringify(job.payload),
70
+ status: job.status,
71
+ priority: job.priority,
72
+ attempts: job.attempts,
73
+ max_attempts: job.maxAttempts,
74
+ run_at: job.runAt.toISOString(),
75
+ started_at: job.startedAt?.toISOString() ?? null,
76
+ completed_at: job.completedAt?.toISOString() ?? null,
77
+ timeout: job.timeout,
78
+ timeout_behavior: job.timeoutBehavior,
79
+ last_error: job.lastError,
80
+ result_pointer: job.resultPointer,
81
+ retry_strategy: JSON.stringify(job.retryStrategy),
82
+ worker_id: job.workerId,
83
+ worker_heartbeat: job.workerHeartbeat?.toISOString() ?? null,
84
+ created_at: job.createdAt.toISOString(),
85
+ updated_at: job.updatedAt.toISOString()
86
+ });
87
+ await this.emitEvent("job.created", job);
88
+ if (job.runAt <= /* @__PURE__ */ new Date()) await this.emitEvent("job.ready", job);
89
+ return job;
90
+ }
91
+ async dequeue(queues, limit, workerId) {
92
+ if (!this.db) throw new Error("Store not initialized");
93
+ const now = (/* @__PURE__ */ new Date()).toISOString();
94
+ const queuePlaceholders = queues.map(() => "?").join(", ");
95
+ const { rows } = await this.db.query(`
89
96
  SELECT * FROM ${this.tableName}
90
97
  WHERE status = 'pending'
91
98
  AND queue IN (${queuePlaceholders})
92
99
  AND run_at <= ?
93
100
  ORDER BY priority DESC, run_at ASC
94
101
  LIMIT ?
95
- `,
96
- [...queues, now, limit]
97
- );
98
- if (!rows.length) return [];
99
- const jobIds = rows.map((r) => r.id);
100
- const idPlaceholders = jobIds.map(() => "?").join(", ");
101
- await this.db.query(
102
- `
102
+ `, [
103
+ ...queues,
104
+ now,
105
+ limit
106
+ ]);
107
+ if (!rows.length) return [];
108
+ const jobIds = rows.map((r) => r.id);
109
+ const idPlaceholders = jobIds.map(() => "?").join(", ");
110
+ await this.db.query(`
103
111
  UPDATE ${this.tableName}
104
112
  SET status = 'running',
105
113
  worker_id = ?,
@@ -109,133 +117,115 @@ class SqliteJobStore extends BaseJobStore {
109
117
  updated_at = ?
110
118
  WHERE id IN (${idPlaceholders})
111
119
  AND status = 'pending'
112
- `,
113
- [workerId, now, now, now, ...jobIds]
114
- );
115
- const jobs = rows.map((row) => {
116
- const job = this.parseJobRow(row);
117
- job.status = "running";
118
- job.workerId = workerId;
119
- job.workerHeartbeat = /* @__PURE__ */ new Date();
120
- job.startedAt = /* @__PURE__ */ new Date();
121
- job.attempts += 1;
122
- return job;
123
- });
124
- for (const job of jobs) {
125
- await this.emitEvent("job.started", job);
126
- }
127
- return jobs;
128
- }
129
- async update(id, updates) {
130
- if (!this.db) throw new Error("Store not initialized");
131
- const setClause = [];
132
- const params = [];
133
- const fieldMap = {
134
- queue: "queue",
135
- payload: "payload",
136
- status: "status",
137
- priority: "priority",
138
- attempts: "attempts",
139
- maxAttempts: "max_attempts",
140
- runAt: "run_at",
141
- startedAt: "started_at",
142
- completedAt: "completed_at",
143
- timeout: "timeout",
144
- timeoutBehavior: "timeout_behavior",
145
- lastError: "last_error",
146
- resultPointer: "result_pointer",
147
- retryStrategy: "retry_strategy",
148
- workerId: "worker_id",
149
- workerHeartbeat: "worker_heartbeat"
150
- };
151
- for (const [key, value] of Object.entries(updates)) {
152
- const column = fieldMap[key];
153
- if (!column) continue;
154
- setClause.push(`${column} = ?`);
155
- if (value instanceof Date) {
156
- params.push(value.toISOString());
157
- } else if (typeof value === "object" && value !== null) {
158
- params.push(JSON.stringify(value));
159
- } else {
160
- params.push(value);
161
- }
162
- }
163
- if (setClause.length === 0) {
164
- const job2 = await this.get(id);
165
- if (!job2) throw new Error(`Job not found: ${id}`);
166
- return job2;
167
- }
168
- setClause.push("updated_at = ?");
169
- params.push((/* @__PURE__ */ new Date()).toISOString());
170
- params.push(id);
171
- await this.db.query(
172
- `UPDATE ${this.tableName} SET ${setClause.join(", ")} WHERE id = ?`,
173
- params
174
- );
175
- const job = await this.get(id);
176
- if (!job) throw new Error(`Job not found after update: ${id}`);
177
- if (updates.status === "completed") {
178
- await this.emitEvent("job.completed", job, {
179
- resultPointer: job.resultPointer ?? void 0
180
- });
181
- } else if (updates.status === "failed") {
182
- await this.emitEvent("job.failed", job, {
183
- error: job.lastError ?? void 0
184
- });
185
- } else if (updates.status === "cancelled") {
186
- await this.emitEvent("job.cancelled", job);
187
- }
188
- return job;
189
- }
190
- async get(id) {
191
- if (!this.db) throw new Error("Store not initialized");
192
- const row = await this.db.get(this.tableName, { id });
193
- if (!row) return null;
194
- return this.parseJobRow(row);
195
- }
196
- async list(filter) {
197
- if (!this.db) throw new Error("Store not initialized");
198
- const { where, params: whereParams } = this.buildFilterWhere(filter);
199
- const orderBy = this.buildOrderBy(filter);
200
- const { clause: limitOffset, params: limitParams } = this.buildLimitOffset(filter);
201
- const { rows } = await this.db.query(
202
- `SELECT * FROM ${this.tableName} ${where} ${orderBy} ${limitOffset}`,
203
- [...whereParams, ...limitParams]
204
- );
205
- return rows.map((row) => this.parseJobRow(row));
206
- }
207
- async cancel(id) {
208
- if (!this.db) throw new Error("Store not initialized");
209
- const job = await this.get(id);
210
- if (!job) throw new Error(`Job not found: ${id}`);
211
- if (job.status === "completed" || job.status === "cancelled") {
212
- throw new Error(`Cannot cancel job with status: ${job.status}`);
213
- }
214
- await this.update(id, {
215
- status: "cancelled",
216
- completedAt: /* @__PURE__ */ new Date()
217
- });
218
- }
219
- async cleanup(options) {
220
- if (!this.db) throw new Error("Store not initialized");
221
- const conditions = [];
222
- const params = [];
223
- if (options.completedBefore) {
224
- conditions.push("(status = 'completed' AND completed_at < ?)");
225
- params.push(options.completedBefore.toISOString());
226
- }
227
- if (options.failedBefore) {
228
- conditions.push("(status = 'failed' AND completed_at < ?)");
229
- params.push(options.failedBefore.toISOString());
230
- }
231
- if (options.cancelledBefore) {
232
- conditions.push("(status = 'cancelled' AND completed_at < ?)");
233
- params.push(options.cancelledBefore.toISOString());
234
- }
235
- if (conditions.length === 0) return 0;
236
- let query = `DELETE FROM ${this.tableName} WHERE (${conditions.join(" OR ")})`;
237
- if (options.limit) {
238
- query = `
120
+ `, [
121
+ workerId,
122
+ now,
123
+ now,
124
+ now,
125
+ ...jobIds
126
+ ]);
127
+ const jobs = rows.map((row) => {
128
+ const job = this.parseJobRow(row);
129
+ job.status = "running";
130
+ job.workerId = workerId;
131
+ job.workerHeartbeat = /* @__PURE__ */ new Date();
132
+ job.startedAt = /* @__PURE__ */ new Date();
133
+ job.attempts += 1;
134
+ return job;
135
+ });
136
+ for (const job of jobs) await this.emitEvent("job.started", job);
137
+ return jobs;
138
+ }
139
+ async update(id, updates) {
140
+ if (!this.db) throw new Error("Store not initialized");
141
+ const setClause = [];
142
+ const params = [];
143
+ const fieldMap = {
144
+ queue: "queue",
145
+ payload: "payload",
146
+ status: "status",
147
+ priority: "priority",
148
+ attempts: "attempts",
149
+ maxAttempts: "max_attempts",
150
+ runAt: "run_at",
151
+ startedAt: "started_at",
152
+ completedAt: "completed_at",
153
+ timeout: "timeout",
154
+ timeoutBehavior: "timeout_behavior",
155
+ lastError: "last_error",
156
+ resultPointer: "result_pointer",
157
+ retryStrategy: "retry_strategy",
158
+ workerId: "worker_id",
159
+ workerHeartbeat: "worker_heartbeat"
160
+ };
161
+ for (const [key, value] of Object.entries(updates)) {
162
+ const column = fieldMap[key];
163
+ if (!column) continue;
164
+ setClause.push(`${column} = ?`);
165
+ if (value instanceof Date) params.push(value.toISOString());
166
+ else if (typeof value === "object" && value !== null) params.push(JSON.stringify(value));
167
+ else params.push(value);
168
+ }
169
+ if (setClause.length === 0) {
170
+ const job = await this.get(id);
171
+ if (!job) throw new Error(`Job not found: ${id}`);
172
+ return job;
173
+ }
174
+ setClause.push("updated_at = ?");
175
+ params.push((/* @__PURE__ */ new Date()).toISOString());
176
+ params.push(id);
177
+ await this.db.query(`UPDATE ${this.tableName} SET ${setClause.join(", ")} WHERE id = ?`, params);
178
+ const job = await this.get(id);
179
+ if (!job) throw new Error(`Job not found after update: ${id}`);
180
+ if (updates.status === "completed") await this.emitEvent("job.completed", job, { resultPointer: job.resultPointer ?? void 0 });
181
+ else if (updates.status === "failed") await this.emitEvent("job.failed", job, { error: job.lastError ?? void 0 });
182
+ else if (updates.status === "cancelled") await this.emitEvent("job.cancelled", job);
183
+ return job;
184
+ }
185
+ async get(id) {
186
+ if (!this.db) throw new Error("Store not initialized");
187
+ const row = await this.db.get(this.tableName, { id });
188
+ if (!row) return null;
189
+ return this.parseJobRow(row);
190
+ }
191
+ async list(filter) {
192
+ if (!this.db) throw new Error("Store not initialized");
193
+ const { where, params: whereParams } = this.buildFilterWhere(filter);
194
+ const orderBy = this.buildOrderBy(filter);
195
+ const { clause: limitOffset, params: limitParams } = this.buildLimitOffset(filter);
196
+ const { rows } = await this.db.query(`SELECT * FROM ${this.tableName} ${where} ${orderBy} ${limitOffset}`, [...whereParams, ...limitParams]);
197
+ return rows.map((row) => this.parseJobRow(row));
198
+ }
199
+ async cancel(id) {
200
+ if (!this.db) throw new Error("Store not initialized");
201
+ const job = await this.get(id);
202
+ if (!job) throw new Error(`Job not found: ${id}`);
203
+ if (job.status === "completed" || job.status === "cancelled") throw new Error(`Cannot cancel job with status: ${job.status}`);
204
+ await this.update(id, {
205
+ status: "cancelled",
206
+ completedAt: /* @__PURE__ */ new Date()
207
+ });
208
+ }
209
+ async cleanup(options) {
210
+ if (!this.db) throw new Error("Store not initialized");
211
+ const conditions = [];
212
+ const params = [];
213
+ if (options.completedBefore) {
214
+ conditions.push("(status = 'completed' AND completed_at < ?)");
215
+ params.push(options.completedBefore.toISOString());
216
+ }
217
+ if (options.failedBefore) {
218
+ conditions.push("(status = 'failed' AND completed_at < ?)");
219
+ params.push(options.failedBefore.toISOString());
220
+ }
221
+ if (options.cancelledBefore) {
222
+ conditions.push("(status = 'cancelled' AND completed_at < ?)");
223
+ params.push(options.cancelledBefore.toISOString());
224
+ }
225
+ if (conditions.length === 0) return 0;
226
+ let query = `DELETE FROM ${this.tableName} WHERE (${conditions.join(" OR ")})`;
227
+ if (options.limit) {
228
+ query = `
239
229
  DELETE FROM ${this.tableName}
240
230
  WHERE id IN (
241
231
  SELECT id FROM ${this.tableName}
@@ -243,41 +233,36 @@ class SqliteJobStore extends BaseJobStore {
243
233
  LIMIT ?
244
234
  )
245
235
  `;
246
- params.push(options.limit);
247
- }
248
- const result = await this.db.query(query, params);
249
- return result.rowCount ?? 0;
250
- }
251
- async heartbeat(jobId, workerId) {
252
- if (!this.db) throw new Error("Store not initialized");
253
- await this.db.query(
254
- `
236
+ params.push(options.limit);
237
+ }
238
+ return (await this.db.query(query, params)).rowCount ?? 0;
239
+ }
240
+ async heartbeat(jobId, workerId) {
241
+ if (!this.db) throw new Error("Store not initialized");
242
+ await this.db.query(`
255
243
  UPDATE ${this.tableName}
256
244
  SET worker_heartbeat = ?, updated_at = ?
257
245
  WHERE id = ? AND worker_id = ? AND status = 'running'
258
- `,
259
- [(/* @__PURE__ */ new Date()).toISOString(), (/* @__PURE__ */ new Date()).toISOString(), jobId, workerId]
260
- );
261
- }
262
- async stats(queue) {
263
- if (!this.db) throw new Error("Store not initialized");
264
- const queueFilter = queue ? "WHERE queue = ?" : "";
265
- const params = queue ? [queue] : [];
266
- const { rows: countRows } = await this.db.query(
267
- `
246
+ `, [
247
+ (/* @__PURE__ */ new Date()).toISOString(),
248
+ (/* @__PURE__ */ new Date()).toISOString(),
249
+ jobId,
250
+ workerId
251
+ ]);
252
+ }
253
+ async stats(queue) {
254
+ if (!this.db) throw new Error("Store not initialized");
255
+ const queueFilter = queue ? "WHERE queue = ?" : "";
256
+ const params = queue ? [queue] : [];
257
+ const { rows: countRows } = await this.db.query(`
268
258
  SELECT status, COUNT(*) as count
269
259
  FROM ${this.tableName}
270
260
  ${queueFilter}
271
261
  GROUP BY status
272
- `,
273
- params
274
- );
275
- const counts = {};
276
- for (const row of countRows) {
277
- counts[row.status] = row.count;
278
- }
279
- const { rows: durationRows } = await this.db.query(
280
- `
262
+ `, params);
263
+ const counts = {};
264
+ for (const row of countRows) counts[row.status] = row.count;
265
+ const { rows: durationRows } = await this.db.query(`
281
266
  SELECT AVG(
282
267
  (julianday(completed_at) - julianday(started_at)) * 86400000
283
268
  ) as avg_duration
@@ -286,38 +271,31 @@ class SqliteJobStore extends BaseJobStore {
286
271
  AND started_at IS NOT NULL
287
272
  AND completed_at IS NOT NULL
288
273
  ${queue ? "AND queue = ?" : ""}
289
- `,
290
- params
291
- );
292
- const avgDuration = durationRows[0]?.avg_duration ?? null;
293
- return {
294
- pending: counts["pending"] ?? 0,
295
- running: counts["running"] ?? 0,
296
- completed: counts["completed"] ?? 0,
297
- failed: counts["failed"] ?? 0,
298
- cancelled: counts["cancelled"] ?? 0,
299
- avgDuration: avgDuration ? Math.round(avgDuration) : null
300
- };
301
- }
302
- async close() {
303
- if (this.db && !this.externalDb) {
304
- await this.db.close?.();
305
- }
306
- this.db = null;
307
- this.initialized = false;
308
- }
309
- async waitForUpdate(timeoutMs) {
310
- if (!this.db?.notifications) {
311
- if (timeoutMs && timeoutMs > 0) {
312
- await new Promise((resolve) => setTimeout(resolve, timeoutMs));
313
- }
314
- return false;
315
- }
316
- return this.db.notifications.waitForUpdate({ timeoutMs });
317
- }
318
- }
319
- export {
320
- SqliteJobStore,
321
- SqliteJobStore as default
274
+ `, params);
275
+ const avgDuration = durationRows[0]?.avg_duration ?? null;
276
+ return {
277
+ pending: counts["pending"] ?? 0,
278
+ running: counts["running"] ?? 0,
279
+ completed: counts["completed"] ?? 0,
280
+ failed: counts["failed"] ?? 0,
281
+ cancelled: counts["cancelled"] ?? 0,
282
+ avgDuration: avgDuration ? Math.round(avgDuration) : null
283
+ };
284
+ }
285
+ async close() {
286
+ if (this.db && !this.externalDb) await this.db.close?.();
287
+ this.db = null;
288
+ this.initialized = false;
289
+ }
290
+ async waitForUpdate(timeoutMs) {
291
+ if (!this.db?.notifications) {
292
+ if (timeoutMs && timeoutMs > 0) await new Promise((resolve) => setTimeout(resolve, timeoutMs));
293
+ return false;
294
+ }
295
+ return this.db.notifications.waitForUpdate({ timeoutMs });
296
+ }
322
297
  };
323
- //# sourceMappingURL=sqlite.js.map
298
+ //#endregion
299
+ export { SqliteJobStore, SqliteJobStore as default };
300
+
301
+ //# sourceMappingURL=sqlite.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite.js","sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n type DatabaseInterface,\n getDatabase,\n type SqliteCapabilitiesOptions,\n syncSchema,\n} from '@happyvertical/sql';\nimport { BaseJobStore, validateTableName } from '../base-store.js';\nimport type {\n CleanupOptions,\n Job,\n JobCreateOptions,\n JobFilter,\n QueueStats,\n} from '../types.js';\n\n/**\n * SQLite job store configuration\n */\nexport interface SqliteJobStoreConfig {\n /** Database URL or path (default: ':memory:') */\n url?: string;\n /** Existing database instance to use */\n db?: DatabaseInterface;\n /** Table name for jobs (default: '_jobs') */\n tableName?: string;\n /** Optional SQLite native capabilities for development/test modes */\n capabilities?: SqliteCapabilitiesOptions;\n}\n\n/**\n * SQLite-based job store\n *\n * Uses SQLite for job persistence. Supports polling-based job retrieval.\n * Good for single-instance deployments or development.\n */\nexport class SqliteJobStore extends BaseJobStore {\n private db: DatabaseInterface | null = null;\n private readonly url: string;\n private readonly externalDb: DatabaseInterface | null;\n private readonly tableName: string;\n private readonly capabilities: SqliteCapabilitiesOptions | undefined;\n\n constructor(config: SqliteJobStoreConfig = {}) {\n super();\n this.url = config.url ?? ':memory:';\n this.externalDb = config.db ?? null;\n this.tableName = validateTableName(config.tableName ?? '_jobs');\n this.capabilities = config.capabilities;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Use existing database or create new one\n this.db =\n this.externalDb ??\n (await getDatabase({\n type: 'sqlite',\n url: this.url,\n capabilities: this.capabilities,\n }));\n\n // Create jobs table and indexes using syncSchema\n const schema = `\n CREATE TABLE IF NOT EXISTS \"${this.tableName}\" (\n id TEXT PRIMARY KEY,\n queue TEXT NOT NULL DEFAULT 'default',\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n priority INTEGER NOT NULL DEFAULT 50,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n run_at TEXT NOT NULL,\n started_at TEXT,\n completed_at TEXT,\n timeout INTEGER NOT NULL DEFAULT 300000,\n timeout_behavior TEXT NOT NULL DEFAULT 'fail',\n last_error TEXT,\n result_pointer TEXT,\n retry_strategy TEXT NOT NULL,\n worker_id TEXT,\n worker_heartbeat TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_status_queue\" ON \"${this.tableName}\" (status, queue, run_at, priority DESC);\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_created_at\" ON \"${this.tableName}\" (created_at);\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_queue\" ON \"${this.tableName}\" (queue);\n `;\n\n await syncSchema({ db: this.db, schema });\n\n this.initialized = true;\n }\n\n async enqueue(options: JobCreateOptions): Promise<Job> {\n if (!this.db) throw new Error('Store not initialized');\n\n const job = this.createJobRecord(options);\n\n await this.db.insert(this.tableName, {\n id: job.id,\n queue: job.queue,\n payload: JSON.stringify(job.payload),\n status: job.status,\n priority: job.priority,\n attempts: job.attempts,\n max_attempts: job.maxAttempts,\n run_at: job.runAt.toISOString(),\n started_at: job.startedAt?.toISOString() ?? null,\n completed_at: job.completedAt?.toISOString() ?? null,\n timeout: job.timeout,\n timeout_behavior: job.timeoutBehavior,\n last_error: job.lastError,\n result_pointer: job.resultPointer,\n retry_strategy: JSON.stringify(job.retryStrategy),\n worker_id: job.workerId,\n worker_heartbeat: job.workerHeartbeat?.toISOString() ?? null,\n created_at: job.createdAt.toISOString(),\n updated_at: job.updatedAt.toISOString(),\n });\n\n await this.emitEvent('job.created', job);\n\n // Also emit ready if job should run immediately\n if (job.runAt <= new Date()) {\n await this.emitEvent('job.ready', job);\n }\n\n return job;\n }\n\n async dequeue(\n queues: string[],\n limit: number,\n workerId: string,\n ): Promise<Job[]> {\n if (!this.db) throw new Error('Store not initialized');\n\n const now = new Date().toISOString();\n const queuePlaceholders = queues.map(() => '?').join(', ');\n\n // Find jobs ready to process\n const { rows } = await this.db.query(\n `\n SELECT * FROM ${this.tableName}\n WHERE status = 'pending'\n AND queue IN (${queuePlaceholders})\n AND run_at <= ?\n ORDER BY priority DESC, run_at ASC\n LIMIT ?\n `,\n [...queues, now, limit],\n );\n\n if (!rows.length) return [];\n\n // Claim the jobs by updating their status\n const jobIds = rows.map((r) => r.id as string);\n const idPlaceholders = jobIds.map(() => '?').join(', ');\n\n await this.db.query(\n `\n UPDATE ${this.tableName}\n SET status = 'running',\n worker_id = ?,\n worker_heartbeat = ?,\n started_at = ?,\n attempts = attempts + 1,\n updated_at = ?\n WHERE id IN (${idPlaceholders})\n AND status = 'pending'\n `,\n [workerId, now, now, now, ...jobIds],\n );\n\n // Return the claimed jobs\n const jobs = rows.map((row) => {\n const job = this.parseJobRow(row as Record<string, unknown>);\n job.status = 'running';\n job.workerId = workerId;\n job.workerHeartbeat = new Date();\n job.startedAt = new Date();\n job.attempts += 1;\n return job;\n });\n\n // Emit started events\n for (const job of jobs) {\n await this.emitEvent('job.started', job);\n }\n\n return jobs;\n }\n\n async update(id: string, updates: Partial<Job>): Promise<Job> {\n if (!this.db) throw new Error('Store not initialized');\n\n const setClause: string[] = [];\n const params: unknown[] = [];\n\n // Map Job fields to database columns\n const fieldMap: Record<string, string> = {\n queue: 'queue',\n payload: 'payload',\n status: 'status',\n priority: 'priority',\n attempts: 'attempts',\n maxAttempts: 'max_attempts',\n runAt: 'run_at',\n startedAt: 'started_at',\n completedAt: 'completed_at',\n timeout: 'timeout',\n timeoutBehavior: 'timeout_behavior',\n lastError: 'last_error',\n resultPointer: 'result_pointer',\n retryStrategy: 'retry_strategy',\n workerId: 'worker_id',\n workerHeartbeat: 'worker_heartbeat',\n };\n\n for (const [key, value] of Object.entries(updates)) {\n const column = fieldMap[key];\n if (!column) continue;\n\n setClause.push(`${column} = ?`);\n\n if (value instanceof Date) {\n params.push(value.toISOString());\n } else if (typeof value === 'object' && value !== null) {\n params.push(JSON.stringify(value));\n } else {\n params.push(value);\n }\n }\n\n if (setClause.length === 0) {\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found: ${id}`);\n return job;\n }\n\n // Always update updated_at\n setClause.push('updated_at = ?');\n params.push(new Date().toISOString());\n\n params.push(id);\n\n await this.db.query(\n `UPDATE ${this.tableName} SET ${setClause.join(', ')} WHERE id = ?`,\n params,\n );\n\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found after update: ${id}`);\n\n // Emit events based on status changes\n if (updates.status === 'completed') {\n await this.emitEvent('job.completed', job, {\n resultPointer: job.resultPointer ?? undefined,\n });\n } else if (updates.status === 'failed') {\n await this.emitEvent('job.failed', job, {\n error: job.lastError ?? undefined,\n });\n } else if (updates.status === 'cancelled') {\n await this.emitEvent('job.cancelled', job);\n }\n\n return job;\n }\n\n async get(id: string): Promise<Job | null> {\n if (!this.db) throw new Error('Store not initialized');\n\n const row = await this.db.get(this.tableName, { id });\n if (!row) return null;\n\n return this.parseJobRow(row as Record<string, unknown>);\n }\n\n async list(filter: JobFilter): Promise<Job[]> {\n if (!this.db) throw new Error('Store not initialized');\n\n const { where, params: whereParams } = this.buildFilterWhere(filter);\n const orderBy = this.buildOrderBy(filter);\n const { clause: limitOffset, params: limitParams } =\n this.buildLimitOffset(filter);\n\n const { rows } = await this.db.query(\n `SELECT * FROM ${this.tableName} ${where} ${orderBy} ${limitOffset}`,\n [...whereParams, ...limitParams],\n );\n\n return rows.map((row) => this.parseJobRow(row as Record<string, unknown>));\n }\n\n async cancel(id: string): Promise<void> {\n if (!this.db) throw new Error('Store not initialized');\n\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found: ${id}`);\n\n if (job.status === 'completed' || job.status === 'cancelled') {\n throw new Error(`Cannot cancel job with status: ${job.status}`);\n }\n\n await this.update(id, {\n status: 'cancelled',\n completedAt: new Date(),\n });\n }\n\n async cleanup(options: CleanupOptions): Promise<number> {\n if (!this.db) throw new Error('Store not initialized');\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.completedBefore) {\n conditions.push(\"(status = 'completed' AND completed_at < ?)\");\n params.push(options.completedBefore.toISOString());\n }\n\n if (options.failedBefore) {\n conditions.push(\"(status = 'failed' AND completed_at < ?)\");\n params.push(options.failedBefore.toISOString());\n }\n\n if (options.cancelledBefore) {\n conditions.push(\"(status = 'cancelled' AND completed_at < ?)\");\n params.push(options.cancelledBefore.toISOString());\n }\n\n if (conditions.length === 0) return 0;\n\n let query = `DELETE FROM ${this.tableName} WHERE (${conditions.join(' OR ')})`;\n\n if (options.limit) {\n // SQLite doesn't support LIMIT in DELETE directly, so we need a subquery\n query = `\n DELETE FROM ${this.tableName}\n WHERE id IN (\n SELECT id FROM ${this.tableName}\n WHERE (${conditions.join(' OR ')})\n LIMIT ?\n )\n `;\n params.push(options.limit);\n }\n\n const result = await this.db.query(query, params);\n\n return result.rowCount ?? 0;\n }\n\n async heartbeat(jobId: string, workerId: string): Promise<void> {\n if (!this.db) throw new Error('Store not initialized');\n\n await this.db.query(\n `\n UPDATE ${this.tableName}\n SET worker_heartbeat = ?, updated_at = ?\n WHERE id = ? AND worker_id = ? AND status = 'running'\n `,\n [new Date().toISOString(), new Date().toISOString(), jobId, workerId],\n );\n }\n\n async stats(queue?: string): Promise<QueueStats> {\n if (!this.db) throw new Error('Store not initialized');\n\n const queueFilter = queue ? 'WHERE queue = ?' : '';\n const params = queue ? [queue] : [];\n\n // Get status counts\n const { rows: countRows } = await this.db.query(\n `\n SELECT status, COUNT(*) as count\n FROM ${this.tableName}\n ${queueFilter}\n GROUP BY status\n `,\n params,\n );\n\n const counts: Record<string, number> = {};\n for (const row of countRows) {\n counts[row.status as string] = row.count as number;\n }\n\n // Get average duration for completed jobs\n const { rows: durationRows } = await this.db.query(\n `\n SELECT AVG(\n (julianday(completed_at) - julianday(started_at)) * 86400000\n ) as avg_duration\n FROM ${this.tableName}\n WHERE status = 'completed'\n AND started_at IS NOT NULL\n AND completed_at IS NOT NULL\n ${queue ? 'AND queue = ?' : ''}\n `,\n params,\n );\n\n const avgDuration =\n (durationRows[0] as { avg_duration: number | null })?.avg_duration ??\n null;\n\n return {\n pending: counts['pending'] ?? 0,\n running: counts['running'] ?? 0,\n completed: counts['completed'] ?? 0,\n failed: counts['failed'] ?? 0,\n cancelled: counts['cancelled'] ?? 0,\n avgDuration: avgDuration ? Math.round(avgDuration) : null,\n };\n }\n\n async close(): Promise<void> {\n if (this.db && !this.externalDb) {\n await this.db.close?.();\n }\n this.db = null;\n this.initialized = false;\n }\n\n async waitForUpdate(timeoutMs?: number): Promise<boolean> {\n if (!this.db?.notifications) {\n if (timeoutMs && timeoutMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, timeoutMs));\n }\n return false;\n }\n\n return this.db.notifications.waitForUpdate({ timeoutMs });\n }\n}\n\nexport default SqliteJobStore;\n"],"names":["job"],"mappings":"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"}
1
+ {"version":3,"file":"sqlite.js","names":[],"sources":["../../src/adapters/sqlite.ts"],"sourcesContent":["import {\n type DatabaseInterface,\n getDatabase,\n type SqliteCapabilitiesOptions,\n syncSchema,\n} from '@happyvertical/sql';\nimport { BaseJobStore, validateTableName } from '../base-store.js';\nimport type {\n CleanupOptions,\n Job,\n JobCreateOptions,\n JobFilter,\n QueueStats,\n} from '../types.js';\n\n/**\n * SQLite job store configuration\n */\nexport interface SqliteJobStoreConfig {\n /** Database URL or path (default: ':memory:') */\n url?: string;\n /** Existing database instance to use */\n db?: DatabaseInterface;\n /** Table name for jobs (default: '_jobs') */\n tableName?: string;\n /** Optional SQLite native capabilities for development/test modes */\n capabilities?: SqliteCapabilitiesOptions;\n}\n\n/**\n * SQLite-based job store\n *\n * Uses SQLite for job persistence. Supports polling-based job retrieval.\n * Good for single-instance deployments or development.\n */\nexport class SqliteJobStore extends BaseJobStore {\n private db: DatabaseInterface | null = null;\n private readonly url: string;\n private readonly externalDb: DatabaseInterface | null;\n private readonly tableName: string;\n private readonly capabilities: SqliteCapabilitiesOptions | undefined;\n\n constructor(config: SqliteJobStoreConfig = {}) {\n super();\n this.url = config.url ?? ':memory:';\n this.externalDb = config.db ?? null;\n this.tableName = validateTableName(config.tableName ?? '_jobs');\n this.capabilities = config.capabilities;\n }\n\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // Use existing database or create new one\n this.db =\n this.externalDb ??\n (await getDatabase({\n type: 'sqlite',\n url: this.url,\n capabilities: this.capabilities,\n }));\n\n // Create jobs table and indexes using syncSchema\n const schema = `\n CREATE TABLE IF NOT EXISTS \"${this.tableName}\" (\n id TEXT PRIMARY KEY,\n queue TEXT NOT NULL DEFAULT 'default',\n payload TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'pending',\n priority INTEGER NOT NULL DEFAULT 50,\n attempts INTEGER NOT NULL DEFAULT 0,\n max_attempts INTEGER NOT NULL DEFAULT 3,\n run_at TEXT NOT NULL,\n started_at TEXT,\n completed_at TEXT,\n timeout INTEGER NOT NULL DEFAULT 300000,\n timeout_behavior TEXT NOT NULL DEFAULT 'fail',\n last_error TEXT,\n result_pointer TEXT,\n retry_strategy TEXT NOT NULL,\n worker_id TEXT,\n worker_heartbeat TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_status_queue\" ON \"${this.tableName}\" (status, queue, run_at, priority DESC);\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_created_at\" ON \"${this.tableName}\" (created_at);\n CREATE INDEX IF NOT EXISTS \"idx_${this.tableName}_queue\" ON \"${this.tableName}\" (queue);\n `;\n\n await syncSchema({ db: this.db, schema });\n\n this.initialized = true;\n }\n\n async enqueue(options: JobCreateOptions): Promise<Job> {\n if (!this.db) throw new Error('Store not initialized');\n\n const job = this.createJobRecord(options);\n\n await this.db.insert(this.tableName, {\n id: job.id,\n queue: job.queue,\n payload: JSON.stringify(job.payload),\n status: job.status,\n priority: job.priority,\n attempts: job.attempts,\n max_attempts: job.maxAttempts,\n run_at: job.runAt.toISOString(),\n started_at: job.startedAt?.toISOString() ?? null,\n completed_at: job.completedAt?.toISOString() ?? null,\n timeout: job.timeout,\n timeout_behavior: job.timeoutBehavior,\n last_error: job.lastError,\n result_pointer: job.resultPointer,\n retry_strategy: JSON.stringify(job.retryStrategy),\n worker_id: job.workerId,\n worker_heartbeat: job.workerHeartbeat?.toISOString() ?? null,\n created_at: job.createdAt.toISOString(),\n updated_at: job.updatedAt.toISOString(),\n });\n\n await this.emitEvent('job.created', job);\n\n // Also emit ready if job should run immediately\n if (job.runAt <= new Date()) {\n await this.emitEvent('job.ready', job);\n }\n\n return job;\n }\n\n async dequeue(\n queues: string[],\n limit: number,\n workerId: string,\n ): Promise<Job[]> {\n if (!this.db) throw new Error('Store not initialized');\n\n const now = new Date().toISOString();\n const queuePlaceholders = queues.map(() => '?').join(', ');\n\n // Find jobs ready to process\n const { rows } = await this.db.query(\n `\n SELECT * FROM ${this.tableName}\n WHERE status = 'pending'\n AND queue IN (${queuePlaceholders})\n AND run_at <= ?\n ORDER BY priority DESC, run_at ASC\n LIMIT ?\n `,\n [...queues, now, limit],\n );\n\n if (!rows.length) return [];\n\n // Claim the jobs by updating their status\n const jobIds = rows.map((r) => r.id as string);\n const idPlaceholders = jobIds.map(() => '?').join(', ');\n\n await this.db.query(\n `\n UPDATE ${this.tableName}\n SET status = 'running',\n worker_id = ?,\n worker_heartbeat = ?,\n started_at = ?,\n attempts = attempts + 1,\n updated_at = ?\n WHERE id IN (${idPlaceholders})\n AND status = 'pending'\n `,\n [workerId, now, now, now, ...jobIds],\n );\n\n // Return the claimed jobs\n const jobs = rows.map((row) => {\n const job = this.parseJobRow(row as Record<string, unknown>);\n job.status = 'running';\n job.workerId = workerId;\n job.workerHeartbeat = new Date();\n job.startedAt = new Date();\n job.attempts += 1;\n return job;\n });\n\n // Emit started events\n for (const job of jobs) {\n await this.emitEvent('job.started', job);\n }\n\n return jobs;\n }\n\n async update(id: string, updates: Partial<Job>): Promise<Job> {\n if (!this.db) throw new Error('Store not initialized');\n\n const setClause: string[] = [];\n const params: unknown[] = [];\n\n // Map Job fields to database columns\n const fieldMap: Record<string, string> = {\n queue: 'queue',\n payload: 'payload',\n status: 'status',\n priority: 'priority',\n attempts: 'attempts',\n maxAttempts: 'max_attempts',\n runAt: 'run_at',\n startedAt: 'started_at',\n completedAt: 'completed_at',\n timeout: 'timeout',\n timeoutBehavior: 'timeout_behavior',\n lastError: 'last_error',\n resultPointer: 'result_pointer',\n retryStrategy: 'retry_strategy',\n workerId: 'worker_id',\n workerHeartbeat: 'worker_heartbeat',\n };\n\n for (const [key, value] of Object.entries(updates)) {\n const column = fieldMap[key];\n if (!column) continue;\n\n setClause.push(`${column} = ?`);\n\n if (value instanceof Date) {\n params.push(value.toISOString());\n } else if (typeof value === 'object' && value !== null) {\n params.push(JSON.stringify(value));\n } else {\n params.push(value);\n }\n }\n\n if (setClause.length === 0) {\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found: ${id}`);\n return job;\n }\n\n // Always update updated_at\n setClause.push('updated_at = ?');\n params.push(new Date().toISOString());\n\n params.push(id);\n\n await this.db.query(\n `UPDATE ${this.tableName} SET ${setClause.join(', ')} WHERE id = ?`,\n params,\n );\n\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found after update: ${id}`);\n\n // Emit events based on status changes\n if (updates.status === 'completed') {\n await this.emitEvent('job.completed', job, {\n resultPointer: job.resultPointer ?? undefined,\n });\n } else if (updates.status === 'failed') {\n await this.emitEvent('job.failed', job, {\n error: job.lastError ?? undefined,\n });\n } else if (updates.status === 'cancelled') {\n await this.emitEvent('job.cancelled', job);\n }\n\n return job;\n }\n\n async get(id: string): Promise<Job | null> {\n if (!this.db) throw new Error('Store not initialized');\n\n const row = await this.db.get(this.tableName, { id });\n if (!row) return null;\n\n return this.parseJobRow(row as Record<string, unknown>);\n }\n\n async list(filter: JobFilter): Promise<Job[]> {\n if (!this.db) throw new Error('Store not initialized');\n\n const { where, params: whereParams } = this.buildFilterWhere(filter);\n const orderBy = this.buildOrderBy(filter);\n const { clause: limitOffset, params: limitParams } =\n this.buildLimitOffset(filter);\n\n const { rows } = await this.db.query(\n `SELECT * FROM ${this.tableName} ${where} ${orderBy} ${limitOffset}`,\n [...whereParams, ...limitParams],\n );\n\n return rows.map((row) => this.parseJobRow(row as Record<string, unknown>));\n }\n\n async cancel(id: string): Promise<void> {\n if (!this.db) throw new Error('Store not initialized');\n\n const job = await this.get(id);\n if (!job) throw new Error(`Job not found: ${id}`);\n\n if (job.status === 'completed' || job.status === 'cancelled') {\n throw new Error(`Cannot cancel job with status: ${job.status}`);\n }\n\n await this.update(id, {\n status: 'cancelled',\n completedAt: new Date(),\n });\n }\n\n async cleanup(options: CleanupOptions): Promise<number> {\n if (!this.db) throw new Error('Store not initialized');\n\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (options.completedBefore) {\n conditions.push(\"(status = 'completed' AND completed_at < ?)\");\n params.push(options.completedBefore.toISOString());\n }\n\n if (options.failedBefore) {\n conditions.push(\"(status = 'failed' AND completed_at < ?)\");\n params.push(options.failedBefore.toISOString());\n }\n\n if (options.cancelledBefore) {\n conditions.push(\"(status = 'cancelled' AND completed_at < ?)\");\n params.push(options.cancelledBefore.toISOString());\n }\n\n if (conditions.length === 0) return 0;\n\n let query = `DELETE FROM ${this.tableName} WHERE (${conditions.join(' OR ')})`;\n\n if (options.limit) {\n // SQLite doesn't support LIMIT in DELETE directly, so we need a subquery\n query = `\n DELETE FROM ${this.tableName}\n WHERE id IN (\n SELECT id FROM ${this.tableName}\n WHERE (${conditions.join(' OR ')})\n LIMIT ?\n )\n `;\n params.push(options.limit);\n }\n\n const result = await this.db.query(query, params);\n\n return result.rowCount ?? 0;\n }\n\n async heartbeat(jobId: string, workerId: string): Promise<void> {\n if (!this.db) throw new Error('Store not initialized');\n\n await this.db.query(\n `\n UPDATE ${this.tableName}\n SET worker_heartbeat = ?, updated_at = ?\n WHERE id = ? AND worker_id = ? AND status = 'running'\n `,\n [new Date().toISOString(), new Date().toISOString(), jobId, workerId],\n );\n }\n\n async stats(queue?: string): Promise<QueueStats> {\n if (!this.db) throw new Error('Store not initialized');\n\n const queueFilter = queue ? 'WHERE queue = ?' : '';\n const params = queue ? [queue] : [];\n\n // Get status counts\n const { rows: countRows } = await this.db.query(\n `\n SELECT status, COUNT(*) as count\n FROM ${this.tableName}\n ${queueFilter}\n GROUP BY status\n `,\n params,\n );\n\n const counts: Record<string, number> = {};\n for (const row of countRows) {\n counts[row.status as string] = row.count as number;\n }\n\n // Get average duration for completed jobs\n const { rows: durationRows } = await this.db.query(\n `\n SELECT AVG(\n (julianday(completed_at) - julianday(started_at)) * 86400000\n ) as avg_duration\n FROM ${this.tableName}\n WHERE status = 'completed'\n AND started_at IS NOT NULL\n AND completed_at IS NOT NULL\n ${queue ? 'AND queue = ?' : ''}\n `,\n params,\n );\n\n const avgDuration =\n (durationRows[0] as { avg_duration: number | null })?.avg_duration ??\n null;\n\n return {\n pending: counts['pending'] ?? 0,\n running: counts['running'] ?? 0,\n completed: counts['completed'] ?? 0,\n failed: counts['failed'] ?? 0,\n cancelled: counts['cancelled'] ?? 0,\n avgDuration: avgDuration ? Math.round(avgDuration) : null,\n };\n }\n\n async close(): Promise<void> {\n if (this.db && !this.externalDb) {\n await this.db.close?.();\n }\n this.db = null;\n this.initialized = false;\n }\n\n async waitForUpdate(timeoutMs?: number): Promise<boolean> {\n if (!this.db?.notifications) {\n if (timeoutMs && timeoutMs > 0) {\n await new Promise((resolve) => setTimeout(resolve, timeoutMs));\n }\n return false;\n }\n\n return this.db.notifications.waitForUpdate({ timeoutMs });\n }\n}\n\nexport default SqliteJobStore;\n"],"mappings":"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"}