@holo-js/queue-db 0.1.4 → 0.1.5
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 +10 -4
- package/dist/index.mjs +58 -59
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ type StoredFailedQueueJobRow = {
|
|
|
20
20
|
exception: unknown;
|
|
21
21
|
failed_at: unknown;
|
|
22
22
|
};
|
|
23
|
-
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
24
23
|
declare function assertQueueJsonValue(value: unknown, path: string, seen?: Set<unknown>): asserts value is QueueJsonValue;
|
|
25
24
|
declare function normalizeIdentifierPath(value: string, label: string): string;
|
|
26
25
|
declare function quoteIdentifierPath(dialect: Dialect, path: string): string;
|
|
@@ -34,7 +33,7 @@ declare function parseStoredQueueEnvelope(value: unknown): QueueJobEnvelope<Queu
|
|
|
34
33
|
declare function parseStoredFailedQueueJobRow(row: StoredFailedQueueJobRow): QueueFailedJobRecord;
|
|
35
34
|
declare function serializeQueueJson(value: unknown): string;
|
|
36
35
|
declare function ensureConnectionReady(connection: DatabaseContext): Promise<DatabaseContext>;
|
|
37
|
-
|
|
36
|
+
declare function resolveDatabaseConnection(name: string): DatabaseContext;
|
|
38
37
|
declare const queueDatabaseInternals: {
|
|
39
38
|
assertQueueJsonValue: typeof assertQueueJsonValue;
|
|
40
39
|
coerceOptionalInteger: typeof coerceOptionalInteger;
|
|
@@ -42,13 +41,13 @@ declare const queueDatabaseInternals: {
|
|
|
42
41
|
coerceRequiredString: typeof coerceRequiredString;
|
|
43
42
|
createPlaceholderList: typeof createPlaceholderList;
|
|
44
43
|
ensureConnectionReady: typeof ensureConnectionReady;
|
|
45
|
-
isPlainObject: typeof isPlainObject;
|
|
46
44
|
normalizeIdentifierPath: typeof normalizeIdentifierPath;
|
|
47
45
|
parseStoredFailedQueueJobRow: typeof parseStoredFailedQueueJobRow;
|
|
48
|
-
parseStoredQueueEnvelope: typeof parseStoredQueueEnvelope;
|
|
49
46
|
parseStoredPayload: typeof parseStoredPayload;
|
|
47
|
+
parseStoredQueueEnvelope: typeof parseStoredQueueEnvelope;
|
|
50
48
|
parseStoredQueueJobRow: typeof parseStoredQueueJobRow;
|
|
51
49
|
quoteIdentifierPath: typeof quoteIdentifierPath;
|
|
50
|
+
resolveDatabaseConnection: typeof resolveDatabaseConnection;
|
|
52
51
|
serializeQueueJson: typeof serializeQueueJson;
|
|
53
52
|
};
|
|
54
53
|
|
|
@@ -58,6 +57,7 @@ declare class DatabaseQueueDriverError extends Error {
|
|
|
58
57
|
}
|
|
59
58
|
declare function wrapDatabaseError(connectionName: string, action: string, error: unknown): DatabaseQueueDriverError;
|
|
60
59
|
declare function normalizeQueueNames(queueNames: readonly string[] | undefined, fallbackQueueName: string): readonly string[];
|
|
60
|
+
declare function createPlaceholders(connection: DatabaseContext, count: number, startIndex?: number): readonly string[];
|
|
61
61
|
declare class DatabaseQueueDriver implements QueueAsyncDriver {
|
|
62
62
|
private readonly connection;
|
|
63
63
|
private readonly context;
|
|
@@ -85,6 +85,7 @@ declare class DatabaseQueueDriver implements QueueAsyncDriver {
|
|
|
85
85
|
}
|
|
86
86
|
declare const databaseQueueDriverFactory: QueueDriverFactory<NormalizedQueueDatabaseConnectionConfig>;
|
|
87
87
|
declare const databaseQueueDriverInternals: {
|
|
88
|
+
createPlaceholders: typeof createPlaceholders;
|
|
88
89
|
normalizeDatabaseErrorMessage: typeof normalizeDatabaseErrorMessage;
|
|
89
90
|
normalizeQueueNames: typeof normalizeQueueNames;
|
|
90
91
|
wrapDatabaseError: typeof wrapDatabaseError;
|
|
@@ -95,10 +96,15 @@ declare function getFailedStoreConnection(): Promise<{
|
|
|
95
96
|
connection: DatabaseContext;
|
|
96
97
|
tableName: string;
|
|
97
98
|
} | null>;
|
|
99
|
+
declare function loadFailedJobs(failedStore: {
|
|
100
|
+
connection: DatabaseContext;
|
|
101
|
+
tableName: string;
|
|
102
|
+
}, id?: string): Promise<readonly QueueFailedJobRecord[]>;
|
|
98
103
|
declare const queueDbFailedJobStore: QueueFailedJobStore;
|
|
99
104
|
declare const queueDbFailedStoreInternals: {
|
|
100
105
|
getFailedStoreConfig: typeof getFailedStoreConfig;
|
|
101
106
|
getFailedStoreConnection: typeof getFailedStoreConnection;
|
|
107
|
+
loadFailedJobs: typeof loadFailedJobs;
|
|
102
108
|
};
|
|
103
109
|
|
|
104
110
|
declare function createQueueDbRuntimeOptions(): Readonly<{
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/database.ts
|
|
2
|
+
import { connectionAsyncContext, DB } from "@holo-js/db";
|
|
2
3
|
function isPlainObject(value) {
|
|
3
4
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
4
5
|
return false;
|
|
@@ -93,16 +94,16 @@ function parseStoredPayload(value, label) {
|
|
|
93
94
|
return parsed;
|
|
94
95
|
}
|
|
95
96
|
function parseStoredQueueJobRow(row) {
|
|
96
|
-
return
|
|
97
|
-
id:
|
|
98
|
-
name:
|
|
99
|
-
connection:
|
|
100
|
-
queue:
|
|
97
|
+
return parseStoredQueueEnvelope({
|
|
98
|
+
id: row.id,
|
|
99
|
+
name: row.job,
|
|
100
|
+
connection: row.connection,
|
|
101
|
+
queue: row.queue,
|
|
101
102
|
payload: parseStoredPayload(row.payload, "Stored queue job payload"),
|
|
102
|
-
attempts:
|
|
103
|
-
maxAttempts:
|
|
104
|
-
|
|
105
|
-
createdAt:
|
|
103
|
+
attempts: row.attempts,
|
|
104
|
+
maxAttempts: row.max_attempts,
|
|
105
|
+
availableAt: row.available_at,
|
|
106
|
+
createdAt: row.created_at
|
|
106
107
|
});
|
|
107
108
|
}
|
|
108
109
|
function parseStoredQueueEnvelope(value) {
|
|
@@ -142,6 +143,13 @@ async function ensureConnectionReady(connection) {
|
|
|
142
143
|
await connection.initialize();
|
|
143
144
|
return connection;
|
|
144
145
|
}
|
|
146
|
+
function resolveDatabaseConnection(name) {
|
|
147
|
+
const active = connectionAsyncContext.getActive()?.connection;
|
|
148
|
+
if (active && active.getConnectionName() === name) {
|
|
149
|
+
return active;
|
|
150
|
+
}
|
|
151
|
+
return DB.connection(name);
|
|
152
|
+
}
|
|
145
153
|
var queueDatabaseInternals = {
|
|
146
154
|
assertQueueJsonValue,
|
|
147
155
|
coerceOptionalInteger,
|
|
@@ -149,20 +157,18 @@ var queueDatabaseInternals = {
|
|
|
149
157
|
coerceRequiredString,
|
|
150
158
|
createPlaceholderList,
|
|
151
159
|
ensureConnectionReady,
|
|
152
|
-
isPlainObject,
|
|
153
160
|
normalizeIdentifierPath,
|
|
154
161
|
parseStoredFailedQueueJobRow,
|
|
155
|
-
parseStoredQueueEnvelope,
|
|
156
162
|
parseStoredPayload,
|
|
163
|
+
parseStoredQueueEnvelope,
|
|
157
164
|
parseStoredQueueJobRow,
|
|
158
165
|
quoteIdentifierPath,
|
|
166
|
+
resolveDatabaseConnection,
|
|
159
167
|
serializeQueueJson
|
|
160
168
|
};
|
|
161
169
|
|
|
162
170
|
// src/drivers/database.ts
|
|
163
171
|
import { randomUUID } from "crypto";
|
|
164
|
-
import { DB } from "@holo-js/db";
|
|
165
|
-
import { connectionAsyncContext } from "@holo-js/db";
|
|
166
172
|
function normalizeDatabaseErrorMessage(error) {
|
|
167
173
|
if (error instanceof Error) {
|
|
168
174
|
return error.message;
|
|
@@ -192,31 +198,24 @@ function normalizeQueueNames(queueNames, fallbackQueueName) {
|
|
|
192
198
|
return Object.freeze(normalized.length > 0 ? normalized : [fallbackQueueName]);
|
|
193
199
|
}
|
|
194
200
|
function createPlaceholders(connection, count, startIndex = 1) {
|
|
195
|
-
return
|
|
196
|
-
}
|
|
197
|
-
function resolveDatabaseConnection(name) {
|
|
198
|
-
const active = connectionAsyncContext.getActive()?.connection;
|
|
199
|
-
if (active && active.getConnectionName() === name) {
|
|
200
|
-
return active;
|
|
201
|
-
}
|
|
202
|
-
return DB.connection(name);
|
|
201
|
+
return createPlaceholderList(connection.getDialect(), count, startIndex).split(", ");
|
|
203
202
|
}
|
|
204
203
|
var DatabaseQueueDriver = class {
|
|
205
204
|
constructor(connection, context) {
|
|
206
205
|
this.connection = connection;
|
|
207
206
|
this.context = context;
|
|
208
207
|
this.name = connection.name;
|
|
209
|
-
this.tableName =
|
|
208
|
+
this.tableName = normalizeIdentifierPath(connection.table, "Queue table name");
|
|
210
209
|
}
|
|
211
210
|
name;
|
|
212
211
|
driver = "database";
|
|
213
212
|
mode = "async";
|
|
214
213
|
tableName;
|
|
215
214
|
async getConnection() {
|
|
216
|
-
return
|
|
215
|
+
return ensureConnectionReady(resolveDatabaseConnection(this.connection.connection));
|
|
217
216
|
}
|
|
218
217
|
getQuotedTable(connection) {
|
|
219
|
-
return
|
|
218
|
+
return quoteIdentifierPath(connection.getDialect(), this.tableName);
|
|
220
219
|
}
|
|
221
220
|
getExpiredReservationCutoff(now) {
|
|
222
221
|
return now - this.connection.retryAfter * 1e3;
|
|
@@ -225,7 +224,7 @@ var DatabaseQueueDriver = class {
|
|
|
225
224
|
return Object.freeze({
|
|
226
225
|
reservationId,
|
|
227
226
|
reservedAt,
|
|
228
|
-
envelope:
|
|
227
|
+
envelope: parseStoredQueueJobRow(row)
|
|
229
228
|
});
|
|
230
229
|
}
|
|
231
230
|
async dispatch(job) {
|
|
@@ -240,7 +239,7 @@ var DatabaseQueueDriver = class {
|
|
|
240
239
|
job.name,
|
|
241
240
|
job.connection,
|
|
242
241
|
job.queue,
|
|
243
|
-
|
|
242
|
+
serializeQueueJson(job.payload),
|
|
244
243
|
job.attempts,
|
|
245
244
|
job.maxAttempts,
|
|
246
245
|
job.availableAt ?? job.createdAt,
|
|
@@ -277,7 +276,7 @@ var DatabaseQueueDriver = class {
|
|
|
277
276
|
break;
|
|
278
277
|
}
|
|
279
278
|
const reservationId = `${input.workerId}:${randomUUID()}`;
|
|
280
|
-
const nextAttempts =
|
|
279
|
+
const nextAttempts = coerceRequiredInteger(row.attempts, "Stored queue job attempts") + 1;
|
|
281
280
|
const [
|
|
282
281
|
reservedAtPlaceholder,
|
|
283
282
|
reservationPlaceholder,
|
|
@@ -378,6 +377,7 @@ var databaseQueueDriverFactory = {
|
|
|
378
377
|
}
|
|
379
378
|
};
|
|
380
379
|
var databaseQueueDriverInternals = {
|
|
380
|
+
createPlaceholders,
|
|
381
381
|
normalizeDatabaseErrorMessage,
|
|
382
382
|
normalizeQueueNames,
|
|
383
383
|
wrapDatabaseError
|
|
@@ -385,39 +385,49 @@ var databaseQueueDriverInternals = {
|
|
|
385
385
|
|
|
386
386
|
// src/failed.ts
|
|
387
387
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
388
|
-
import { DB as DB2 } from "@holo-js/db";
|
|
389
|
-
import { connectionAsyncContext as connectionAsyncContext2 } from "@holo-js/db";
|
|
390
388
|
import { getQueueRuntime } from "@holo-js/queue";
|
|
391
389
|
function getFailedStoreConfig() {
|
|
392
390
|
return getQueueRuntime().config.failed;
|
|
393
391
|
}
|
|
394
|
-
function resolveDatabaseConnection2(name) {
|
|
395
|
-
const active = connectionAsyncContext2.getActive()?.connection;
|
|
396
|
-
if (active && active.getConnectionName() === name) {
|
|
397
|
-
return active;
|
|
398
|
-
}
|
|
399
|
-
return DB2.connection(name);
|
|
400
|
-
}
|
|
401
392
|
async function getFailedStoreConnection() {
|
|
402
393
|
const config = getFailedStoreConfig();
|
|
403
394
|
if (config === false) {
|
|
404
395
|
return null;
|
|
405
396
|
}
|
|
406
|
-
const tableName =
|
|
407
|
-
const connection = await
|
|
397
|
+
const tableName = normalizeIdentifierPath(config.table, "Failed jobs table name");
|
|
398
|
+
const connection = await ensureConnectionReady(resolveDatabaseConnection(config.connection));
|
|
408
399
|
return {
|
|
409
400
|
connection,
|
|
410
401
|
tableName
|
|
411
402
|
};
|
|
412
403
|
}
|
|
404
|
+
async function loadFailedJobs(failedStore, id) {
|
|
405
|
+
const quotedTable = quoteIdentifierPath(failedStore.connection.getDialect(), failedStore.tableName);
|
|
406
|
+
const dialect = failedStore.connection.getDialect();
|
|
407
|
+
const filter = typeof id === "string" ? {
|
|
408
|
+
clause: ` WHERE id = ${dialect.createPlaceholder(1)}`,
|
|
409
|
+
bindings: [id],
|
|
410
|
+
source: "queue:failed:load"
|
|
411
|
+
} : {
|
|
412
|
+
clause: "",
|
|
413
|
+
bindings: void 0,
|
|
414
|
+
source: "queue:failed:list"
|
|
415
|
+
};
|
|
416
|
+
const result = await failedStore.connection.queryCompiled({
|
|
417
|
+
sql: `SELECT id, job_id, payload, exception, failed_at FROM ${quotedTable}${filter.clause} ORDER BY failed_at DESC, id DESC`,
|
|
418
|
+
bindings: filter.bindings,
|
|
419
|
+
source: filter.source
|
|
420
|
+
});
|
|
421
|
+
return Object.freeze(result.rows.map((row) => parseStoredFailedQueueJobRow(row)));
|
|
422
|
+
}
|
|
413
423
|
var queueDbFailedJobStore = {
|
|
414
424
|
async persistFailedJob(reserved, error) {
|
|
415
425
|
const failedStore = await getFailedStoreConnection();
|
|
416
426
|
if (!failedStore) {
|
|
417
427
|
return null;
|
|
418
428
|
}
|
|
419
|
-
const quotedTable =
|
|
420
|
-
const placeholders =
|
|
429
|
+
const quotedTable = quoteIdentifierPath(failedStore.connection.getDialect(), failedStore.tableName);
|
|
430
|
+
const placeholders = createPlaceholderList(failedStore.connection.getDialect(), 7);
|
|
421
431
|
const record = Object.freeze({
|
|
422
432
|
id: randomUUID2(),
|
|
423
433
|
jobId: reserved.envelope.id,
|
|
@@ -433,7 +443,7 @@ var queueDbFailedJobStore = {
|
|
|
433
443
|
reserved.envelope.name,
|
|
434
444
|
reserved.envelope.connection,
|
|
435
445
|
reserved.envelope.queue,
|
|
436
|
-
|
|
446
|
+
serializeQueueJson(reserved.envelope),
|
|
437
447
|
record.exception,
|
|
438
448
|
record.failedAt
|
|
439
449
|
],
|
|
@@ -446,28 +456,16 @@ var queueDbFailedJobStore = {
|
|
|
446
456
|
if (!failedStore) {
|
|
447
457
|
return Object.freeze([]);
|
|
448
458
|
}
|
|
449
|
-
|
|
450
|
-
const result = await failedStore.connection.queryCompiled({
|
|
451
|
-
sql: `SELECT id, job_id, payload, exception, failed_at FROM ${quotedTable} ORDER BY failed_at DESC, id DESC`,
|
|
452
|
-
source: "queue:failed:list"
|
|
453
|
-
});
|
|
454
|
-
return Object.freeze(result.rows.map((row) => queueDatabaseInternals.parseStoredFailedQueueJobRow(row)));
|
|
459
|
+
return loadFailedJobs(failedStore);
|
|
455
460
|
},
|
|
456
461
|
async retryFailedJobs(identifier, retry) {
|
|
457
462
|
const failedStore = await getFailedStoreConnection();
|
|
458
463
|
if (!failedStore) {
|
|
459
464
|
return 0;
|
|
460
465
|
}
|
|
461
|
-
const quotedTable =
|
|
466
|
+
const quotedTable = quoteIdentifierPath(failedStore.connection.getDialect(), failedStore.tableName);
|
|
462
467
|
const dialect = failedStore.connection.getDialect();
|
|
463
|
-
const records = await (identifier === "all" ?
|
|
464
|
-
const placeholder = dialect.createPlaceholder(1);
|
|
465
|
-
return failedStore.connection.queryCompiled({
|
|
466
|
-
sql: `SELECT id, job_id, payload, exception, failed_at FROM ${quotedTable} WHERE id = ${placeholder}`,
|
|
467
|
-
bindings: [identifier],
|
|
468
|
-
source: "queue:failed:load"
|
|
469
|
-
}).then((result) => Object.freeze(result.rows.map((row) => queueDatabaseInternals.parseStoredFailedQueueJobRow(row))));
|
|
470
|
-
})());
|
|
468
|
+
const records = await loadFailedJobs(failedStore, identifier === "all" ? void 0 : identifier);
|
|
471
469
|
let retried = 0;
|
|
472
470
|
for (const record of records) {
|
|
473
471
|
await retry(record);
|
|
@@ -486,7 +484,7 @@ var queueDbFailedJobStore = {
|
|
|
486
484
|
if (!failedStore) {
|
|
487
485
|
return false;
|
|
488
486
|
}
|
|
489
|
-
const quotedTable =
|
|
487
|
+
const quotedTable = quoteIdentifierPath(failedStore.connection.getDialect(), failedStore.tableName);
|
|
490
488
|
const placeholder = failedStore.connection.getDialect().createPlaceholder(1);
|
|
491
489
|
const result = await failedStore.connection.executeCompiled({
|
|
492
490
|
sql: `DELETE FROM ${quotedTable} WHERE id = ${placeholder}`,
|
|
@@ -500,7 +498,7 @@ var queueDbFailedJobStore = {
|
|
|
500
498
|
if (!failedStore) {
|
|
501
499
|
return 0;
|
|
502
500
|
}
|
|
503
|
-
const quotedTable =
|
|
501
|
+
const quotedTable = quoteIdentifierPath(failedStore.connection.getDialect(), failedStore.tableName);
|
|
504
502
|
const result = await failedStore.connection.executeCompiled({
|
|
505
503
|
sql: `DELETE FROM ${quotedTable}`,
|
|
506
504
|
source: "queue:failed:flush"
|
|
@@ -510,7 +508,8 @@ var queueDbFailedJobStore = {
|
|
|
510
508
|
};
|
|
511
509
|
var queueDbFailedStoreInternals = {
|
|
512
510
|
getFailedStoreConfig,
|
|
513
|
-
getFailedStoreConnection
|
|
511
|
+
getFailedStoreConnection,
|
|
512
|
+
loadFailedJobs
|
|
514
513
|
};
|
|
515
514
|
|
|
516
515
|
// src/runtime.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@holo-js/queue-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Holo-JS Framework - DB-backed integrations for @holo-js/queue",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,13 +23,13 @@
|
|
|
23
23
|
"test": "vitest --run"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@holo-js/db": "^0.1.
|
|
27
|
-
"@holo-js/queue": "^0.1.
|
|
26
|
+
"@holo-js/db": "^0.1.5",
|
|
27
|
+
"@holo-js/queue": "^0.1.5"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^22.10.2",
|
|
31
31
|
"tsup": "^8.3.5",
|
|
32
32
|
"typescript": "^5.7.2",
|
|
33
|
-
"vitest": "^
|
|
33
|
+
"vitest": "^4.1.5"
|
|
34
34
|
}
|
|
35
35
|
}
|