nuxt-cf-jobs 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-cf-jobs",
3
3
  "configKey": "cfJobs",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -1,5 +1,5 @@
1
1
  import type { D1DatabaseLike } from './d1.js';
2
- import type { DurableJobContinuation, DurableJobRecord, DurableJobRepository, QueuePublisher } from './outbox.js';
2
+ import type { DispatchDurableJobBatchResult, DurableJobContinuation, DurableJobRecord, DurableJobRepository, QueuePublisher } from './outbox.js';
3
3
  /** A `job_batches` row as the lifecycle helpers consume it (post-decrement view). */
4
4
  export interface DurableBatchRecord {
5
5
  id: string;
@@ -81,11 +81,7 @@ export interface CreateJobBatchOptions<Name extends string = string, Payload ext
81
81
  export interface CreateJobBatchResult {
82
82
  batchId: string;
83
83
  jobIds: string[];
84
- dispatched: Array<{
85
- queue: string;
86
- dispatched: boolean;
87
- error?: unknown;
88
- }>;
84
+ dispatched: Array<DispatchDurableJobBatchResult>;
89
85
  }
90
86
  /**
91
87
  * Atomically register a batch of jobs: insert the `job_batches` row with
@@ -69,6 +69,14 @@ export interface D1DurableJobRepositoryOptions<Queue extends string = string> {
69
69
  jobsTable?: string;
70
70
  failedJobsTable?: string;
71
71
  batchesTable?: string;
72
+ /**
73
+ * Laravel's `retry_after`: when set, `claimJob` also reclaims a reservation
74
+ * older than this many seconds (a dead worker that never acked/released), in
75
+ * one atomic statement — so a redelivered message re-runs instead of bouncing
76
+ * until DLQ. MUST be longer than the slowest job, or a still-running job can be
77
+ * double-claimed. Default unset = only unreserved rows are claimable.
78
+ */
79
+ reclaimAfterSeconds?: number;
72
80
  /** Fire-and-forget hook invoked after a successful claim. Errors are swallowed. */
73
81
  onJobClaimed?: (input: {
74
82
  job: D1DurableJobRecord<Queue>;
@@ -88,16 +88,17 @@ export function createD1DurableJobRepository(db, opts = {}) {
88
88
  },
89
89
  async claimJob(id) {
90
90
  const now = currentUnixSeconds();
91
+ const reclaimBefore = typeof opts.reclaimAfterSeconds === "number" ? now - opts.reclaimAfterSeconds : -1;
91
92
  const job = await db.prepare(`
92
93
  UPDATE ${jobsTable}
93
94
  SET reserved_at = ?, attempts = attempts + 1
94
95
  WHERE id = ?
95
- AND reserved_at IS NULL
96
+ AND (reserved_at IS NULL OR reserved_at <= ?)
96
97
  AND available_at <= ?
97
98
  AND completed_at IS NULL
98
99
  AND failed_at IS NULL
99
100
  RETURNING *
100
- `).bind(now, id, now).first();
101
+ `).bind(now, id, reclaimBefore, now).first();
101
102
  if (job)
102
103
  fireHook(() => opts.onJobClaimed?.({ job }));
103
104
  return job;
@@ -200,22 +200,32 @@ export type EnqueueDurableJobResult = {
200
200
  export declare function enqueueDurableJob<Queue extends string, Record extends DurableJobRecord<Queue>>(repository: Pick<DurableJobRepository<Queue, Record>, 'insertJob'>, publisher: Pick<QueuePublisher<Queue>, 'send'>, record: Record, opts?: {
201
201
  delaySeconds?: number;
202
202
  }): Promise<EnqueueDurableJobResult>;
203
+ /**
204
+ * Per-queue outcome of a durable batch dispatch, discriminated on `status`:
205
+ * - `sent`: the queue accepted the batch.
206
+ * - `not-dispatched`: the queue binding was missing, so the send was skipped (no
207
+ * throw); the rows are durable and a sweep will redispatch them.
208
+ * - `failed`: the send threw; `cause` is the raw (infra) throw. Also sweep-recoverable.
209
+ */
210
+ export type DispatchDurableJobBatchResult<Queue extends string = string> = {
211
+ queue: Queue;
212
+ status: 'sent';
213
+ } | {
214
+ queue: Queue;
215
+ status: 'not-dispatched';
216
+ } | {
217
+ queue: Queue;
218
+ status: 'failed';
219
+ cause: unknown;
220
+ };
203
221
  export interface SweepDurableJobsResult<Queue extends string> {
204
222
  swept: number;
205
- dispatched: Array<{
206
- queue: Queue;
207
- dispatched: boolean;
208
- error?: unknown;
209
- }>;
223
+ dispatched: Array<DispatchDurableJobBatchResult<Queue>>;
210
224
  }
211
225
  export declare function sweepDispatchableDurableJobs<Queue extends string>(repository: Pick<DurableJobRecoveryRepository<Queue, Pick<DurableJobRecord<Queue>, 'id' | 'queue'>>, 'findDispatchableJobs'>, publisher: Pick<QueuePublisher<Queue>, 'sendBatch'>, query?: DurableJobRecoveryQuery): Promise<SweepDurableJobsResult<Queue>>;
212
226
  export declare function dispatchDurableJobBatch<Queue extends string>(publisher: Pick<QueuePublisher<Queue>, 'sendBatch'>, records: Array<Pick<DurableJobRecord<Queue>, 'id' | 'queue'>>, opts?: {
213
227
  delaySeconds?: number;
214
- }): Promise<Array<{
215
- queue: Queue;
216
- dispatched: boolean;
217
- error?: unknown;
218
- }>>;
228
+ }): Promise<Array<DispatchDurableJobBatchResult<Queue>>>;
219
229
  export type DurableJobMessageStatus = 'invalid-message' | DurableJobClaimMiss | 'dispatch-failed' | 'released' | 'failed' | 'completed' | 'errored';
220
230
  export interface RunDurableJobMessageOptions<StoredJob, Job extends DispatchableJob, Message extends QueueJobMessage = QueueJobMessage, Env = unknown, Db = unknown, Logger = unknown, CompleteResult = unknown, FailOptions = unknown> {
221
231
  message: Pick<QueueMessage<Message>, 'body' | 'ack' | 'retry'>;
@@ -197,12 +197,10 @@ export async function dispatchDurableJobBatch(publisher, records, opts) {
197
197
  return await Promise.all(
198
198
  [...groups].map(async ([queue, messages]) => {
199
199
  try {
200
- return {
201
- queue,
202
- dispatched: await publisher.sendBatch(queue, messages, opts)
203
- };
204
- } catch (error) {
205
- return { queue, dispatched: false, error };
200
+ const sent = await publisher.sendBatch(queue, messages, opts);
201
+ return sent ? { queue, status: "sent" } : { queue, status: "not-dispatched" };
202
+ } catch (cause) {
203
+ return { queue, status: "failed", cause };
206
204
  }
207
205
  })
208
206
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-cf-jobs",
3
3
  "type": "module",
4
- "version": "0.5.0",
4
+ "version": "0.5.2",
5
5
  "description": "Nuxt module for typed Cloudflare queue jobs.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",