@tyravel/queue 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/database-queue.d.ts +20 -0
- package/dist/database-queue.d.ts.map +1 -0
- package/dist/database-queue.js +101 -0
- package/dist/database-queue.js.map +1 -0
- package/dist/database-queue.test.d.ts +2 -0
- package/dist/database-queue.test.d.ts.map +1 -0
- package/dist/database-queue.test.js +74 -0
- package/dist/database-queue.test.js.map +1 -0
- package/dist/dispatcher.d.ts +9 -0
- package/dist/dispatcher.d.ts.map +1 -0
- package/dist/dispatcher.js +13 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/failed-job-repository.d.ts +17 -0
- package/dist/failed-job-repository.d.ts.map +1 -0
- package/dist/failed-job-repository.js +74 -0
- package/dist/failed-job-repository.js.map +1 -0
- package/dist/failed-job-repository.test.d.ts +2 -0
- package/dist/failed-job-repository.test.d.ts.map +1 -0
- package/dist/failed-job-repository.test.js +75 -0
- package/dist/failed-job-repository.test.js.map +1 -0
- package/dist/failed-job-types.d.ts +21 -0
- package/dist/failed-job-types.d.ts.map +1 -0
- package/dist/failed-job-types.js +2 -0
- package/dist/failed-job-types.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/job.d.ts +8 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +13 -0
- package/dist/job.js.map +1 -0
- package/dist/payload.d.ts +6 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +18 -0
- package/dist/payload.js.map +1 -0
- package/dist/queue-contract.d.ts +8 -0
- package/dist/queue-contract.d.ts.map +1 -0
- package/dist/queue-contract.js +2 -0
- package/dist/queue-contract.js.map +1 -0
- package/dist/queue-manager.d.ts +15 -0
- package/dist/queue-manager.d.ts.map +1 -0
- package/dist/queue-manager.js +46 -0
- package/dist/queue-manager.js.map +1 -0
- package/dist/queue-processor.d.ts +22 -0
- package/dist/queue-processor.d.ts.map +1 -0
- package/dist/queue-processor.js +65 -0
- package/dist/queue-processor.js.map +1 -0
- package/dist/registry.d.ts +12 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +24 -0
- package/dist/registry.js.map +1 -0
- package/dist/sync-queue.d.ts +12 -0
- package/dist/sync-queue.d.ts.map +1 -0
- package/dist/sync-queue.js +26 -0
- package/dist/sync-queue.js.map +1 -0
- package/dist/sync-queue.test.d.ts +2 -0
- package/dist/sync-queue.test.d.ts.map +1 -0
- package/dist/sync-queue.test.js +32 -0
- package/dist/sync-queue.test.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.d.ts +15 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +22 -0
- package/dist/worker.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DatabaseConnection } from '@tyravel/database';
|
|
2
|
+
import type { Job } from './job.js';
|
|
3
|
+
import type { QueueContract } from './queue-contract.js';
|
|
4
|
+
import type { DatabaseQueueConnectionConfig, QueueJobRecord, SerializedJobPayload } from './types.js';
|
|
5
|
+
export declare class DatabaseQueue implements QueueContract {
|
|
6
|
+
private readonly connection;
|
|
7
|
+
private readonly table;
|
|
8
|
+
private readonly retryAfter;
|
|
9
|
+
constructor(connection: DatabaseConnection, config?: DatabaseQueueConnectionConfig);
|
|
10
|
+
push(job: Job, queue?: string): Promise<string>;
|
|
11
|
+
pushRaw(payload: SerializedJobPayload, queue?: string): Promise<string>;
|
|
12
|
+
later(delaySeconds: number, job: Job, queue?: string): Promise<string>;
|
|
13
|
+
pop(queue?: string): Promise<QueueJobRecord | null>;
|
|
14
|
+
deleteJob(id: number): Promise<void>;
|
|
15
|
+
release(id: number, delaySeconds?: number): Promise<void>;
|
|
16
|
+
decode(record: QueueJobRecord): SerializedJobPayload;
|
|
17
|
+
getRetryAfter(): number;
|
|
18
|
+
private tableBuilder;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=database-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-queue.d.ts","sourceRoot":"","sources":["../src/database-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EACV,6BAA6B,EAC7B,cAAc,EACd,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAYpB,qBAAa,aAAc,YAAW,aAAa;IAK/C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAGjB,UAAU,EAAE,kBAAkB,EAC/C,MAAM,GAAE,6BAAsD;IAM1D,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAIlD,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAc1E,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAezE,GAAG,CAAC,KAAK,SAAY,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA2BtD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D,MAAM,CAAC,MAAM,EAAE,cAAc;IAI7B,aAAa,IAAI,MAAM;IAIvB,OAAO,CAAC,YAAY;CAGrB"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { QueryBuilder } from '@tyravel/database';
|
|
3
|
+
import { decodePayload, encodePayload, serializeJob } from './payload.js';
|
|
4
|
+
export class DatabaseQueue {
|
|
5
|
+
connection;
|
|
6
|
+
table;
|
|
7
|
+
retryAfter;
|
|
8
|
+
constructor(connection, config = { driver: 'database' }) {
|
|
9
|
+
this.connection = connection;
|
|
10
|
+
this.table = config.table ?? 'jobs';
|
|
11
|
+
this.retryAfter = config.retryAfter ?? 90;
|
|
12
|
+
}
|
|
13
|
+
async push(job, queue = 'default') {
|
|
14
|
+
return this.pushRaw(serializeJob(job), queue);
|
|
15
|
+
}
|
|
16
|
+
async pushRaw(payload, queue = 'default') {
|
|
17
|
+
const now = unixNow();
|
|
18
|
+
const id = await this.tableBuilder().insert({
|
|
19
|
+
queue,
|
|
20
|
+
payload: encodePayload(payload),
|
|
21
|
+
attempts: 0,
|
|
22
|
+
reserved_at: null,
|
|
23
|
+
available_at: now,
|
|
24
|
+
created_at: now,
|
|
25
|
+
});
|
|
26
|
+
return String(id ?? randomUUID());
|
|
27
|
+
}
|
|
28
|
+
async later(delaySeconds, job, queue = 'default') {
|
|
29
|
+
const payload = serializeJob(job);
|
|
30
|
+
const now = unixNow();
|
|
31
|
+
const id = await this.tableBuilder().insert({
|
|
32
|
+
queue,
|
|
33
|
+
payload: encodePayload(payload),
|
|
34
|
+
attempts: 0,
|
|
35
|
+
reserved_at: null,
|
|
36
|
+
available_at: now + delaySeconds,
|
|
37
|
+
created_at: now,
|
|
38
|
+
});
|
|
39
|
+
return String(id ?? randomUUID());
|
|
40
|
+
}
|
|
41
|
+
async pop(queue = 'default') {
|
|
42
|
+
const now = unixNow();
|
|
43
|
+
const grammar = this.connection.grammar;
|
|
44
|
+
const table = grammar.wrapIdentifier(this.table);
|
|
45
|
+
const result = await this.connection.query(`SELECT id, queue, payload, attempts, reserved_at, available_at, created_at
|
|
46
|
+
FROM ${table}
|
|
47
|
+
WHERE ${grammar.wrapIdentifier('queue')} = ${grammar.parameter(1)}
|
|
48
|
+
AND ${grammar.wrapIdentifier('reserved_at')} IS NULL
|
|
49
|
+
AND ${grammar.wrapIdentifier('available_at')} <= ${grammar.parameter(2)}
|
|
50
|
+
ORDER BY ${grammar.wrapIdentifier('id')} ASC
|
|
51
|
+
LIMIT 1`, [queue, now]);
|
|
52
|
+
const row = result.rows[0];
|
|
53
|
+
if (!row) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
await this.tableBuilder()
|
|
57
|
+
.where('id', row.id)
|
|
58
|
+
.update({ reserved_at: now });
|
|
59
|
+
return mapRow(row);
|
|
60
|
+
}
|
|
61
|
+
async deleteJob(id) {
|
|
62
|
+
await this.tableBuilder().where('id', id).delete();
|
|
63
|
+
}
|
|
64
|
+
async release(id, delaySeconds = 0) {
|
|
65
|
+
const row = (await this.tableBuilder().where('id', id).first());
|
|
66
|
+
if (!row) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
await this.tableBuilder()
|
|
70
|
+
.where('id', id)
|
|
71
|
+
.update({
|
|
72
|
+
reserved_at: null,
|
|
73
|
+
available_at: unixNow() + delaySeconds,
|
|
74
|
+
attempts: row.attempts + 1,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
decode(record) {
|
|
78
|
+
return decodePayload(record.payload);
|
|
79
|
+
}
|
|
80
|
+
getRetryAfter() {
|
|
81
|
+
return this.retryAfter;
|
|
82
|
+
}
|
|
83
|
+
tableBuilder() {
|
|
84
|
+
return new QueryBuilder(this.connection, this.table);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function unixNow() {
|
|
88
|
+
return Math.floor(Date.now() / 1000);
|
|
89
|
+
}
|
|
90
|
+
function mapRow(row) {
|
|
91
|
+
return {
|
|
92
|
+
id: row.id,
|
|
93
|
+
queue: row.queue,
|
|
94
|
+
payload: row.payload,
|
|
95
|
+
attempts: row.attempts,
|
|
96
|
+
reservedAt: row.reserved_at,
|
|
97
|
+
availableAt: row.available_at,
|
|
98
|
+
createdAt: row.created_at,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=database-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-queue.js","sourceRoot":"","sources":["../src/database-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAkB1E,MAAM,OAAO,aAAa;IAKL;IAJF,KAAK,CAAS;IACd,UAAU,CAAS;IAEpC,YACmB,UAA8B,EAC/C,SAAwC,EAAE,MAAM,EAAE,UAAU,EAAE;QAD7C,eAAU,GAAV,UAAU,CAAoB;QAG/C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC;QACpC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,GAAQ,EAAE,KAAK,GAAG,SAAS;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAA6B,EAAE,KAAK,GAAG,SAAS;QAC5D,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC;YAC1C,KAAK;YACL,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC;YAC/B,QAAQ,EAAE,CAAC;YACX,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,GAAG;YACjB,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,EAAE,IAAI,UAAU,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,YAAoB,EAAE,GAAQ,EAAE,KAAK,GAAG,SAAS;QAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,MAAM,CAAC;YAC1C,KAAK;YACL,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC;YAC/B,QAAQ,EAAE,CAAC;YACX,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,GAAG,GAAG,YAAY;YAChC,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,EAAE,IAAI,UAAU,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,SAAS;QACzB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CACxC;cACQ,KAAK;eACJ,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;eACzD,OAAO,CAAC,cAAc,CAAC,aAAa,CAAC;eACrC,OAAO,CAAC,cAAc,CAAC,cAAc,CAAC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;kBAC9D,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC;eAC/B,EACT,CAAC,KAAK,EAAE,GAAG,CAAC,CACb,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAA6B,CAAC;QACvD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,IAAI,CAAC,YAAY,EAAE;aACtB,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;aACnB,MAAM,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC;QAEhC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,EAAU;QACxB,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,EAAU,EAAE,YAAY,GAAG,CAAC;QACxC,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAwB,CAAC;QACvF,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QAED,MAAM,IAAI,CAAC,YAAY,EAAE;aACtB,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;aACf,MAAM,CAAC;YACN,WAAW,EAAE,IAAI;YACjB,YAAY,EAAE,OAAO,EAAE,GAAG,YAAY;YACtC,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC;SAC3B,CAAC,CAAC;IACP,CAAC;IAED,MAAM,CAAC,MAAsB;QAC3B,OAAO,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAEO,YAAY;QAClB,OAAO,IAAI,YAAY,CAAe,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACrE,CAAC;CACF;AAED,SAAS,OAAO;IACd,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,MAAM,CAAC,GAAiB;IAC/B,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,SAAS,EAAE,GAAG,CAAC,UAAU;KAC1B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-queue.test.d.ts","sourceRoot":"","sources":["../src/database-queue.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { SchemaBuilder } from '@tyravel/database';
|
|
3
|
+
import { SqliteConnection } from '@tyravel/database';
|
|
4
|
+
import { Job } from './job.js';
|
|
5
|
+
import { JobRegistry } from './registry.js';
|
|
6
|
+
import { DatabaseQueue } from './database-queue.js';
|
|
7
|
+
import { QueueManager } from './queue-manager.js';
|
|
8
|
+
import { QueueProcessor } from './queue-processor.js';
|
|
9
|
+
import { QueueWorker } from './worker.js';
|
|
10
|
+
class PersistedJob extends Job {
|
|
11
|
+
static values = [];
|
|
12
|
+
async handle() {
|
|
13
|
+
PersistedJob.values.push(this.data.value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function createJobsTable(connection) {
|
|
17
|
+
const schema = new SchemaBuilder(connection);
|
|
18
|
+
await schema.create('jobs', (table) => {
|
|
19
|
+
table.id();
|
|
20
|
+
table.string('queue');
|
|
21
|
+
table.text('payload');
|
|
22
|
+
table.integer('attempts');
|
|
23
|
+
table.integer('reserved_at').nullable();
|
|
24
|
+
table.integer('available_at');
|
|
25
|
+
table.integer('created_at');
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function asDatabaseManager(connection) {
|
|
29
|
+
return {
|
|
30
|
+
connection: () => connection,
|
|
31
|
+
close: async () => { },
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
describe('DatabaseQueue', () => {
|
|
35
|
+
it('stores and processes jobs through the worker', async () => {
|
|
36
|
+
PersistedJob.values = [];
|
|
37
|
+
const connection = new SqliteConnection(':memory:');
|
|
38
|
+
await createJobsTable(connection);
|
|
39
|
+
const registry = new JobRegistry().register(PersistedJob);
|
|
40
|
+
const worker = new QueueWorker(registry);
|
|
41
|
+
const databaseQueue = new DatabaseQueue(connection, { driver: 'database', table: 'jobs' });
|
|
42
|
+
await databaseQueue.push(new PersistedJob({ value: 'first' }));
|
|
43
|
+
const record = await databaseQueue.pop('default');
|
|
44
|
+
expect(record).not.toBeNull();
|
|
45
|
+
await worker.process(databaseQueue.decode(record));
|
|
46
|
+
await databaseQueue.deleteJob(record.id);
|
|
47
|
+
expect(PersistedJob.values).toEqual(['first']);
|
|
48
|
+
});
|
|
49
|
+
it('processes queued jobs via QueueProcessor', async () => {
|
|
50
|
+
PersistedJob.values = [];
|
|
51
|
+
const connection = new SqliteConnection(':memory:');
|
|
52
|
+
await createJobsTable(connection);
|
|
53
|
+
const registry = new JobRegistry().register(PersistedJob);
|
|
54
|
+
const worker = new QueueWorker(registry);
|
|
55
|
+
const config = {
|
|
56
|
+
default: 'database',
|
|
57
|
+
connections: {
|
|
58
|
+
database: { driver: 'database', table: 'jobs' },
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
const manager = new QueueManager(config, worker, asDatabaseManager(connection));
|
|
62
|
+
const queue = manager.connection('database');
|
|
63
|
+
await queue.push(new PersistedJob({ value: 'queued' }));
|
|
64
|
+
const processor = new QueueProcessor(manager, registry, worker);
|
|
65
|
+
const processed = await processor.run({
|
|
66
|
+
connection: 'database',
|
|
67
|
+
maxJobs: 1,
|
|
68
|
+
sleepSeconds: 0,
|
|
69
|
+
});
|
|
70
|
+
expect(processed).toBe(1);
|
|
71
|
+
expect(PersistedJob.values).toEqual(['queued']);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=database-queue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database-queue.test.js","sourceRoot":"","sources":["../src/database-queue.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Job } from './job.js';
|
|
2
|
+
import type { QueueContract } from './queue-contract.js';
|
|
3
|
+
export declare class Dispatcher {
|
|
4
|
+
private readonly queue;
|
|
5
|
+
constructor(queue: QueueContract);
|
|
6
|
+
dispatch(job: Job, queue?: string): Promise<string>;
|
|
7
|
+
dispatchLater(delaySeconds: number, job: Job, queue?: string): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,aAAa;IAEjD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAInD,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG/E"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export class Dispatcher {
|
|
2
|
+
queue;
|
|
3
|
+
constructor(queue) {
|
|
4
|
+
this.queue = queue;
|
|
5
|
+
}
|
|
6
|
+
dispatch(job, queue) {
|
|
7
|
+
return this.queue.push(job, queue);
|
|
8
|
+
}
|
|
9
|
+
dispatchLater(delaySeconds, job, queue) {
|
|
10
|
+
return this.queue.later(delaySeconds, job, queue);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,UAAU;IACQ;IAA7B,YAA6B,KAAoB;QAApB,UAAK,GAAL,KAAK,CAAe;IAAG,CAAC;IAErD,QAAQ,CAAC,GAAQ,EAAE,KAAc;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,aAAa,CAAC,YAAoB,EAAE,GAAQ,EAAE,KAAc;QAC1D,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DatabaseConnection } from '@tyravel/database';
|
|
2
|
+
import type { FailedJobRecord, FailedJobsConfig, RecordFailedJobInput } from './failed-job-types.js';
|
|
3
|
+
import type { SerializedJobPayload } from './types.js';
|
|
4
|
+
export declare class FailedJobRepository {
|
|
5
|
+
private readonly connection;
|
|
6
|
+
private readonly table;
|
|
7
|
+
constructor(connection: DatabaseConnection, config?: FailedJobsConfig);
|
|
8
|
+
record(input: RecordFailedJobInput): Promise<number>;
|
|
9
|
+
all(limit?: number): Promise<FailedJobRecord[]>;
|
|
10
|
+
find(id: number): Promise<FailedJobRecord | null>;
|
|
11
|
+
retry(id: number, push: (payload: SerializedJobPayload, queue: string) => Promise<void>): Promise<boolean>;
|
|
12
|
+
forget(id: number): Promise<boolean>;
|
|
13
|
+
private tableBuilder;
|
|
14
|
+
}
|
|
15
|
+
export declare function formatJobException(error: unknown): string;
|
|
16
|
+
export declare function newFailedJobUuid(): string;
|
|
17
|
+
//# sourceMappingURL=failed-job-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-repository.d.ts","sourceRoot":"","sources":["../src/failed-job-repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAYvD,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAH7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAGZ,UAAU,EAAE,kBAAkB,EAC/C,MAAM,GAAE,gBAAqB;IAKzB,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;IAapD,GAAG,CAAC,KAAK,SAAK,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAS3C,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAOjD,KAAK,CACT,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GACpE,OAAO,CAAC,OAAO,CAAC;IAYb,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1C,OAAO,CAAC,YAAY;CAGrB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { QueryBuilder } from '@tyravel/database';
|
|
3
|
+
import { decodePayload } from './payload.js';
|
|
4
|
+
export class FailedJobRepository {
|
|
5
|
+
connection;
|
|
6
|
+
table;
|
|
7
|
+
constructor(connection, config = {}) {
|
|
8
|
+
this.connection = connection;
|
|
9
|
+
this.table = config.table ?? 'failed_jobs';
|
|
10
|
+
}
|
|
11
|
+
async record(input) {
|
|
12
|
+
const id = await this.tableBuilder().insert({
|
|
13
|
+
uuid: input.uuid,
|
|
14
|
+
connection: input.connection,
|
|
15
|
+
queue: input.queue,
|
|
16
|
+
payload: input.payload,
|
|
17
|
+
exception: input.exception,
|
|
18
|
+
failed_at: unixNow(),
|
|
19
|
+
});
|
|
20
|
+
return Number(id);
|
|
21
|
+
}
|
|
22
|
+
async all(limit = 50) {
|
|
23
|
+
const rows = (await this.tableBuilder()
|
|
24
|
+
.orderBy('id', 'desc')
|
|
25
|
+
.limit(limit)
|
|
26
|
+
.get());
|
|
27
|
+
return rows.map(mapRow);
|
|
28
|
+
}
|
|
29
|
+
async find(id) {
|
|
30
|
+
const row = (await this.tableBuilder().where('id', id).first());
|
|
31
|
+
return row ? mapRow(row) : null;
|
|
32
|
+
}
|
|
33
|
+
async retry(id, push) {
|
|
34
|
+
const record = await this.find(id);
|
|
35
|
+
if (!record) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const payload = decodePayload(record.payload);
|
|
39
|
+
await push(payload, record.queue);
|
|
40
|
+
await this.tableBuilder().where('id', id).delete();
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
async forget(id) {
|
|
44
|
+
const deleted = await this.tableBuilder().where('id', id).delete();
|
|
45
|
+
return deleted > 0;
|
|
46
|
+
}
|
|
47
|
+
tableBuilder() {
|
|
48
|
+
return new QueryBuilder(this.connection, this.table);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function formatJobException(error) {
|
|
52
|
+
if (error instanceof Error) {
|
|
53
|
+
return `${error.name}: ${error.message}\n${error.stack ?? ''}`;
|
|
54
|
+
}
|
|
55
|
+
return String(error);
|
|
56
|
+
}
|
|
57
|
+
export function newFailedJobUuid() {
|
|
58
|
+
return randomUUID();
|
|
59
|
+
}
|
|
60
|
+
function unixNow() {
|
|
61
|
+
return Math.floor(Date.now() / 1000);
|
|
62
|
+
}
|
|
63
|
+
function mapRow(row) {
|
|
64
|
+
return {
|
|
65
|
+
id: row.id,
|
|
66
|
+
uuid: row.uuid,
|
|
67
|
+
connection: row.connection,
|
|
68
|
+
queue: row.queue,
|
|
69
|
+
payload: row.payload,
|
|
70
|
+
exception: row.exception,
|
|
71
|
+
failedAt: row.failed_at,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=failed-job-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-repository.js","sourceRoot":"","sources":["../src/failed-job-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-repository.test.d.ts","sourceRoot":"","sources":["../src/failed-job-repository.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { SchemaBuilder, SqliteConnection } from '@tyravel/database';
|
|
3
|
+
import { FailedJobRepository } from './failed-job-repository.js';
|
|
4
|
+
import { Job } from './job.js';
|
|
5
|
+
import { JobRegistry } from './registry.js';
|
|
6
|
+
import { QueueManager } from './queue-manager.js';
|
|
7
|
+
import { QueueProcessor } from './queue-processor.js';
|
|
8
|
+
import { QueueWorker } from './worker.js';
|
|
9
|
+
class ExplodingJob extends Job {
|
|
10
|
+
async handle() {
|
|
11
|
+
throw new Error(this.data.message);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function asDatabaseManager(connection) {
|
|
15
|
+
return {
|
|
16
|
+
connection: () => connection,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function createJobsTable(connection) {
|
|
20
|
+
const schema = new SchemaBuilder(connection);
|
|
21
|
+
await schema.create('jobs', (table) => {
|
|
22
|
+
table.id();
|
|
23
|
+
table.string('queue');
|
|
24
|
+
table.text('payload');
|
|
25
|
+
table.integer('attempts');
|
|
26
|
+
table.integer('reserved_at').nullable();
|
|
27
|
+
table.integer('available_at');
|
|
28
|
+
table.integer('created_at');
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function createFailedJobsTable(connection) {
|
|
32
|
+
const schema = new SchemaBuilder(connection);
|
|
33
|
+
await schema.create('failed_jobs', (table) => {
|
|
34
|
+
table.id();
|
|
35
|
+
table.string('uuid');
|
|
36
|
+
table.string('connection');
|
|
37
|
+
table.string('queue');
|
|
38
|
+
table.text('payload');
|
|
39
|
+
table.text('exception');
|
|
40
|
+
table.integer('failed_at');
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
describe('FailedJobRepository', () => {
|
|
44
|
+
it('records permanently failed jobs and supports retry', async () => {
|
|
45
|
+
const connection = new SqliteConnection(':memory:');
|
|
46
|
+
await createJobsTable(connection);
|
|
47
|
+
await createFailedJobsTable(connection);
|
|
48
|
+
const failedJobs = new FailedJobRepository(connection);
|
|
49
|
+
const registry = new JobRegistry().register(ExplodingJob);
|
|
50
|
+
const worker = new QueueWorker(registry, undefined, { maxAttempts: 1 });
|
|
51
|
+
const config = {
|
|
52
|
+
default: 'database',
|
|
53
|
+
connections: {
|
|
54
|
+
database: { driver: 'database', table: 'jobs' },
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
const manager = new QueueManager(config, worker, asDatabaseManager(connection));
|
|
58
|
+
const queue = manager.connection('database');
|
|
59
|
+
await queue.push(new ExplodingJob({ message: 'boom' }));
|
|
60
|
+
const processor = new QueueProcessor(manager, registry, worker, { failedJobs });
|
|
61
|
+
await processor.run({ connection: 'database', maxJobs: 1, sleepSeconds: 0 });
|
|
62
|
+
const failed = await failedJobs.all();
|
|
63
|
+
expect(failed).toHaveLength(1);
|
|
64
|
+
expect(failed[0]?.queue).toBe('default');
|
|
65
|
+
expect(failed[0]?.exception).toContain('boom');
|
|
66
|
+
const retried = await failedJobs.retry(failed[0].id, async (payload, queueName) => {
|
|
67
|
+
await queue.pushRaw(payload, queueName);
|
|
68
|
+
});
|
|
69
|
+
expect(retried).toBe(true);
|
|
70
|
+
expect(await failedJobs.all()).toHaveLength(0);
|
|
71
|
+
const record = await queue.pop('default');
|
|
72
|
+
expect(record).not.toBeNull();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=failed-job-repository.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-repository.test.js","sourceRoot":"","sources":["../src/failed-job-repository.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface FailedJobsConfig {
|
|
2
|
+
table?: string;
|
|
3
|
+
database?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface FailedJobRecord {
|
|
6
|
+
id: number;
|
|
7
|
+
uuid: string;
|
|
8
|
+
connection: string;
|
|
9
|
+
queue: string;
|
|
10
|
+
payload: string;
|
|
11
|
+
exception: string;
|
|
12
|
+
failedAt: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RecordFailedJobInput {
|
|
15
|
+
uuid: string;
|
|
16
|
+
connection: string;
|
|
17
|
+
queue: string;
|
|
18
|
+
payload: string;
|
|
19
|
+
exception: string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=failed-job-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-types.d.ts","sourceRoot":"","sources":["../src/failed-job-types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failed-job-types.js","sourceRoot":"","sources":["../src/failed-job-types.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Container } from '@tyravel/container';
|
|
2
|
+
import type { DatabaseManager } from '@tyravel/database';
|
|
3
|
+
import { Dispatcher } from './dispatcher.js';
|
|
4
|
+
import { JobRegistry } from './registry.js';
|
|
5
|
+
import { QueueManager } from './queue-manager.js';
|
|
6
|
+
import { QueueProcessor } from './queue-processor.js';
|
|
7
|
+
import type { QueueConfig } from './types.js';
|
|
8
|
+
export declare function createQueueStack(options: {
|
|
9
|
+
config: QueueConfig;
|
|
10
|
+
registry: JobRegistry;
|
|
11
|
+
database?: DatabaseManager;
|
|
12
|
+
container?: Container;
|
|
13
|
+
}): {
|
|
14
|
+
manager: QueueManager;
|
|
15
|
+
dispatcher: Dispatcher;
|
|
16
|
+
processor: QueueProcessor;
|
|
17
|
+
registry: JobRegistry;
|
|
18
|
+
};
|
|
19
|
+
export { DatabaseQueue } from './database-queue.js';
|
|
20
|
+
export { Dispatcher } from './dispatcher.js';
|
|
21
|
+
export { FailedJobRepository, formatJobException, newFailedJobUuid, } from './failed-job-repository.js';
|
|
22
|
+
export { Job } from './job.js';
|
|
23
|
+
export { JobNotFoundException, JobRegistry } from './registry.js';
|
|
24
|
+
export { decodePayload, encodePayload, serializeJob } from './payload.js';
|
|
25
|
+
export type { QueueContract } from './queue-contract.js';
|
|
26
|
+
export { QueueManager } from './queue-manager.js';
|
|
27
|
+
export { QueueProcessor } from './queue-processor.js';
|
|
28
|
+
export { SyncQueue } from './sync-queue.js';
|
|
29
|
+
export type { DatabaseQueueConnectionConfig, FailedJobRecord, FailedJobsConfig, QueueConfig, QueueConnectionConfig, QueueJobRecord, SerializedJobPayload, SyncQueueConnectionConfig, } from './types.js';
|
|
30
|
+
export { QueueWorker } from './worker.js';
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,GAAG;IACF,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,UAAU,CAAC;IACvB,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,WAAW,CAAC;CACvB,CAQA;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EACV,6BAA6B,EAC7B,eAAe,EACf,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Dispatcher } from './dispatcher.js';
|
|
2
|
+
import { QueueManager } from './queue-manager.js';
|
|
3
|
+
import { QueueProcessor } from './queue-processor.js';
|
|
4
|
+
import { QueueWorker } from './worker.js';
|
|
5
|
+
export function createQueueStack(options) {
|
|
6
|
+
const registry = options.registry;
|
|
7
|
+
const worker = new QueueWorker(registry, options.container);
|
|
8
|
+
const manager = new QueueManager(options.config, worker, options.database);
|
|
9
|
+
const dispatcher = new Dispatcher(manager.connection());
|
|
10
|
+
const processor = new QueueProcessor(manager, registry, worker);
|
|
11
|
+
return { manager, dispatcher, processor, registry };
|
|
12
|
+
}
|
|
13
|
+
export { DatabaseQueue } from './database-queue.js';
|
|
14
|
+
export { Dispatcher } from './dispatcher.js';
|
|
15
|
+
export { FailedJobRepository, formatJobException, newFailedJobUuid, } from './failed-job-repository.js';
|
|
16
|
+
export { Job } from './job.js';
|
|
17
|
+
export { JobNotFoundException, JobRegistry } from './registry.js';
|
|
18
|
+
export { decodePayload, encodePayload, serializeJob } from './payload.js';
|
|
19
|
+
export { QueueManager } from './queue-manager.js';
|
|
20
|
+
export { QueueProcessor } from './queue-processor.js';
|
|
21
|
+
export { SyncQueue } from './sync-queue.js';
|
|
22
|
+
export { QueueWorker } from './worker.js';
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,gBAAgB,CAAC,OAKhC;IAMC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3E,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,IAAI,cAAc,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEhE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACtD,CAAC;AAED,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAW5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/job.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare abstract class Job<TData extends Record<string, unknown> = Record<string, unknown>> {
|
|
2
|
+
readonly data: TData;
|
|
3
|
+
constructor(data: TData);
|
|
4
|
+
abstract handle(): void | Promise<void>;
|
|
5
|
+
jobName(): string;
|
|
6
|
+
displayName(): string;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA,8BAAsB,GAAG,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;aAC3D,IAAI,EAAE,KAAK;gBAAX,IAAI,EAAE,KAAK;IAEvC,QAAQ,CAAC,MAAM,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvC,OAAO,IAAI,MAAM;IAIjB,WAAW,IAAI,MAAM;CAGtB"}
|
package/dist/job.js
ADDED
package/dist/job.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.js","sourceRoot":"","sources":["../src/job.ts"],"names":[],"mappings":"AAAA,MAAM,OAAgB,GAAG;IACK;IAA5B,YAA4B,IAAW;QAAX,SAAI,GAAJ,IAAI,CAAO;IAAG,CAAC;IAI3C,OAAO;QACL,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Job } from './job.js';
|
|
2
|
+
import type { SerializedJobPayload } from './types.js';
|
|
3
|
+
export declare function serializeJob(job: Job): SerializedJobPayload;
|
|
4
|
+
export declare function encodePayload(payload: SerializedJobPayload): string;
|
|
5
|
+
export declare function decodePayload(raw: string): SerializedJobPayload;
|
|
6
|
+
//# sourceMappingURL=payload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,oBAAoB,CAM3D;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CAEnE;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAM/D"}
|
package/dist/payload.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function serializeJob(job) {
|
|
2
|
+
return {
|
|
3
|
+
job: job.jobName(),
|
|
4
|
+
data: job.data,
|
|
5
|
+
displayName: job.displayName(),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export function encodePayload(payload) {
|
|
9
|
+
return JSON.stringify(payload);
|
|
10
|
+
}
|
|
11
|
+
export function decodePayload(raw) {
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
if (!parsed || typeof parsed.job !== 'string' || typeof parsed.data !== 'object') {
|
|
14
|
+
throw new Error('Invalid queue job payload');
|
|
15
|
+
}
|
|
16
|
+
return parsed;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"AAGA,MAAM,UAAU,YAAY,CAAC,GAAQ;IACnC,OAAO;QACL,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE;QAClB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE;KAC/B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;IACvD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACjF,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Job } from './job.js';
|
|
2
|
+
import type { SerializedJobPayload } from './types.js';
|
|
3
|
+
export interface QueueContract {
|
|
4
|
+
push(job: Job, queue?: string): Promise<string>;
|
|
5
|
+
pushRaw(payload: SerializedJobPayload, queue?: string): Promise<string>;
|
|
6
|
+
later(delaySeconds: number, job: Job, queue?: string): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=queue-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-contract.d.ts","sourceRoot":"","sources":["../src/queue-contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxE,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-contract.js","sourceRoot":"","sources":["../src/queue-contract.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DatabaseManager } from '@tyravel/database';
|
|
2
|
+
import type { QueueContract } from './queue-contract.js';
|
|
3
|
+
import type { QueueConfig } from './types.js';
|
|
4
|
+
import { QueueWorker } from './worker.js';
|
|
5
|
+
export declare class QueueManager {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly worker;
|
|
8
|
+
private readonly database?;
|
|
9
|
+
private readonly queues;
|
|
10
|
+
constructor(config: QueueConfig, worker: QueueWorker, database?: DatabaseManager | undefined);
|
|
11
|
+
connection(name?: string): QueueContract;
|
|
12
|
+
getDefaultConnection(): string;
|
|
13
|
+
private createConnection;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=queue-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-manager.d.ts","sourceRoot":"","sources":["../src/queue-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAyB,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,qBAAa,YAAY;IAIrB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAL5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;gBAGxC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,WAAW,EACnB,QAAQ,CAAC,EAAE,eAAe,YAAA;IAG7C,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa;IAiBxC,oBAAoB,IAAI,MAAM;IAI9B,OAAO,CAAC,gBAAgB;CAezB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DatabaseQueue } from './database-queue.js';
|
|
2
|
+
import { SyncQueue } from './sync-queue.js';
|
|
3
|
+
export class QueueManager {
|
|
4
|
+
config;
|
|
5
|
+
worker;
|
|
6
|
+
database;
|
|
7
|
+
queues = new Map();
|
|
8
|
+
constructor(config, worker, database) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
this.worker = worker;
|
|
11
|
+
this.database = database;
|
|
12
|
+
}
|
|
13
|
+
connection(name) {
|
|
14
|
+
const connectionName = name ?? this.config.default;
|
|
15
|
+
const existing = this.queues.get(connectionName);
|
|
16
|
+
if (existing) {
|
|
17
|
+
return existing;
|
|
18
|
+
}
|
|
19
|
+
const connectionConfig = this.config.connections[connectionName];
|
|
20
|
+
if (!connectionConfig) {
|
|
21
|
+
throw new Error(`Queue connection not configured: ${connectionName}`);
|
|
22
|
+
}
|
|
23
|
+
const queue = this.createConnection(connectionConfig);
|
|
24
|
+
this.queues.set(connectionName, queue);
|
|
25
|
+
return queue;
|
|
26
|
+
}
|
|
27
|
+
getDefaultConnection() {
|
|
28
|
+
return this.config.default;
|
|
29
|
+
}
|
|
30
|
+
createConnection(config) {
|
|
31
|
+
switch (config.driver) {
|
|
32
|
+
case 'sync':
|
|
33
|
+
return new SyncQueue(this.worker);
|
|
34
|
+
case 'database': {
|
|
35
|
+
if (!this.database) {
|
|
36
|
+
throw new Error('Database manager is required for the database queue driver');
|
|
37
|
+
}
|
|
38
|
+
const dbConnection = this.database.connection(config.connection);
|
|
39
|
+
return new DatabaseQueue(dbConnection, config);
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
throw new Error(`Unsupported queue driver: ${config.driver}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=queue-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-manager.js","sourceRoot":"","sources":["../src/queue-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,YAAY;IAIJ;IACA;IACA;IALF,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE3D,YACmB,MAAmB,EACnB,MAAmB,EACnB,QAA0B;QAF1B,WAAM,GAAN,MAAM,CAAa;QACnB,WAAM,GAAN,MAAM,CAAa;QACnB,aAAQ,GAAR,QAAQ,CAAkB;IAC1C,CAAC;IAEJ,UAAU,CAAC,IAAa;QACtB,MAAM,cAAc,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACjE,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,oCAAoC,cAAc,EAAE,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oBAAoB;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;IAC7B,CAAC;IAEO,gBAAgB,CAAC,MAA6B;QACpD,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,MAAM;gBACT,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpC,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACnB,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;gBAChF,CAAC;gBACD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBACjE,OAAO,IAAI,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YACjD,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CAAC,6BAA8B,MAAgC,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7F,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FailedJobRepository } from './failed-job-repository.js';
|
|
2
|
+
import { JobRegistry } from './registry.js';
|
|
3
|
+
import { QueueManager } from './queue-manager.js';
|
|
4
|
+
import { QueueWorker } from './worker.js';
|
|
5
|
+
export interface QueueWorkerRunOptions {
|
|
6
|
+
connection?: string;
|
|
7
|
+
queue?: string;
|
|
8
|
+
sleepSeconds?: number;
|
|
9
|
+
maxJobs?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface QueueProcessorOptions {
|
|
12
|
+
failedJobs?: FailedJobRepository;
|
|
13
|
+
}
|
|
14
|
+
export declare class QueueProcessor {
|
|
15
|
+
private readonly manager;
|
|
16
|
+
private readonly registry;
|
|
17
|
+
private readonly worker;
|
|
18
|
+
private readonly options;
|
|
19
|
+
constructor(manager: QueueManager, registry: JobRegistry, worker: QueueWorker, options?: QueueProcessorOptions);
|
|
20
|
+
run(options?: QueueWorkerRunOptions): Promise<number>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=queue-processor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-processor.d.ts","sourceRoot":"","sources":["../src/queue-processor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAKtE,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,mBAAmB,CAAC;CAClC;AAED,qBAAa,cAAc;IAEvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAHP,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,WAAW,EACnB,OAAO,GAAE,qBAA0B;IAGhD,GAAG,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC;CAmDhE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DatabaseQueue } from './database-queue.js';
|
|
2
|
+
import { formatJobException, newFailedJobUuid, } from './failed-job-repository.js';
|
|
3
|
+
export class QueueProcessor {
|
|
4
|
+
manager;
|
|
5
|
+
registry;
|
|
6
|
+
worker;
|
|
7
|
+
options;
|
|
8
|
+
constructor(manager, registry, worker, options = {}) {
|
|
9
|
+
this.manager = manager;
|
|
10
|
+
this.registry = registry;
|
|
11
|
+
this.worker = worker;
|
|
12
|
+
this.options = options;
|
|
13
|
+
}
|
|
14
|
+
async run(options = {}) {
|
|
15
|
+
const connectionName = options.connection ?? this.manager.getDefaultConnection();
|
|
16
|
+
const queueName = options.queue ?? 'default';
|
|
17
|
+
const sleepSeconds = options.sleepSeconds ?? 1;
|
|
18
|
+
const maxJobs = options.maxJobs;
|
|
19
|
+
const connection = this.manager.connection(connectionName);
|
|
20
|
+
if (!(connection instanceof DatabaseQueue)) {
|
|
21
|
+
throw new Error('Queue worker only supports the database queue driver');
|
|
22
|
+
}
|
|
23
|
+
let processed = 0;
|
|
24
|
+
while (maxJobs === undefined || processed < maxJobs) {
|
|
25
|
+
const record = await connection.pop(queueName);
|
|
26
|
+
if (!record) {
|
|
27
|
+
await sleep(sleepSeconds * 1000);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const payload = connection.decode(record);
|
|
32
|
+
if (!this.registry.has(payload.job)) {
|
|
33
|
+
throw new Error(`Job class not registered: ${payload.job}`);
|
|
34
|
+
}
|
|
35
|
+
await this.worker.process(payload);
|
|
36
|
+
await connection.deleteJob(record.id);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (record.attempts + 1 >= this.worker.getMaxAttempts()) {
|
|
40
|
+
if (this.options.failedJobs) {
|
|
41
|
+
await this.options.failedJobs.record({
|
|
42
|
+
uuid: newFailedJobUuid(),
|
|
43
|
+
connection: connectionName,
|
|
44
|
+
queue: queueName,
|
|
45
|
+
payload: record.payload,
|
|
46
|
+
exception: formatJobException(error),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
await connection.deleteJob(record.id);
|
|
50
|
+
process.stderr.write(`Job ${record.id} failed permanently: ${String(error)}\n`);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await connection.release(record.id, connection.getRetryAfter());
|
|
54
|
+
process.stderr.write(`Job ${record.id} failed, released for retry: ${String(error)}\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
processed += 1;
|
|
58
|
+
}
|
|
59
|
+
return processed;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function sleep(ms) {
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=queue-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-processor.js","sourceRoot":"","sources":["../src/queue-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Job } from './job.js';
|
|
2
|
+
export type JobConstructor<T extends Job = Job> = new (data: Record<string, unknown>) => T;
|
|
3
|
+
export declare class JobNotFoundException extends Error {
|
|
4
|
+
constructor(name: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class JobRegistry {
|
|
7
|
+
private readonly jobs;
|
|
8
|
+
register<TData extends Record<string, unknown>, TJob extends Job<TData>>(constructor: new (data: TData) => TJob): this;
|
|
9
|
+
has(name: string): boolean;
|
|
10
|
+
create(name: string, data: Record<string, unknown>): Job;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,IAAI,KAChD,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,CAAC,CAAC;AAEP,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,IAAI,EAAE,MAAM;CAIzB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAE1D,QAAQ,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,SAAS,GAAG,CAAC,KAAK,CAAC,EACrE,WAAW,EAAE,KAAK,IAAI,EAAE,KAAK,KAAK,IAAI,GACrC,IAAI;IAKP,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,GAAG;CAOzD"}
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class JobNotFoundException extends Error {
|
|
2
|
+
constructor(name) {
|
|
3
|
+
super(`Job class not registered: ${name}`);
|
|
4
|
+
this.name = 'JobNotFoundException';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class JobRegistry {
|
|
8
|
+
jobs = new Map();
|
|
9
|
+
register(constructor) {
|
|
10
|
+
this.jobs.set(constructor.name, constructor);
|
|
11
|
+
return this;
|
|
12
|
+
}
|
|
13
|
+
has(name) {
|
|
14
|
+
return this.jobs.has(name);
|
|
15
|
+
}
|
|
16
|
+
create(name, data) {
|
|
17
|
+
const constructor = this.jobs.get(name);
|
|
18
|
+
if (!constructor) {
|
|
19
|
+
throw new JobNotFoundException(name);
|
|
20
|
+
}
|
|
21
|
+
return new constructor(data);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,IAAY;QACtB,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAED,MAAM,OAAO,WAAW;IACL,IAAI,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE1D,QAAQ,CACN,WAAsC;QAEtC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,WAAwC,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAA6B;QAChD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Job } from './job.js';
|
|
2
|
+
import type { QueueContract } from './queue-contract.js';
|
|
3
|
+
import type { SerializedJobPayload } from './types.js';
|
|
4
|
+
import type { QueueWorker } from './worker.js';
|
|
5
|
+
export declare class SyncQueue implements QueueContract {
|
|
6
|
+
private readonly worker;
|
|
7
|
+
constructor(worker: QueueWorker);
|
|
8
|
+
push(job: Job, _queue?: string): Promise<string>;
|
|
9
|
+
pushRaw(payload: SerializedJobPayload, _queue?: string): Promise<string>;
|
|
10
|
+
later(delaySeconds: number, job: Job, queue?: string): Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=sync-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-queue.d.ts","sourceRoot":"","sources":["../src/sync-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,SAAU,YAAW,aAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,WAAW;IAE1C,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAInD,OAAO,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAM3E,KAAK,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,SAAY,GAAG,OAAO,CAAC,MAAM,CAAC;CAMhF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { decodePayload, encodePayload, serializeJob } from './payload.js';
|
|
3
|
+
export class SyncQueue {
|
|
4
|
+
worker;
|
|
5
|
+
constructor(worker) {
|
|
6
|
+
this.worker = worker;
|
|
7
|
+
}
|
|
8
|
+
async push(job, _queue = 'default') {
|
|
9
|
+
return this.pushRaw(serializeJob(job), _queue);
|
|
10
|
+
}
|
|
11
|
+
async pushRaw(payload, _queue = 'default') {
|
|
12
|
+
const id = randomUUID();
|
|
13
|
+
await this.worker.process(decodePayload(encodePayload(payload)));
|
|
14
|
+
return id;
|
|
15
|
+
}
|
|
16
|
+
async later(delaySeconds, job, queue = 'default') {
|
|
17
|
+
if (delaySeconds > 0) {
|
|
18
|
+
await sleep(delaySeconds * 1000);
|
|
19
|
+
}
|
|
20
|
+
return this.push(job, queue);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function sleep(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=sync-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-queue.js","sourceRoot":"","sources":["../src/sync-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAK1E,MAAM,OAAO,SAAS;IACS;IAA7B,YAA6B,MAAmB;QAAnB,WAAM,GAAN,MAAM,CAAa;IAAG,CAAC;IAEpD,KAAK,CAAC,IAAI,CAAC,GAAQ,EAAE,MAAM,GAAG,SAAS;QACrC,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAA6B,EAAE,MAAM,GAAG,SAAS;QAC7D,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACjE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,YAAoB,EAAE,GAAQ,EAAE,KAAK,GAAG,SAAS;QAC3D,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;CACF;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-queue.test.d.ts","sourceRoot":"","sources":["../src/sync-queue.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { Job } from './job.js';
|
|
3
|
+
import { JobRegistry } from './registry.js';
|
|
4
|
+
import { QueueManager } from './queue-manager.js';
|
|
5
|
+
import { SyncQueue } from './sync-queue.js';
|
|
6
|
+
import { QueueWorker } from './worker.js';
|
|
7
|
+
class AddJob extends Job {
|
|
8
|
+
static result = 0;
|
|
9
|
+
async handle() {
|
|
10
|
+
AddJob.result += this.data.total;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const queueConfig = {
|
|
14
|
+
default: 'sync',
|
|
15
|
+
connections: {
|
|
16
|
+
sync: { driver: 'sync' },
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
describe('SyncQueue', () => {
|
|
20
|
+
it('runs jobs immediately when dispatched', async () => {
|
|
21
|
+
AddJob.result = 0;
|
|
22
|
+
const registry = new JobRegistry().register(AddJob);
|
|
23
|
+
const worker = new QueueWorker(registry);
|
|
24
|
+
const manager = new QueueManager(queueConfig, worker);
|
|
25
|
+
const queue = manager.connection();
|
|
26
|
+
expect(queue).toBeInstanceOf(SyncQueue);
|
|
27
|
+
await queue.push(new AddJob({ total: 2 }));
|
|
28
|
+
await queue.push(new AddJob({ total: 3 }));
|
|
29
|
+
expect(AddJob.result).toBe(5);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=sync-queue.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-queue.test.js","sourceRoot":"","sources":["../src/sync-queue.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAO,SAAQ,GAAsB;IACzC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IAET,KAAK,CAAC,MAAM;QACnB,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;IACnC,CAAC;;AAGH,MAAM,WAAW,GAAgB;IAC/B,OAAO,EAAE,MAAM;IACf,WAAW,EAAE;QACX,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KACzB;CACF,CAAC;AAEF,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QAEnC,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAExC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3C,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FailedJobsConfig } from './failed-job-types.js';
|
|
2
|
+
export type QueueDriver = 'sync' | 'database';
|
|
3
|
+
export interface SyncQueueConnectionConfig {
|
|
4
|
+
driver: 'sync';
|
|
5
|
+
}
|
|
6
|
+
export interface DatabaseQueueConnectionConfig {
|
|
7
|
+
driver: 'database';
|
|
8
|
+
table?: string;
|
|
9
|
+
connection?: string;
|
|
10
|
+
retryAfter?: number;
|
|
11
|
+
}
|
|
12
|
+
export type QueueConnectionConfig = SyncQueueConnectionConfig | DatabaseQueueConnectionConfig;
|
|
13
|
+
export interface QueueConfig {
|
|
14
|
+
default: string;
|
|
15
|
+
connections: Record<string, QueueConnectionConfig>;
|
|
16
|
+
failed?: FailedJobsConfig;
|
|
17
|
+
}
|
|
18
|
+
export type { FailedJobsConfig, FailedJobRecord, RecordFailedJobInput } from './failed-job-types.js';
|
|
19
|
+
export interface SerializedJobPayload {
|
|
20
|
+
job: string;
|
|
21
|
+
data: Record<string, unknown>;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface QueueJobRecord {
|
|
25
|
+
id: number;
|
|
26
|
+
queue: string;
|
|
27
|
+
payload: string;
|
|
28
|
+
attempts: number;
|
|
29
|
+
reservedAt: number | null;
|
|
30
|
+
availableAt: number;
|
|
31
|
+
createdAt: number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9C,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,qBAAqB,GAC7B,yBAAyB,GACzB,6BAA6B,CAAC;AAElC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IACnD,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAErG,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Container } from '@tyravel/container';
|
|
2
|
+
import type { JobRegistry } from './registry.js';
|
|
3
|
+
import type { SerializedJobPayload } from './types.js';
|
|
4
|
+
export interface QueueWorkerOptions {
|
|
5
|
+
maxAttempts?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class QueueWorker {
|
|
8
|
+
private readonly registry;
|
|
9
|
+
private readonly container?;
|
|
10
|
+
private readonly options;
|
|
11
|
+
constructor(registry: JobRegistry, container?: Container | undefined, options?: QueueWorkerOptions);
|
|
12
|
+
process(payload: SerializedJobPayload): Promise<void>;
|
|
13
|
+
getMaxAttempts(): number;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAFP,QAAQ,EAAE,WAAW,EACrB,SAAS,CAAC,EAAE,SAAS,YAAA,EACrB,OAAO,GAAE,kBAAuB;IAG7C,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,cAAc,IAAI,MAAM;CAGzB"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class QueueWorker {
|
|
2
|
+
registry;
|
|
3
|
+
container;
|
|
4
|
+
options;
|
|
5
|
+
constructor(registry, container, options = {}) {
|
|
6
|
+
this.registry = registry;
|
|
7
|
+
this.container = container;
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
async process(payload) {
|
|
11
|
+
const job = this.registry.create(payload.job, payload.data);
|
|
12
|
+
if (this.container) {
|
|
13
|
+
await this.container.call(job.handle.bind(job));
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
await job.handle();
|
|
17
|
+
}
|
|
18
|
+
getMaxAttempts() {
|
|
19
|
+
return this.options.maxAttempts ?? 3;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,WAAW;IAEH;IACA;IACA;IAHnB,YACmB,QAAqB,EACrB,SAAqB,EACrB,UAA8B,EAAE;QAFhC,aAAQ,GAAR,QAAQ,CAAa;QACrB,cAAS,GAAT,SAAS,CAAY;QACrB,YAAO,GAAP,OAAO,CAAyB;IAChD,CAAC;IAEJ,KAAK,CAAC,OAAO,CAAC,OAA6B;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QACD,MAAM,GAAG,CAAC,MAAM,EAAE,CAAC;IACrB,CAAC;IAED,cAAc;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;IACvC,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tyravel/queue",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc -p tsconfig.json",
|
|
15
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@tyravel/container": "0.1.0",
|
|
19
|
+
"@tyravel/database": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.15.30"
|
|
29
|
+
},
|
|
30
|
+
"description": "Queue and background jobs for Tyravel",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/thesimonharms/tyravel.git",
|
|
35
|
+
"directory": "packages/queue"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|