@nest-native/jobs 0.1.0 → 0.2.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 (53) hide show
  1. package/README.md +33 -5
  2. package/dist/dialects/mysql/index.d.ts +1 -0
  3. package/dist/dialects/mysql/index.js +1 -0
  4. package/dist/dialects/mysql/index.js.map +1 -1
  5. package/dist/dialects/mysql/schedule-store.d.ts +36 -0
  6. package/dist/dialects/mysql/schedule-store.js +185 -0
  7. package/dist/dialects/mysql/schedule-store.js.map +1 -0
  8. package/dist/dialects/mysql/schema.d.ts +274 -0
  9. package/dist/dialects/mysql/schema.js +31 -1
  10. package/dist/dialects/mysql/schema.js.map +1 -1
  11. package/dist/dialects/postgres/index.d.ts +1 -0
  12. package/dist/dialects/postgres/index.js +1 -0
  13. package/dist/dialects/postgres/index.js.map +1 -1
  14. package/dist/dialects/postgres/schedule-store.d.ts +22 -0
  15. package/dist/dialects/postgres/schedule-store.js +143 -0
  16. package/dist/dialects/postgres/schedule-store.js.map +1 -0
  17. package/dist/dialects/postgres/schema.d.ts +273 -0
  18. package/dist/dialects/postgres/schema.js +30 -1
  19. package/dist/dialects/postgres/schema.js.map +1 -1
  20. package/dist/dialects/sqlite/index.d.ts +1 -0
  21. package/dist/dialects/sqlite/index.js +2 -1
  22. package/dist/dialects/sqlite/index.js.map +1 -1
  23. package/dist/dialects/sqlite/schedule-store.d.ts +23 -0
  24. package/dist/dialects/sqlite/schedule-store.js +153 -0
  25. package/dist/dialects/sqlite/schedule-store.js.map +1 -0
  26. package/dist/dialects/sqlite/schema.d.ts +295 -0
  27. package/dist/dialects/sqlite/schema.js +32 -1
  28. package/dist/dialects/sqlite/schema.js.map +1 -1
  29. package/dist/errors.d.ts +10 -0
  30. package/dist/errors.js +15 -1
  31. package/dist/errors.js.map +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.js +2 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/interfaces.d.ts +137 -0
  36. package/dist/job-schedules.service.d.ts +44 -0
  37. package/dist/job-schedules.service.js +125 -0
  38. package/dist/job-schedules.service.js.map +1 -0
  39. package/dist/jobs-claimer.service.d.ts +16 -2
  40. package/dist/jobs-claimer.service.js +71 -2
  41. package/dist/jobs-claimer.service.js.map +1 -1
  42. package/dist/jobs.module.d.ts +3 -1
  43. package/dist/jobs.module.js +17 -2
  44. package/dist/jobs.module.js.map +1 -1
  45. package/dist/schedule-planner.d.ts +30 -0
  46. package/dist/schedule-planner.js +58 -0
  47. package/dist/schedule-planner.js.map +1 -0
  48. package/dist/testing/harness.js +2 -1
  49. package/dist/testing/harness.js.map +1 -1
  50. package/dist/tokens.d.ts +5 -0
  51. package/dist/tokens.js +6 -1
  52. package/dist/tokens.js.map +1 -1
  53. package/package.json +6 -4
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/postgres/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kEAAkE;AAClE,oDAAoD;AACpD,2CAAyB;AACzB,8CAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/postgres/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kEAAkE;AAClE,oDAAoD;AACpD,2CAAyB;AACzB,8CAA4B;AAC5B,mDAAiC"}
@@ -0,0 +1,22 @@
1
+ import type { ResolvedScheduleUpsert, ScheduleClaim, ScheduleClaimResult, ScheduleRow, ScheduleStore } from '../../interfaces';
2
+ /**
3
+ * Postgres (node-postgres) schedule store. Every method is **asynchronous**,
4
+ * mirroring {@link PostgresJobStore}.
5
+ *
6
+ * `claimAndEnqueue` runs the compare-and-swap on `next_run_at` and the
7
+ * occurrence insert in ONE transaction. The insert uses
8
+ * `ON CONFLICT DO NOTHING` on `(name, unique_key)` — crucial on Postgres,
9
+ * where a raised unique violation would poison the whole transaction; the
10
+ * conflict-tolerant insert lets the schedule row advance even when the
11
+ * overlap guard suppresses the occurrence.
12
+ */
13
+ export declare class PostgresScheduleStore implements ScheduleStore {
14
+ upsert(db: unknown, input: ResolvedScheduleUpsert): Promise<ScheduleRow>;
15
+ get(db: unknown, name: string): Promise<ScheduleRow | undefined>;
16
+ list(db: unknown): Promise<ScheduleRow[]>;
17
+ remove(db: unknown, name: string): Promise<boolean>;
18
+ setEnabled(db: unknown, name: string, enabled: boolean, nextRunAt: string | null, expectedUpdatedAt?: string): Promise<ScheduleRow | undefined>;
19
+ listDue(db: unknown, nowIso: string, limit: number): Promise<ScheduleRow[]>;
20
+ claimAndEnqueue(db: unknown, claim: ScheduleClaim): Promise<ScheduleClaimResult>;
21
+ disable(db: unknown, id: string, lastError: string): Promise<void>;
22
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresScheduleStore = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const drizzle_orm_1 = require("drizzle-orm");
6
+ const schema_1 = require("./schema");
7
+ /**
8
+ * Postgres (node-postgres) schedule store. Every method is **asynchronous**,
9
+ * mirroring {@link PostgresJobStore}.
10
+ *
11
+ * `claimAndEnqueue` runs the compare-and-swap on `next_run_at` and the
12
+ * occurrence insert in ONE transaction. The insert uses
13
+ * `ON CONFLICT DO NOTHING` on `(name, unique_key)` — crucial on Postgres,
14
+ * where a raised unique violation would poison the whole transaction; the
15
+ * conflict-tolerant insert lets the schedule row advance even when the
16
+ * overlap guard suppresses the occurrence.
17
+ */
18
+ class PostgresScheduleStore {
19
+ async upsert(db, input) {
20
+ const nowIso = new Date().toISOString();
21
+ const [row] = await db
22
+ .insert(schema_1.jobSchedules)
23
+ .values({
24
+ id: (0, node_crypto_1.randomUUID)(),
25
+ ...input,
26
+ enabled: input.enabled ?? true,
27
+ lastEnqueuedAt: null,
28
+ lastError: null,
29
+ createdAt: nowIso,
30
+ updatedAt: nowIso,
31
+ })
32
+ .onConflictDoUpdate({
33
+ target: schema_1.jobSchedules.name,
34
+ // Updates keep id / createdAt / lastEnqueuedAt; a successful upsert
35
+ // clears any stale claim-time error. Two deliberate preservations
36
+ // make boot-time upserts safe: an omitted `enabled` (null) keeps the
37
+ // stored flag (ops kill switches survive redeploys), and the stored
38
+ // `next_run_at` survives while cron/timezone are unchanged (a pending
39
+ // catch-up is not skipped; the rhythm is not perturbed).
40
+ set: {
41
+ nextRunAt: (0, drizzle_orm_1.sql) `CASE WHEN ${schema_1.jobSchedules.cron} = excluded.cron AND ${schema_1.jobSchedules.timezone} IS NOT DISTINCT FROM excluded.timezone AND ${schema_1.jobSchedules.nextRunAt} IS NOT NULL THEN ${schema_1.jobSchedules.nextRunAt} ELSE excluded.next_run_at END`,
42
+ jobName: input.jobName,
43
+ payload: input.payload,
44
+ cron: input.cron,
45
+ timezone: input.timezone,
46
+ maxAttempts: input.maxAttempts,
47
+ priority: input.priority,
48
+ uniqueKey: input.uniqueKey,
49
+ lastError: null,
50
+ updatedAt: nowIso,
51
+ ...(input.enabled !== null && { enabled: input.enabled }),
52
+ },
53
+ })
54
+ .returning();
55
+ return row;
56
+ }
57
+ async get(db, name) {
58
+ const rows = await db
59
+ .select()
60
+ .from(schema_1.jobSchedules)
61
+ .where((0, drizzle_orm_1.eq)(schema_1.jobSchedules.name, name));
62
+ return rows[0];
63
+ }
64
+ async list(db) {
65
+ return db
66
+ .select()
67
+ .from(schema_1.jobSchedules)
68
+ .orderBy((0, drizzle_orm_1.asc)(schema_1.jobSchedules.name));
69
+ }
70
+ async remove(db, name) {
71
+ const rows = await db
72
+ .delete(schema_1.jobSchedules)
73
+ .where((0, drizzle_orm_1.eq)(schema_1.jobSchedules.name, name))
74
+ .returning({ id: schema_1.jobSchedules.id });
75
+ return rows.length > 0;
76
+ }
77
+ async setEnabled(db, name, enabled, nextRunAt, expectedUpdatedAt) {
78
+ const rows = await db
79
+ .update(schema_1.jobSchedules)
80
+ .set({ enabled, nextRunAt, updatedAt: new Date().toISOString() })
81
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobSchedules.name, name),
82
+ // Optimistic guard: only write over the row version the caller read.
83
+ ...(expectedUpdatedAt === undefined
84
+ ? []
85
+ : [(0, drizzle_orm_1.eq)(schema_1.jobSchedules.updatedAt, expectedUpdatedAt)])))
86
+ .returning();
87
+ return rows[0];
88
+ }
89
+ async listDue(db, nowIso, limit) {
90
+ return db
91
+ .select()
92
+ .from(schema_1.jobSchedules)
93
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobSchedules.enabled, true), (0, drizzle_orm_1.isNotNull)(schema_1.jobSchedules.nextRunAt), (0, drizzle_orm_1.lte)(schema_1.jobSchedules.nextRunAt, nowIso)))
94
+ .orderBy((0, drizzle_orm_1.asc)(schema_1.jobSchedules.nextRunAt))
95
+ .limit(limit);
96
+ }
97
+ async claimAndEnqueue(db, claim) {
98
+ return db.transaction(async (tx) => {
99
+ const cas = await tx
100
+ .update(schema_1.jobSchedules)
101
+ .set({
102
+ nextRunAt: claim.nextRunAt,
103
+ // A schedule with no future occurrence disables itself.
104
+ enabled: claim.nextRunAt !== null,
105
+ lastEnqueuedAt: claim.nowIso,
106
+ lastError: null,
107
+ updatedAt: claim.nowIso,
108
+ })
109
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema_1.jobSchedules.id, claim.id), (0, drizzle_orm_1.eq)(schema_1.jobSchedules.nextRunAt, claim.expectedNextRunAt), (0, drizzle_orm_1.eq)(schema_1.jobSchedules.enabled, true)))
110
+ .returning({ id: schema_1.jobSchedules.id });
111
+ if (cas.length === 0) {
112
+ return { claimed: false, job: null };
113
+ }
114
+ const inserted = await tx
115
+ .insert(schema_1.jobs)
116
+ .values({
117
+ id: (0, node_crypto_1.randomUUID)(),
118
+ name: claim.input.name,
119
+ payload: claim.input.payload,
120
+ status: 'pending',
121
+ maxAttempts: claim.input.maxAttempts ?? 10,
122
+ uniqueKey: claim.input.uniqueKey ?? null,
123
+ priority: claim.input.priority ?? 0,
124
+ availableAt: claim.nowIso,
125
+ createdAt: claim.nowIso,
126
+ })
127
+ .onConflictDoNothing({ target: [schema_1.jobs.name, schema_1.jobs.uniqueKey] })
128
+ .returning();
129
+ // An empty RETURNING means the overlap guard suppressed the insert (an
130
+ // occurrence with this (name, unique_key) is still active) — the
131
+ // schedule still advanced; there is just no new job.
132
+ return { claimed: true, job: inserted.length > 0 ? inserted[0] : null };
133
+ });
134
+ }
135
+ async disable(db, id, lastError) {
136
+ await db
137
+ .update(schema_1.jobSchedules)
138
+ .set({ enabled: false, lastError, updatedAt: new Date().toISOString() })
139
+ .where((0, drizzle_orm_1.eq)(schema_1.jobSchedules.id, id));
140
+ }
141
+ }
142
+ exports.PostgresScheduleStore = PostgresScheduleStore;
143
+ //# sourceMappingURL=schedule-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-store.js","sourceRoot":"","sources":["../../../dialects/postgres/schedule-store.ts"],"names":[],"mappings":"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"}
@@ -260,3 +260,276 @@ export declare const jobs: import("drizzle-orm/pg-core").PgTableWithColumns<{
260
260
  };
