@nest-native/jobs 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 (68) hide show
  1. package/README.md +115 -0
  2. package/dist/dialects/mysql/index.d.ts +2 -0
  3. package/dist/dialects/mysql/index.js +21 -0
  4. package/dist/dialects/mysql/index.js.map +1 -0
  5. package/dist/dialects/mysql/job-store.d.ts +35 -0
  6. package/dist/dialects/mysql/job-store.js +150 -0
  7. package/dist/dialects/mysql/job-store.js.map +1 -0
  8. package/dist/dialects/mysql/schema.d.ts +255 -0
  9. package/dist/dialects/mysql/schema.js +47 -0
  10. package/dist/dialects/mysql/schema.js.map +1 -0
  11. package/dist/dialects/postgres/index.d.ts +2 -0
  12. package/dist/dialects/postgres/index.js +21 -0
  13. package/dist/dialects/postgres/index.js.map +1 -0
  14. package/dist/dialects/postgres/job-store.d.ts +32 -0
  15. package/dist/dialects/postgres/job-store.js +145 -0
  16. package/dist/dialects/postgres/job-store.js.map +1 -0
  17. package/dist/dialects/postgres/schema.d.ts +262 -0
  18. package/dist/dialects/postgres/schema.js +37 -0
  19. package/dist/dialects/postgres/schema.js.map +1 -0
  20. package/dist/dialects/sqlite/index.d.ts +2 -0
  21. package/dist/dialects/sqlite/index.js +21 -0
  22. package/dist/dialects/sqlite/index.js.map +1 -0
  23. package/dist/dialects/sqlite/job-store.d.ts +32 -0
  24. package/dist/dialects/sqlite/job-store.js +155 -0
  25. package/dist/dialects/sqlite/job-store.js.map +1 -0
  26. package/dist/dialects/sqlite/schema.d.ts +279 -0
  27. package/dist/dialects/sqlite/schema.js +37 -0
  28. package/dist/dialects/sqlite/schema.js.map +1 -0
  29. package/dist/enqueue-input.d.ts +12 -0
  30. package/dist/enqueue-input.js +26 -0
  31. package/dist/enqueue-input.js.map +1 -0
  32. package/dist/errors.d.ts +18 -0
  33. package/dist/errors.js +33 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.js +30 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/interfaces.d.ts +125 -0
  39. package/dist/interfaces.js +10 -0
  40. package/dist/interfaces.js.map +1 -0
  41. package/dist/job-handler.decorator.d.ts +31 -0
  42. package/dist/job-handler.decorator.js +18 -0
  43. package/dist/job-handler.decorator.js.map +1 -0
  44. package/dist/jobs-claimer.service.d.ts +37 -0
  45. package/dist/jobs-claimer.service.js +121 -0
  46. package/dist/jobs-claimer.service.js.map +1 -0
  47. package/dist/jobs-handler.explorer.d.ts +24 -0
  48. package/dist/jobs-handler.explorer.js +67 -0
  49. package/dist/jobs-handler.explorer.js.map +1 -0
  50. package/dist/jobs-worker.d.ts +21 -0
  51. package/dist/jobs-worker.js +40 -0
  52. package/dist/jobs-worker.js.map +1 -0
  53. package/dist/jobs.module.d.ts +19 -0
  54. package/dist/jobs.module.js +49 -0
  55. package/dist/jobs.module.js.map +1 -0
  56. package/dist/jobs.service.d.ts +25 -0
  57. package/dist/jobs.service.js +55 -0
  58. package/dist/jobs.service.js.map +1 -0
  59. package/dist/testing/harness.d.ts +61 -0
  60. package/dist/testing/harness.js +86 -0
  61. package/dist/testing/harness.js.map +1 -0
  62. package/dist/testing/index.d.ts +1 -0
  63. package/dist/testing/index.js +19 -0
  64. package/dist/testing/index.js.map +1 -0
  65. package/dist/tokens.d.ts +10 -0
  66. package/dist/tokens.js +15 -0
  67. package/dist/tokens.js.map +1 -0
  68. package/package.json +107 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // @nest-native/jobs/postgres — the node-postgres (async) dialect:
18
+ // the `jobs` table definition + the Postgres store.
19
+ __exportStar(require("./schema"), exports);
20
+ __exportStar(require("./job-store"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/postgres/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kEAAkE;AAClE,oDAAoD;AACpD,2CAAyB;AACzB,8CAA4B"}
@@ -0,0 +1,32 @@
1
+ import type { EnqueueJobInput, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
2
+ /**
3
+ * Postgres surfaces a unique-constraint violation with SQLSTATE `23505`
4
+ * (`unique_violation`). node-postgres exposes it as `error.code === '23505'`;
5
+ * Drizzle wraps driver errors in a `DrizzleQueryError`, so the code may instead
6
+ * sit on `error.cause`. Check both so the predicate is robust to wrapping.
7
+ */
8
+ export declare function isPgUniqueViolation(error: unknown): boolean;
9
+ /**
10
+ * Postgres (node-postgres) job store. Every method is **asynchronous** —
11
+ * `enqueue` awaits the insert (call it with `await` inside an async
12
+ * `@Transactional` body), and the claimer's batch claim runs in an async
13
+ * transaction.
14
+ *
15
+ * A `(name, unique_key)` unique violation on `enqueue` is the active-dedup
16
+ * no-op: the store returns the EXISTING active row instead of inserting.
17
+ */
18
+ export declare class PostgresJobStore implements JobStore {
19
+ enqueue(db: unknown, input: EnqueueJobInput<object>): Promise<JobRow>;
20
+ /**
21
+ * On a unique violation with a `uniqueKey`, the ACTIVE row that owns the
22
+ * `(name, uniqueKey)` pair — terminal rows cannot own it (their key is
23
+ * cleared to NULL). Any other error, or a vanished row (the owner completed
24
+ * between the insert and this read), resolves undefined so the caller
25
+ * rethrows the original error.
26
+ */
27
+ private findActiveDuplicate;
28
+ claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
29
+ markCompleted(db: unknown, id: string): Promise<void>;
30
+ retry(db: unknown, id: string, delayMs: number, lastError?: string): Promise<void>;
31
+ markFailed(db: unknown, id: string, reason: string): Promise<void>;
32
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresJobStore = void 0;
4
+ exports.isPgUniqueViolation = isPgUniqueViolation;
5
+ const node_crypto_1 = require("node:crypto");
6
+ const drizzle_orm_1 = require("drizzle-orm");
7
+ const enqueue_input_1 = require("../../enqueue-input");
8
+ const schema_1 = require("./schema");
9
+ /**
10
+ * Postgres surfaces a unique-constraint violation with SQLSTATE `23505`
11
+ * (`unique_violation`). node-postgres exposes it as `error.code === '23505'`;
12
+ * Drizzle wraps driver errors in a `DrizzleQueryError`, so the code may instead
13
+ * sit on `error.cause`. Check both so the predicate is robust to wrapping.
14
+ */
15
+ function isPgUniqueViolation(error) {
16
+ return hasCode(error, '23505') || hasCode(error?.cause, '23505');
17
+ }
18
+ function hasCode(error, code) {
19
+ return (typeof error === 'object' &&
20
+ error !== null &&
21
+ 'code' in error &&
22
+ error.code === code);
23
+ }
24
+ /**
25
+ * Postgres (node-postgres) job store. Every method is **asynchronous** —
26
+ * `enqueue` awaits the insert (call it with `await` inside an async
27
+ * `@Transactional` body), and the claimer's batch claim runs in an async
28
+ * transaction.
29
+ *
30
+ * A `(name, unique_key)` unique violation on `enqueue` is the active-dedup
31
+ * no-op: the store returns the EXISTING active row instead of inserting.
32
+ */
33
+ class PostgresJobStore {
34
+ async enqueue(db, input) {
35
+ const availableAt = (0, enqueue_input_1.resolveAvailableAt)(input).toISOString();
36
+ try {
37
+ const [row] = await db
38
+ .insert(schema_1.jobs)
39
+ .values({
40
+ id: (0, node_crypto_1.randomUUID)(),
41
+ name: input.name,
42
+ // The one place the structural input payload widens to the stored shape.
43
+ payload: input.payload,
44
+ status: 'pending',
45
+ maxAttempts: input.maxAttempts ?? 10,
46
+ uniqueKey: input.uniqueKey ?? null,
47
+ priority: input.priority ?? 0,
48
+ availableAt,
49
+ createdAt: new Date().toISOString(),
50
+ })
51
+ .returning();
52
+ return row;
53
+ }
54
+ catch (error) {
55
+ const existing = await this.findActiveDuplicate(db, input, error);
56
+ if (!existing)
57
+ throw error;
58
+ return existing;
59
+ }
60
+ }
61
+ /**
62
+ * On a unique violation with a `uniqueKey`, the ACTIVE row that owns the
63
+ * `(name, uniqueKey)` pair — terminal rows cannot own it (their key is
64
+ * cleared to NULL). Any other error, or a vanished row (the owner completed
65
+ * between the insert and this read), resolves undefined so the caller
66
+ * rethrows the original error.
67
+ */
68
+ async findActiveDuplicate(db, input, error) {
69
+ if (!isPgUniqueViolation(error) || input.uniqueKey == null) {
70
+ return undefined;
71
+ }
72
+ const [existing] = await db
73
+ .select()
74
+ .from(schema_1.jobs)
75
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.name, input.name), (0, drizzle_orm_1.eq)(schema_1.jobs.uniqueKey, input.uniqueKey)));
76
+ return existing;
77
+ }
78
+ async claimBatch(db, cfg) {
79
+ const now = new Date();
80
+ const nowIso = now.toISOString();
81
+ const stuckCutoff = new Date(now.getTime() - cfg.stuckTimeoutMs).toISOString();
82
+ return db.transaction(async (tx) => {
83
+ const candidates = await tx
84
+ .select({ id: schema_1.jobs.id })
85
+ .from(schema_1.jobs)
86
+ .where((0, drizzle_orm_1.or)((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'pending'), (0, drizzle_orm_1.lte)(schema_1.jobs.availableAt, nowIso)), (0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobs.status, 'processing'), (0, drizzle_orm_1.lte)(schema_1.jobs.claimedAt, stuckCutoff))))
87
+ .orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt))
88
+ .limit(cfg.batchSize);
89
+ if (candidates.length === 0)
90
+ return [];
91
+ const ids = candidates.map((c) => c.id);
92
+ await tx
93
+ .update(schema_1.jobs)
94
+ .set({ status: 'processing', claimedAt: nowIso, claimedBy: cfg.workerInstanceId })
95
+ .where((0, drizzle_orm_1.inArray)(schema_1.jobs.id, ids));
96
+ return tx
97
+ .select()
98
+ .from(schema_1.jobs)
99
+ .where((0, drizzle_orm_1.inArray)(schema_1.jobs.id, ids))
100
+ .orderBy((0, drizzle_orm_1.desc)(schema_1.jobs.priority), (0, drizzle_orm_1.asc)(schema_1.jobs.availableAt));
101
+ });
102
+ }
103
+ async markCompleted(db, id) {
104
+ await db
105
+ .update(schema_1.jobs)
106
+ .set({
107
+ status: 'completed',
108
+ processedAt: new Date().toISOString(),
109
+ lastError: null,
110
+ // Terminal → release the active-dedup key.
111
+ uniqueKey: null,
112
+ })
113
+ .where((0, drizzle_orm_1.eq)(schema_1.jobs.id, id));
114
+ }
115
+ async retry(db, id, delayMs, lastError) {
116
+ const nextAvailable = new Date(Date.now() + delayMs).toISOString();
117
+ await db
118
+ .update(schema_1.jobs)
119
+ .set({
120
+ status: 'pending',
121
+ attempts: (0, drizzle_orm_1.sql) `${schema_1.jobs.attempts} + 1`,
122
+ availableAt: nextAvailable,
123
+ claimedAt: null,
124
+ claimedBy: null,
125
+ lastError: lastError ?? null,
126
+ // Still active → the uniqueKey stays claimed.
127
+ })
128
+ .where((0, drizzle_orm_1.eq)(schema_1.jobs.id, id));
129
+ }
130
+ async markFailed(db, id, reason) {
131
+ await db
132
+ .update(schema_1.jobs)
133
+ .set({
134
+ status: 'failed',
135
+ attempts: (0, drizzle_orm_1.sql) `${schema_1.jobs.attempts} + 1`,
136
+ lastError: reason,
137
+ processedAt: new Date().toISOString(),
138
+ // Terminal → release the active-dedup key.
139
+ uniqueKey: null,
140
+ })
141
+ .where((0, drizzle_orm_1.eq)(schema_1.jobs.id, id));
142
+ }
143
+ }
144
+ exports.PostgresJobStore = PostgresJobStore;
145
+ //# sourceMappingURL=job-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-store.js","sourceRoot":"","sources":["../../../dialects/postgres/job-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Postgres `jobs` table. Add it to your Drizzle schema and generate a
3
+ * migration with drizzle-kit. Timestamps are stored as ISO-8601 `text` so the
4
+ * row shape is identical across dialects (ISO-8601 compares lexicographically,
5
+ * which the claimer's `available_at` range query relies on).
6
+ *
7
+ * The **full** unique index on `(name, unique_key)` is the active-dedup
8
+ * primitive: Postgres treats `NULL`s as distinct in unique indexes (the
9
+ * default `NULLS DISTINCT`), so jobs without a `uniqueKey` never collide,
10
+ * and — because terminal transitions clear `unique_key` to `NULL` —
11
+ * uniqueness only constrains ACTIVE jobs. No partial index needed, so the
12
+ * same shape works on every dialect.
13
+ */
14
+ export declare const jobs: import("drizzle-orm/pg-core").PgTableWithColumns<{
15
+ name: "jobs";
16
+ schema: undefined;
17
+ columns: {
18
+ id: import("drizzle-orm/pg-core").PgColumn<{
19
+ name: "id";
20
+ tableName: "jobs";
21
+ dataType: "string";
22
+ columnType: "PgText";
23
+ data: string;
24
+ driverParam: string;
25
+ notNull: true;
26
+ hasDefault: false;
27
+ isPrimaryKey: true;
28
+ isAutoincrement: false;
29
+ hasRuntimeDefault: false;
30
+ enumValues: [string, ...string[]];
31
+ baseColumn: never;
32
+ identity: undefined;
33
+ generated: undefined;
34
+ }, {}, {}>;
35
+ name: import("drizzle-orm/pg-core").PgColumn<{
36
+ name: "name";
37
+ tableName: "jobs";
38
+ dataType: "string";
39
+ columnType: "PgText";
40
+ data: string;
41
+ driverParam: string;
42
+ notNull: true;
43
+ hasDefault: false;
44
+ isPrimaryKey: false;
45
+ isAutoincrement: false;
46
+ hasRuntimeDefault: false;
47
+ enumValues: [string, ...string[]];
48
+ baseColumn: never;
49
+ identity: undefined;
50
+ generated: undefined;
51
+ }, {}, {}>;
52
+ payload: import("drizzle-orm/pg-core").PgColumn<{
53
+ name: "payload";
54
+ tableName: "jobs";
55
+ dataType: "json";
56
+ columnType: "PgJsonb";
57
+ data: Record<string, unknown>;
58
+ driverParam: unknown;
59
+ notNull: true;
60
+ hasDefault: false;
61
+ isPrimaryKey: false;
62
+ isAutoincrement: false;
63
+ hasRuntimeDefault: false;
64
+ enumValues: undefined;
65
+ baseColumn: never;
66
+ identity: undefined;
67
+ generated: undefined;
68
+ }, {}, {
69
+ $type: Record<string, unknown>;
70
+ }>;
71
+ status: import("drizzle-orm/pg-core").PgColumn<{
72
+ name: "status";
73
+ tableName: "jobs";
74
+ dataType: "string";
75
+ columnType: "PgText";
76
+ data: "pending" | "processing" | "completed" | "failed";
77
+ driverParam: string;
78
+ notNull: true;
79
+ hasDefault: false;
80
+ isPrimaryKey: false;
81
+ isAutoincrement: false;
82
+ hasRuntimeDefault: false;
83
+ enumValues: [string, ...string[]];
84
+ baseColumn: never;
85
+ identity: undefined;
86
+ generated: undefined;
87
+ }, {}, {
88
+ $type: "pending" | "processing" | "completed" | "failed";
89
+ }>;
90
+ attempts: import("drizzle-orm/pg-core").PgColumn<{
91
+ name: "attempts";
92
+ tableName: "jobs";
93
+ dataType: "number";
94
+ columnType: "PgInteger";
95
+ data: number;
96
+ driverParam: string | number;
97
+ notNull: true;
98
+ hasDefault: true;
99
+ isPrimaryKey: false;
100
+ isAutoincrement: false;
101
+ hasRuntimeDefault: false;
102
+ enumValues: undefined;
103
+ baseColumn: never;
104
+ identity: undefined;
105
+ generated: undefined;
106
+ }, {}, {}>;
107
+ maxAttempts: import("drizzle-orm/pg-core").PgColumn<{
108
+ name: "max_attempts";
109
+ tableName: "jobs";
110
+ dataType: "number";
111
+ columnType: "PgInteger";
112
+ data: number;
113
+ driverParam: string | number;
114
+ notNull: true;
115
+ hasDefault: true;
116
+ isPrimaryKey: false;
117
+ isAutoincrement: false;
118
+ hasRuntimeDefault: false;
119
+ enumValues: undefined;
120
+ baseColumn: never;
121
+ identity: undefined;
122
+ generated: undefined;
123
+ }, {}, {}>;
124
+ uniqueKey: import("drizzle-orm/pg-core").PgColumn<{
125
+ name: "unique_key";
126
+ tableName: "jobs";
127
+ dataType: "string";
128
+ columnType: "PgText";
129
+ data: string;
130
+ driverParam: string;
131
+ notNull: false;
132
+ hasDefault: false;
133
+ isPrimaryKey: false;
134
+ isAutoincrement: false;
135
+ hasRuntimeDefault: false;
136
+ enumValues: [string, ...string[]];
137
+ baseColumn: never;
138
+ identity: undefined;
139
+ generated: undefined;
140
+ }, {}, {}>;
141
+ priority: import("drizzle-orm/pg-core").PgColumn<{
142
+ name: "priority";
143
+ tableName: "jobs";
144
+ dataType: "number";
145
+ columnType: "PgInteger";
146
+ data: number;
147
+ driverParam: string | number;
148
+ notNull: true;
149
+ hasDefault: true;
150
+ isPrimaryKey: false;
151
+ isAutoincrement: false;
152
+ hasRuntimeDefault: false;
153
+ enumValues: undefined;
154
+ baseColumn: never;
155
+ identity: undefined;
156
+ generated: undefined;
157
+ }, {}, {}>;
158
+ availableAt: import("drizzle-orm/pg-core").PgColumn<{
159
+ name: "available_at";
160
+ tableName: "jobs";
161
+ dataType: "string";
162
+ columnType: "PgText";
163
+ data: string;
164
+ driverParam: string;
165
+ notNull: true;
166
+ hasDefault: false;
167
+ isPrimaryKey: false;
168
+ isAutoincrement: false;
169
+ hasRuntimeDefault: false;
170
+ enumValues: [string, ...string[]];
171
+ baseColumn: never;
172
+ identity: undefined;
173
+ generated: undefined;
174
+ }, {}, {}>;
175
+ claimedAt: import("drizzle-orm/pg-core").PgColumn<{
176
+ name: "claimed_at";
177
+ tableName: "jobs";
178
+ dataType: "string";
179
+ columnType: "PgText";
180
+ data: string;
181
+ driverParam: string;
182
+ notNull: false;
183
+ hasDefault: false;
184
+ isPrimaryKey: false;
185
+ isAutoincrement: false;
186
+ hasRuntimeDefault: false;
187
+ enumValues: [string, ...string[]];
188
+ baseColumn: never;
189
+ identity: undefined;
190
+ generated: undefined;
191
+ }, {}, {}>;
192
+ claimedBy: import("drizzle-orm/pg-core").PgColumn<{
193
+ name: "claimed_by";
194
+ tableName: "jobs";
195
+ dataType: "string";
196
+ columnType: "PgText";
197
+ data: string;
198
+ driverParam: string;
199
+ notNull: false;
200
+ hasDefault: false;
201
+ isPrimaryKey: false;
202
+ isAutoincrement: false;
203
+ hasRuntimeDefault: false;
204
+ enumValues: [string, ...string[]];
205
+ baseColumn: never;
206
+ identity: undefined;
207
+ generated: undefined;
208
+ }, {}, {}>;
209
+ processedAt: import("drizzle-orm/pg-core").PgColumn<{
210
+ name: "processed_at";
211
+ tableName: "jobs";
212
+ dataType: "string";
213
+ columnType: "PgText";
214
+ data: string;
215
+ driverParam: string;
216
+ notNull: false;
217
+ hasDefault: false;
218
+ isPrimaryKey: false;
219
+ isAutoincrement: false;
220
+ hasRuntimeDefault: false;
221
+ enumValues: [string, ...string[]];
222
+ baseColumn: never;
223
+ identity: undefined;
224
+ generated: undefined;
225
+ }, {}, {}>;
226
+ lastError: import("drizzle-orm/pg-core").PgColumn<{
227
+ name: "last_error";
228
+ tableName: "jobs";
229
+ dataType: "string";
230
+ columnType: "PgText";
231
+ data: string;
232
+ driverParam: string;
233
+ notNull: false;
234
+ hasDefault: false;
235
+ isPrimaryKey: false;
236
+ isAutoincrement: false;
237
+ hasRuntimeDefault: false;
238
+ enumValues: [string, ...string[]];
239
+ baseColumn: never;
240
+ identity: undefined;
241
+ generated: undefined;
242
+ }, {}, {}>;
243
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
244
+ name: "created_at";
245
+ tableName: "jobs";
246
+ dataType: "string";
247
+ columnType: "PgText";
248
+ data: string;
249
+ driverParam: string;
250
+ notNull: true;
251
+ hasDefault: false;
252
+ isPrimaryKey: false;
253
+ isAutoincrement: false;
254
+ hasRuntimeDefault: false;
255
+ enumValues: [string, ...string[]];
256
+ baseColumn: never;
257
+ identity: undefined;
258
+ generated: undefined;
259
+ }, {}, {}>;
260
+ };
261
+ dialect: "pg";
262
+ }>;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.jobs = void 0;
4
+ const pg_core_1 = require("drizzle-orm/pg-core");
5
+ /**
6
+ * Postgres `jobs` table. Add it to your Drizzle schema and generate a
7
+ * migration with drizzle-kit. Timestamps are stored as ISO-8601 `text` so the
8
+ * row shape is identical across dialects (ISO-8601 compares lexicographically,
9
+ * which the claimer's `available_at` range query relies on).
10
+ *
11
+ * The **full** unique index on `(name, unique_key)` is the active-dedup
12
+ * primitive: Postgres treats `NULL`s as distinct in unique indexes (the
13
+ * default `NULLS DISTINCT`), so jobs without a `uniqueKey` never collide,
14
+ * and — because terminal transitions clear `unique_key` to `NULL` —
15
+ * uniqueness only constrains ACTIVE jobs. No partial index needed, so the
16
+ * same shape works on every dialect.
17
+ */
18
+ exports.jobs = (0, pg_core_1.pgTable)('jobs', {
19
+ id: (0, pg_core_1.text)('id').primaryKey(),
20
+ name: (0, pg_core_1.text)('name').notNull(),
21
+ payload: (0, pg_core_1.jsonb)('payload').$type().notNull(),
22
+ status: (0, pg_core_1.text)('status').$type().notNull(),
23
+ attempts: (0, pg_core_1.integer)('attempts').notNull().default(0),
24
+ maxAttempts: (0, pg_core_1.integer)('max_attempts').notNull().default(10),
25
+ uniqueKey: (0, pg_core_1.text)('unique_key'),
26
+ priority: (0, pg_core_1.integer)('priority').notNull().default(0),
27
+ availableAt: (0, pg_core_1.text)('available_at').notNull(),
28
+ claimedAt: (0, pg_core_1.text)('claimed_at'),
29
+ claimedBy: (0, pg_core_1.text)('claimed_by'),
30
+ processedAt: (0, pg_core_1.text)('processed_at'),
31
+ lastError: (0, pg_core_1.text)('last_error'),
32
+ createdAt: (0, pg_core_1.text)('created_at').notNull(),
33
+ }, (table) => [
34
+ (0, pg_core_1.uniqueIndex)('jobs_name_unique_key_unique').on(table.name, table.uniqueKey),
35
+ (0, pg_core_1.index)('jobs_status_available_idx').on(table.status, table.availableAt),
36
+ ]);
37
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../dialects/postgres/schema.ts"],"names":[],"mappings":";;;AAAA,iDAO6B;AAG7B;;;;;;;;;;;;GAYG;AACU,QAAA,IAAI,GAAG,IAAA,iBAAO,EACzB,MAAM,EACN;IACE,EAAE,EAAE,IAAA,cAAI,EAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,IAAI,EAAE,IAAA,cAAI,EAAC,MAAM,CAAC,CAAC,OAAO,EAAE;IAC5B,OAAO,EAAE,IAAA,eAAK,EAAC,SAAS,CAAC,CAAC,KAAK,EAA2B,CAAC,OAAO,EAAE;IACpE,MAAM,EAAE,IAAA,cAAI,EAAC,QAAQ,CAAC,CAAC,KAAK,EAAa,CAAC,OAAO,EAAE;IACnD,QAAQ,EAAE,IAAA,iBAAO,EAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,WAAW,EAAE,IAAA,iBAAO,EAAC,cAAc,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1D,SAAS,EAAE,IAAA,cAAI,EAAC,YAAY,CAAC;IAC7B,QAAQ,EAAE,IAAA,iBAAO,EAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,WAAW,EAAE,IAAA,cAAI,EAAC,cAAc,CAAC,CAAC,OAAO,EAAE;IAC3C,SAAS,EAAE,IAAA,cAAI,EAAC,YAAY,CAAC;IAC7B,SAAS,EAAE,IAAA,cAAI,EAAC,YAAY,CAAC;IAC7B,WAAW,EAAE,IAAA,cAAI,EAAC,cAAc,CAAC;IACjC,SAAS,EAAE,IAAA,cAAI,EAAC,YAAY,CAAC;IAC7B,SAAS,EAAE,IAAA,cAAI,EAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CACxC,EACD,CAAC,KAAK,EAAE,EAAE,CAAC;IACT,IAAA,qBAAW,EAAC,6BAA6B,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1E,IAAA,eAAK,EAAC,2BAA2B,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC;CACvE,CACF,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './schema';
2
+ export * from './job-store';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ // @nest-native/jobs/sqlite — the better-sqlite3 (synchronous) dialect:
18
+ // the `jobs` table definition + the SQLite store.
19
+ __exportStar(require("./schema"), exports);
20
+ __exportStar(require("./job-store"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/sqlite/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uEAAuE;AACvE,kDAAkD;AAClD,2CAAyB;AACzB,8CAA4B"}
@@ -0,0 +1,32 @@
1
+ import type { EnqueueJobInput, JobRow, JobStore, ResolvedRunnerConfig } from '../../interfaces';
2
+ /**
3
+ * better-sqlite3 surfaces a unique-constraint violation as a `SqliteError` with
4
+ * `code === 'SQLITE_CONSTRAINT_UNIQUE'`. Match on the code (stable across driver
5
+ * versions), not the message. Drizzle may wrap driver errors in a
6
+ * `DrizzleQueryError`, so the code may instead sit on `error.cause` — check both.
7
+ */
8
+ export declare function isSqliteUniqueViolation(error: unknown): boolean;
9
+ /**
10
+ * SQLite (better-sqlite3) job store. Every method runs **synchronously** —
11
+ * `enqueue` returns the row directly so it composes inside a synchronous
12
+ * `@Transactional` body, and the rest wrap their synchronous result in a
13
+ * resolved Promise for the engine to await from outside the transaction.
14
+ *
15
+ * A `(name, unique_key)` unique violation on `enqueue` is the active-dedup
16
+ * no-op: the store returns the EXISTING active row instead of inserting.
17
+ */
18
+ export declare class SqliteJobStore implements JobStore {
19
+ enqueue(db: unknown, input: EnqueueJobInput<object>): JobRow;
20
+ /**
21
+ * On a unique violation with a `uniqueKey`, the ACTIVE row that owns the
22
+ * `(name, uniqueKey)` pair — terminal rows cannot own it (their key is
23
+ * cleared to NULL). Any other error, or a vanished row (the owner completed
24
+ * between the insert and this read), returns undefined so the caller
25
+ * rethrows the original error.
26
+ */
27
+ private findActiveDuplicate;
28
+ claimBatch(db: unknown, cfg: ResolvedRunnerConfig): Promise<JobRow[]>;
29
+ markCompleted(db: unknown, id: string): Promise<void>;
30
+ retry(db: unknown, id: string, delayMs: number, lastError?: string): Promise<void>;
31
+ markFailed(db: unknown, id: string, reason: string): Promise<void>;
32
+ }