@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.
Files changed (69) hide show
  1. package/dist/database-queue.d.ts +20 -0
  2. package/dist/database-queue.d.ts.map +1 -0
  3. package/dist/database-queue.js +101 -0
  4. package/dist/database-queue.js.map +1 -0
  5. package/dist/database-queue.test.d.ts +2 -0
  6. package/dist/database-queue.test.d.ts.map +1 -0
  7. package/dist/database-queue.test.js +74 -0
  8. package/dist/database-queue.test.js.map +1 -0
  9. package/dist/dispatcher.d.ts +9 -0
  10. package/dist/dispatcher.d.ts.map +1 -0
  11. package/dist/dispatcher.js +13 -0
  12. package/dist/dispatcher.js.map +1 -0
  13. package/dist/failed-job-repository.d.ts +17 -0
  14. package/dist/failed-job-repository.d.ts.map +1 -0
  15. package/dist/failed-job-repository.js +74 -0
  16. package/dist/failed-job-repository.js.map +1 -0
  17. package/dist/failed-job-repository.test.d.ts +2 -0
  18. package/dist/failed-job-repository.test.d.ts.map +1 -0
  19. package/dist/failed-job-repository.test.js +75 -0
  20. package/dist/failed-job-repository.test.js.map +1 -0
  21. package/dist/failed-job-types.d.ts +21 -0
  22. package/dist/failed-job-types.d.ts.map +1 -0
  23. package/dist/failed-job-types.js +2 -0
  24. package/dist/failed-job-types.js.map +1 -0
  25. package/dist/index.d.ts +31 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +23 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/job.d.ts +8 -0
  30. package/dist/job.d.ts.map +1 -0
  31. package/dist/job.js +13 -0
  32. package/dist/job.js.map +1 -0
  33. package/dist/payload.d.ts +6 -0
  34. package/dist/payload.d.ts.map +1 -0
  35. package/dist/payload.js +18 -0
  36. package/dist/payload.js.map +1 -0
  37. package/dist/queue-contract.d.ts +8 -0
  38. package/dist/queue-contract.d.ts.map +1 -0
  39. package/dist/queue-contract.js +2 -0
  40. package/dist/queue-contract.js.map +1 -0
  41. package/dist/queue-manager.d.ts +15 -0
  42. package/dist/queue-manager.d.ts.map +1 -0
  43. package/dist/queue-manager.js +46 -0
  44. package/dist/queue-manager.js.map +1 -0
  45. package/dist/queue-processor.d.ts +22 -0
  46. package/dist/queue-processor.d.ts.map +1 -0
  47. package/dist/queue-processor.js +65 -0
  48. package/dist/queue-processor.js.map +1 -0
  49. package/dist/registry.d.ts +12 -0
  50. package/dist/registry.d.ts.map +1 -0
  51. package/dist/registry.js +24 -0
  52. package/dist/registry.js.map +1 -0
  53. package/dist/sync-queue.d.ts +12 -0
  54. package/dist/sync-queue.d.ts.map +1 -0
  55. package/dist/sync-queue.js +26 -0
  56. package/dist/sync-queue.js.map +1 -0
  57. package/dist/sync-queue.test.d.ts +2 -0
  58. package/dist/sync-queue.test.d.ts.map +1 -0
  59. package/dist/sync-queue.test.js +32 -0
  60. package/dist/sync-queue.test.js.map +1 -0
  61. package/dist/types.d.ts +33 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +2 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/worker.d.ts +15 -0
  66. package/dist/worker.d.ts.map +1 -0
  67. package/dist/worker.js +22 -0
  68. package/dist/worker.js.map +1 -0
  69. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=database-queue.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=failed-job-repository.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=failed-job-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failed-job-types.js","sourceRoot":"","sources":["../src/failed-job-types.ts"],"names":[],"mappings":""}
@@ -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
@@ -0,0 +1,13 @@
1
+ export class Job {
2
+ data;
3
+ constructor(data) {
4
+ this.data = data;
5
+ }
6
+ jobName() {
7
+ return this.constructor.name;
8
+ }
9
+ displayName() {
10
+ return this.jobName();
11
+ }
12
+ }
13
+ //# sourceMappingURL=job.js.map
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=queue-contract.js.map
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sync-queue.test.d.ts.map
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -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
+ }