261
261
  dialect: "pg";
262
262
  }>;
263
+ /**
264
+ * Postgres `job_schedules` table. Add it to your Drizzle schema (alongside
265
+ * `jobs`) and generate a migration with drizzle-kit — only needed when you
266
+ * opt in to schedules.
267
+ *
268
+ * `name` is the unique schedule identity (upserts key on it); the
269
+ * `(enabled, next_run_at)` index serves the claimer's due query. Timestamps
270
+ * are ISO-8601 strings, like every timestamp in this package.
271
+ */
272
+ export declare const jobSchedules: import("drizzle-orm/pg-core").PgTableWithColumns<{
273
+ name: "job_schedules";
274
+ schema: undefined;
275
+ columns: {
276
+ id: import("drizzle-orm/pg-core").PgColumn<{
277
+ name: "id";
278
+ tableName: "job_schedules";
279
+ dataType: "string";
280
+ columnType: "PgText";
281
+ data: string;
282
+ driverParam: string;
283
+ notNull: true;
284
+ hasDefault: false;
285
+ isPrimaryKey: true;
286
+ isAutoincrement: false;
287
+ hasRuntimeDefault: false;
288
+ enumValues: [string, ...string[]];
289
+ baseColumn: never;
290
+ identity: undefined;
291
+ generated: undefined;
292
+ }, {}, {}>;
293
+ name: import("drizzle-orm/pg-core").PgColumn<{
294
+ name: "name";
295
+ tableName: "job_schedules";
296
+ dataType: "string";
297
+ columnType: "PgText";
298
+ data: string;
299
+ driverParam: string;
300
+ notNull: true;
301
+ hasDefault: false;
302
+ isPrimaryKey: false;
303
+ isAutoincrement: false;
304
+ hasRuntimeDefault: false;
305
+ enumValues: [string, ...string[]];
306
+ baseColumn: never;
307
+ identity: undefined;
308
+ generated: undefined;
309
+ }, {}, {}>;
310
+ jobName: import("drizzle-orm/pg-core").PgColumn<{
311
+ name: "job_name";
312
+ tableName: "job_schedules";
313
+ dataType: "string";
314
+ columnType: "PgText";
315
+ data: string;
316
+ driverParam: string;
317
+ notNull: true;
318
+ hasDefault: false;
319
+ isPrimaryKey: false;
320
+ isAutoincrement: false;
321
+ hasRuntimeDefault: false;
322
+ enumValues: [string, ...string[]];
323
+ baseColumn: never;
324
+ identity: undefined;
325
+ generated: undefined;
326
+ }, {}, {}>;
327
+ payload: import("drizzle-orm/pg-core").PgColumn<{
328
+ name: "payload";
329
+ tableName: "job_schedules";
330
+ dataType: "json";
331
+ columnType: "PgJsonb";
332
+ data: Record<string, unknown>;
333
+ driverParam: unknown;
334
+ notNull: true;
335
+ hasDefault: false;
336
+ isPrimaryKey: false;
337
+ isAutoincrement: false;
338
+ hasRuntimeDefault: false;
339
+ enumValues: undefined;
340
+ baseColumn: never;
341
+ identity: undefined;
342
+ generated: undefined;
343
+ }, {}, {
344
+ $type: Record<string, unknown>;
345
+ }>;
346
+ cron: import("drizzle-orm/pg-core").PgColumn<{
347
+ name: "cron";
348
+ tableName: "job_schedules";
349
+ dataType: "string";
350
+ columnType: "PgText";
351
+ data: string;
352
+ driverParam: string;
353
+ notNull: true;
354
+ hasDefault: false;
355
+ isPrimaryKey: false;
356
+ isAutoincrement: false;
357
+ hasRuntimeDefault: false;
358
+ enumValues: [string, ...string[]];
359
+ baseColumn: never;
360
+ identity: undefined;
361
+ generated: undefined;
362
+ }, {}, {}>;
363
+ timezone: import("drizzle-orm/pg-core").PgColumn<{
364
+ name: "timezone";
365
+ tableName: "job_schedules";
366
+ dataType: "string";
367
+ columnType: "PgText";
368
+ data: string;
369
+ driverParam: string;
370
+ notNull: false;
371
+ hasDefault: false;
372
+ isPrimaryKey: false;
373
+ isAutoincrement: false;
374
+ hasRuntimeDefault: false;
375
+ enumValues: [string, ...string[]];
376
+ baseColumn: never;
377
+ identity: undefined;
378
+ generated: undefined;
379
+ }, {}, {}>;
380
+ enabled: import("drizzle-orm/pg-core").PgColumn<{
381
+ name: "enabled";
382
+ tableName: "job_schedules";
383
+ dataType: "boolean";
384
+ columnType: "PgBoolean";
385
+ data: boolean;
386
+ driverParam: boolean;
387
+ notNull: true;
388
+ hasDefault: true;
389
+ isPrimaryKey: false;
390
+ isAutoincrement: false;
391
+ hasRuntimeDefault: false;
392
+ enumValues: undefined;
393
+ baseColumn: never;
394
+ identity: undefined;
395
+ generated: undefined;
396
+ }, {}, {}>;
397
+ nextRunAt: import("drizzle-orm/pg-core").PgColumn<{
398
+ name: "next_run_at";
399
+ tableName: "job_schedules";
400
+ dataType: "string";
401
+ columnType: "PgText";
402
+ data: string;
403
+ driverParam: string;
404
+ notNull: false;
405
+ hasDefault: false;
406
+ isPrimaryKey: false;
407
+ isAutoincrement: false;
408
+ hasRuntimeDefault: false;
409
+ enumValues: [string, ...string[]];
410
+ baseColumn: never;
411
+ identity: undefined;
412
+ generated: undefined;
413
+ }, {}, {}>;
414
+ maxAttempts: import("drizzle-orm/pg-core").PgColumn<{
415
+ name: "max_attempts";
416
+ tableName: "job_schedules";
417
+ dataType: "number";
418
+ columnType: "PgInteger";
419
+ data: number;
420
+ driverParam: string | number;
421
+ notNull: false;
422
+ hasDefault: false;
423
+ isPrimaryKey: false;
424
+ isAutoincrement: false;
425
+ hasRuntimeDefault: false;
426
+ enumValues: undefined;
427
+ baseColumn: never;
428
+ identity: undefined;
429
+ generated: undefined;
430
+ }, {}, {}>;
431
+ priority: import("drizzle-orm/pg-core").PgColumn<{
432
+ name: "priority";
433
+ tableName: "job_schedules";
434
+ dataType: "number";
435
+ columnType: "PgInteger";
436
+ data: number;
437
+ driverParam: string | number;
438
+ notNull: false;
439
+ hasDefault: false;
440
+ isPrimaryKey: false;
441
+ isAutoincrement: false;
442
+ hasRuntimeDefault: false;
443
+ enumValues: undefined;
444
+ baseColumn: never;
445
+ identity: undefined;
446
+ generated: undefined;
447
+ }, {}, {}>;
448
+ uniqueKey: import("drizzle-orm/pg-core").PgColumn<{
449
+ name: "unique_key";
450
+ tableName: "job_schedules";
451
+ dataType: "string";
452
+ columnType: "PgText";
453
+ data: string;
454
+ driverParam: string;
455
+ notNull: false;
456
+ hasDefault: false;
457
+ isPrimaryKey: false;
458
+ isAutoincrement: false;
459
+ hasRuntimeDefault: false;
460
+ enumValues: [string, ...string[]];
461
+ baseColumn: never;
462
+ identity: undefined;
463
+ generated: undefined;
464
+ }, {}, {}>;
465
+ lastEnqueuedAt: import("drizzle-orm/pg-core").PgColumn<{
466
+ name: "last_enqueued_at";
467
+ tableName: "job_schedules";
468
+ dataType: "string";
469
+ columnType: "PgText";
470
+ data: string;
471
+ driverParam: string;
472
+ notNull: false;
473
+ hasDefault: false;
474
+ isPrimaryKey: false;
475
+ isAutoincrement: false;
476
+ hasRuntimeDefault: false;
477
+ enumValues: [string, ...string[]];
478
+ baseColumn: never;
479
+ identity: undefined;
480
+ generated: undefined;
481
+ }, {}, {}>;
482
+ lastError: import("drizzle-orm/pg-core").PgColumn<{
483
+ name: "last_error";
484
+ tableName: "job_schedules";
485
+ dataType: "string";
486
+ columnType: "PgText";
487
+ data: string;
488
+ driverParam: string;
489
+ notNull: false;
490
+ hasDefault: false;
491
+ isPrimaryKey: false;
492
+ isAutoincrement: false;
493
+ hasRuntimeDefault: false;
494
+ enumValues: [string, ...string[]];
495
+ baseColumn: never;
496
+ identity: undefined;
497
+ generated: undefined;
498
+ }, {}, {}>;
499
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
500
+ name: "created_at";
501
+ tableName: "job_schedules";
502
+ dataType: "string";
503
+ columnType: "PgText";
504
+ data: string;
505
+ driverParam: string;
506
+ notNull: true;
507
+ hasDefault: false;
508
+ isPrimaryKey: false;
509
+ isAutoincrement: false;
510
+ hasRuntimeDefault: false;
511
+ enumValues: [string, ...string[]];
512
+ baseColumn: never;
513
+ identity: undefined;
514
+ generated: undefined;
515
+ }, {}, {}>;
516
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
517
+ name: "updated_at";
518
+ tableName: "job_schedules";
519
+ dataType: "string";
520
+ columnType: "PgText";
521
+ data: string;
522
+ driverParam: string;
523
+ notNull: true;
524
+ hasDefault: false;
525
+ isPrimaryKey: false;
526
+ isAutoincrement: false;
527
+ hasRuntimeDefault: false;
528
+ enumValues: [string, ...string[]];
529
+ baseColumn: never;
530
+ identity: undefined;
531
+ generated: undefined;
532
+ }, {}, {}>;
533
+ };
534
+ dialect: "pg";
535
+ }>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.jobs = void 0;
3
+ exports.jobSchedules = exports.jobs = void 0;
4
4
  const pg_core_1 = require("drizzle-orm/pg-core");
5
5
  /**
6
6
  * Postgres `jobs` table. Add it to your Drizzle schema and generate a
@@ -34,4 +34,33 @@ exports.jobs = (0, pg_core_1.pgTable)('jobs', {
34
34
  (0, pg_core_1.uniqueIndex)('jobs_name_unique_key_unique').on(table.name, table.uniqueKey),
35
35
  (0, pg_core_1.index)('jobs_status_available_idx').on(table.status, table.availableAt),
36
36
  ]);
37
+ /**
38
+ * Postgres `job_schedules` table. Add it to your Drizzle schema (alongside
39
+ * `jobs`) and generate a migration with drizzle-kit — only needed when you
40
+ * opt in to schedules.
41
+ *
42
+ * `name` is the unique schedule identity (upserts key on it); the
43
+ * `(enabled, next_run_at)` index serves the claimer's due query. Timestamps
44
+ * are ISO-8601 strings, like every timestamp in this package.
45
+ */
46
+ exports.jobSchedules = (0, pg_core_1.pgTable)('job_schedules', {
47
+ id: (0, pg_core_1.text)('id').primaryKey(),
48
+ name: (0, pg_core_1.text)('name').notNull(),
49
+ jobName: (0, pg_core_1.text)('job_name').notNull(),
50
+ payload: (0, pg_core_1.jsonb)('payload').$type().notNull(),
51
+ cron: (0, pg_core_1.text)('cron').notNull(),
52
+ timezone: (0, pg_core_1.text)('timezone'),
53
+ enabled: (0, pg_core_1.boolean)('enabled').notNull().default(true),
54
+ nextRunAt: (0, pg_core_1.text)('next_run_at'),
55
+ maxAttempts: (0, pg_core_1.integer)('max_attempts'),
56
+ priority: (0, pg_core_1.integer)('priority'),
57
+ uniqueKey: (0, pg_core_1.text)('unique_key'),
58
+ lastEnqueuedAt: (0, pg_core_1.text)('last_enqueued_at'),
59
+ lastError: (0, pg_core_1.text)('last_error'),
60
+ createdAt: (0, pg_core_1.text)('created_at').notNull(),
61
+ updatedAt: (0, pg_core_1.text)('updated_at').notNull(),
62
+ }, (table) => [
63
+ (0, pg_core_1.uniqueIndex)('job_schedules_name_unique').on(table.name),
64
+ (0, pg_core_1.index)('job_schedules_enabled_next_run_idx').on(table.enabled, table.nextRunAt),
65
+ ]);
37
66
  //# sourceMappingURL=schema.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../dialects/postgres/schema.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  export * from './schema';
2
2
  export * from './job-store';
3
+ export * from './schedule-store';
@@ -15,7 +15,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  // @nest-native/jobs/sqlite — the better-sqlite3 (synchronous) dialect:
18
- // the `jobs` table definition + the SQLite store.
18
+ // the `jobs` + `job_schedules` table definitions + the SQLite stores.
19
19
  __exportStar(require("./schema"), exports);
20
20
  __exportStar(require("./job-store"), exports);
21
+ __exportStar(require("./schedule-store"), exports);
21
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/sqlite/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uEAAuE;AACvE,kDAAkD;AAClD,2CAAyB;AACzB,8CAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../dialects/sqlite/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,uEAAuE;AACvE,sEAAsE;AACtE,2CAAyB;AACzB,8CAA4B;AAC5B,mDAAiC"}
@@ -0,0 +1,23 @@
1
+ import type { ResolvedScheduleUpsert, ScheduleClaim, ScheduleClaimResult, ScheduleRow, ScheduleStore } from '../../interfaces';
2
+ /**
3
+ * SQLite (better-sqlite3) schedule store. Like {@link SqliteJobStore}, every
4
+ * method runs **synchronously** — `upsert` returns the row directly so it
5
+ * composes inside a synchronous `@Transactional` body; the rest wrap their
6
+ * synchronous result in a resolved Promise.
7
+ *
8
+ * `claimAndEnqueue` runs the compare-and-swap on `next_run_at` and the
9
+ * occurrence insert in ONE synchronous transaction. The insert uses
10
+ * `ON CONFLICT DO NOTHING` on `(name, unique_key)` so an active occurrence
11
+ * with the schedule's `uniqueKey` suppresses the new one (the overlap guard)
12
+ * without poisoning the transaction — the schedule row still advances.
13
+ */
14
+ export declare class SqliteScheduleStore implements ScheduleStore {
15
+ upsert(db: unknown, input: ResolvedScheduleUpsert): ScheduleRow;
16
+ get(db: unknown, name: string): Promise<ScheduleRow | undefined>;
17
+ list(db: unknown): Promise<ScheduleRow[]>;
18
+ remove(db: unknown, name: string): Promise<boolean>;
19
+ setEnabled(db: unknown, name: string, enabled: boolean, nextRunAt: string | null, expectedUpdatedAt?: string): Promise<ScheduleRow | undefined>;
20
+ listDue(db: unknown, nowIso: string, limit: number): Promise<ScheduleRow[]>;
21
+ claimAndEnqueue(db: unknown, claim: ScheduleClaim): Promise<ScheduleClaimResult>;
22
+ disable(db: unknown, id: string, lastError: string): Promise<void>;
23
+ }