nuxt-cf-jobs 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/runtime/server/broadcast.js +7 -6
- package/dist/runtime/server/cloudflare.js +7 -5
- package/dist/runtime/server/d1.d.ts +8 -1
- package/dist/runtime/server/d1.js +2 -2
- package/dist/runtime/server/metrics.d.ts +45 -18
- package/dist/runtime/server/metrics.js +23 -12
- package/dist/runtime/server/outbox.d.ts +16 -5
- package/dist/runtime/server/outbox.js +1 -1
- package/dist/runtime/server/runtime.d.ts +31 -27
- package/dist/runtime/server/runtime.js +2 -2
- package/package.json +2 -2
package/dist/module.json
CHANGED
|
@@ -164,6 +164,7 @@ function releaseToExtra(opts) {
|
|
|
164
164
|
return opts?.error ? { error: opts.error } : {};
|
|
165
165
|
}
|
|
166
166
|
function jobEventFromMetrics(event) {
|
|
167
|
+
const completed = event.status === "completed" ? event : void 0;
|
|
167
168
|
return {
|
|
168
169
|
jobName: null,
|
|
169
170
|
jobId: event.jobId,
|
|
@@ -173,12 +174,12 @@ function jobEventFromMetrics(event) {
|
|
|
173
174
|
attempts: event.attempts,
|
|
174
175
|
durationMs: event.durationMs,
|
|
175
176
|
batchId: event.batchId,
|
|
176
|
-
error: event.error,
|
|
177
|
-
rowsFetched:
|
|
178
|
-
rowsInserted:
|
|
179
|
-
d1RowsRead:
|
|
180
|
-
d1RowsWritten:
|
|
181
|
-
result:
|
|
177
|
+
error: event.status === "completed" ? void 0 : event.error,
|
|
178
|
+
rowsFetched: completed?.stats.rowsFetched,
|
|
179
|
+
rowsInserted: completed?.stats.rowsInserted,
|
|
180
|
+
d1RowsRead: completed?.stats.d1RowsRead,
|
|
181
|
+
d1RowsWritten: completed?.stats.d1RowsWritten,
|
|
182
|
+
result: completed?.result
|
|
182
183
|
};
|
|
183
184
|
}
|
|
184
185
|
async function loadBroadcastJobDefinition(registry, name) {
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { noopMetricsSink } from "./metrics.js";
|
|
2
2
|
export function defaultJobDataPoint(event) {
|
|
3
|
+
const stats = event.status === "completed" ? event.stats : void 0;
|
|
4
|
+
const error = event.status === "completed" ? null : event.error ?? null;
|
|
3
5
|
return {
|
|
4
6
|
indexes: [event.queue],
|
|
5
|
-
blobs: [event.queue, event.jobType, event.status,
|
|
7
|
+
blobs: [event.queue, event.jobType, event.status, error],
|
|
6
8
|
// doubles are positional — keep this order stable (the AE SQL API references
|
|
7
9
|
// double1..double6). duration, attempts, then the reported execution stats.
|
|
8
10
|
doubles: [
|
|
9
11
|
event.durationMs ?? 0,
|
|
10
12
|
event.attempts,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
stats?.rowsFetched ?? 0,
|
|
14
|
+
stats?.rowsInserted ?? 0,
|
|
15
|
+
stats?.d1RowsRead ?? 0,
|
|
16
|
+
stats?.d1RowsWritten ?? 0
|
|
15
17
|
]
|
|
16
18
|
};
|
|
17
19
|
}
|
|
@@ -87,10 +87,17 @@ export interface D1DurableJobRepositoryOptions<Queue extends string = string> {
|
|
|
87
87
|
durationMs: number | null;
|
|
88
88
|
result?: unknown;
|
|
89
89
|
}) => void;
|
|
90
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Fire-and-forget hook invoked after `failJob` writes succeed. Errors are swallowed.
|
|
92
|
+
*
|
|
93
|
+
* `error` is the HEADLINE (`"TypeError: <message>"`) — safe for issue titles,
|
|
94
|
+
* realtime payloads and Analytics Engine blobs. `cause` is the ORIGINAL thrown
|
|
95
|
+
* value: report it as-is (`captureException(cause)`) to keep the native stack.
|
|
96
|
+
*/
|
|
91
97
|
onJobFailed?: (input: {
|
|
92
98
|
job: D1DurableJobRecord<Queue>;
|
|
93
99
|
error: string;
|
|
100
|
+
cause?: unknown;
|
|
94
101
|
}) => void;
|
|
95
102
|
/** Fire-and-forget hook invoked after `releaseJob` writes succeed. Errors are swallowed. */
|
|
96
103
|
onJobReleased?: (input: {
|
|
@@ -149,7 +149,7 @@ export function createD1DurableJobRepository(db, opts = {}) {
|
|
|
149
149
|
assertOwnedMutation(resultUpdate, job.id);
|
|
150
150
|
fireHook(() => opts.onJobCompleted?.({ job, durationMs, result }));
|
|
151
151
|
},
|
|
152
|
-
async failJob(job, error) {
|
|
152
|
+
async failJob(job, error, failOpts) {
|
|
153
153
|
const insert = await db.prepare(`
|
|
154
154
|
INSERT OR REPLACE INTO ${failedJobsTable} (
|
|
155
155
|
id, queue, job_type, batch_id, user_id, site_id, partner_id, trace_id, unique_key, payload,
|
|
@@ -180,7 +180,7 @@ export function createD1DurableJobRepository(db, opts = {}) {
|
|
|
180
180
|
AND failed_at IS NULL
|
|
181
181
|
`).bind(job.id, job.reserved_at, job.attempts).run();
|
|
182
182
|
assertOwnedMutation(deleted, job.id);
|
|
183
|
-
fireHook(() => opts.onJobFailed?.({ job, error: headlineOf(error) }));
|
|
183
|
+
fireHook(() => opts.onJobFailed?.({ job, error: headlineOf(error), cause: failOpts?.cause }));
|
|
184
184
|
},
|
|
185
185
|
async releaseJob(job, releaseOpts) {
|
|
186
186
|
const result = await db.prepare(`
|
|
@@ -1,30 +1,57 @@
|
|
|
1
1
|
import type { D1DurableJobRepositoryOptions } from './d1.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
* deliberate retry (`ctx.release()` / a handler throw) rather than a terminal
|
|
6
|
-
* state, but it is worth recording for retry-rate dashboards.
|
|
7
|
-
*/
|
|
8
|
-
export interface JobMetricsEvent {
|
|
2
|
+
import type { JobRunStats } from './types.js';
|
|
3
|
+
/** Fields every lifecycle event carries, whatever its outcome. */
|
|
4
|
+
export interface JobMetricsEventBase {
|
|
9
5
|
jobId: string;
|
|
10
6
|
queue: string;
|
|
11
7
|
jobType: string;
|
|
12
|
-
status: JobMetricStatus;
|
|
13
8
|
attempts: number;
|
|
14
|
-
/** Wall-clock duration when known (completed jobs); null otherwise. */
|
|
15
|
-
durationMs: number | null;
|
|
16
9
|
batchId: string | null;
|
|
17
10
|
siteId: string | null;
|
|
18
11
|
userId: number | null;
|
|
19
|
-
/** Present for `failed` / `released` (the release reason). */
|
|
20
|
-
error?: string;
|
|
21
|
-
/** Optional completion result supplied by the consumer. */
|
|
22
|
-
result?: unknown;
|
|
23
|
-
rowsFetched?: number;
|
|
24
|
-
rowsInserted?: number;
|
|
25
|
-
d1RowsRead?: number;
|
|
26
|
-
d1RowsWritten?: number;
|
|
27
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* One terminal-ish job lifecycle event, engine-neutral, discriminated on `status`
|
|
15
|
+
* so a sink can only read the fields that outcome actually has. `released` is a
|
|
16
|
+
* deliberate retry (`ctx.release()`) rather than a terminal state, but it is worth
|
|
17
|
+
* recording for retry-rate dashboards.
|
|
18
|
+
*
|
|
19
|
+
* Previously this was one interface with `error?`/`cause?`/`result?`/`rows*?` all
|
|
20
|
+
* optional, which made `{ status: 'completed', cause }` and `{ status: 'failed',
|
|
21
|
+
* rowsInserted }` representable — and the Analytics Engine adapter duly wrote an
|
|
22
|
+
* `error` blob and zero-filled stat doubles for completed jobs. The union removes
|
|
23
|
+
* both the illegal states and the guesswork.
|
|
24
|
+
*/
|
|
25
|
+
export type JobMetricsEvent = (JobMetricsEventBase & {
|
|
26
|
+
status: 'completed';
|
|
27
|
+
/** Wall-clock duration when the repository timed the run. */
|
|
28
|
+
durationMs: number | null;
|
|
29
|
+
/** Whatever the consumer's `completeJob` returned. */
|
|
30
|
+
result?: unknown;
|
|
31
|
+
/** Execution stats from `ctx.reportStats`; `{}` when the handler reported none. */
|
|
32
|
+
stats: JobRunStats;
|
|
33
|
+
}) | (JobMetricsEventBase & {
|
|
34
|
+
status: 'failed';
|
|
35
|
+
durationMs: number | null;
|
|
36
|
+
/** Single-line headline (`"TypeError: <message>"`) — safe for titles + blobs. */
|
|
37
|
+
error: string;
|
|
38
|
+
/**
|
|
39
|
+
* The ORIGINAL thrown value, not a rendering of it. An error-tracker sink should
|
|
40
|
+
* `captureException(cause)` so it groups on the real throw site and keeps the
|
|
41
|
+
* native stack + `cause` chain, rather than rebuilding a synthetic
|
|
42
|
+
* `new Error(error)`. Absent when the failure never had a throw (a dispatch
|
|
43
|
+
* fault, `ctx.fail()`). Dimension-oriented sinks must ignore it — it is an
|
|
44
|
+
* arbitrary object, not a blob.
|
|
45
|
+
*/
|
|
46
|
+
cause?: unknown;
|
|
47
|
+
}) | (JobMetricsEventBase & {
|
|
48
|
+
status: 'released';
|
|
49
|
+
/** A release is not a completed run, so it is never timed. */
|
|
50
|
+
durationMs: null;
|
|
51
|
+
/** The release reason, when `ctx.release()` supplied one. */
|
|
52
|
+
error?: string;
|
|
53
|
+
});
|
|
54
|
+
export type JobMetricStatus = JobMetricsEvent['status'];
|
|
28
55
|
export interface JobMetricsSink {
|
|
29
56
|
record: (event: JobMetricsEvent) => void | Promise<void>;
|
|
30
57
|
}
|
|
@@ -10,33 +10,44 @@ function readStats(result) {
|
|
|
10
10
|
}
|
|
11
11
|
return out;
|
|
12
12
|
}
|
|
13
|
-
function
|
|
13
|
+
function baseOf(job) {
|
|
14
14
|
return {
|
|
15
15
|
jobId: job.id,
|
|
16
16
|
queue: job.queue,
|
|
17
17
|
jobType: job.job_type,
|
|
18
|
-
status,
|
|
19
18
|
attempts: job.attempts,
|
|
20
|
-
durationMs: extra.durationMs ?? null,
|
|
21
19
|
batchId: job.batch_id,
|
|
22
20
|
siteId: job.site_id,
|
|
23
|
-
userId: job.user_id
|
|
24
|
-
error: extra.error
|
|
21
|
+
userId: job.user_id
|
|
25
22
|
};
|
|
26
23
|
}
|
|
27
24
|
export function metricsSinkToRepoHooks(sink) {
|
|
28
25
|
return {
|
|
29
26
|
onJobCompleted({ job, durationMs, result }) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
return sink.record({
|
|
28
|
+
...baseOf(job),
|
|
29
|
+
status: "completed",
|
|
30
|
+
durationMs: durationMs ?? null,
|
|
31
|
+
stats: readStats(result),
|
|
32
|
+
...result !== void 0 ? { result } : {}
|
|
33
|
+
});
|
|
34
34
|
},
|
|
35
|
-
onJobFailed({ job, error }) {
|
|
36
|
-
return sink.record(
|
|
35
|
+
onJobFailed({ job, error, cause }) {
|
|
36
|
+
return sink.record({
|
|
37
|
+
...baseOf(job),
|
|
38
|
+
status: "failed",
|
|
39
|
+
durationMs: job.duration_ms ?? null,
|
|
40
|
+
error,
|
|
41
|
+
...cause !== void 0 ? { cause } : {}
|
|
42
|
+
});
|
|
37
43
|
},
|
|
38
44
|
onJobReleased({ job, opts }) {
|
|
39
|
-
return sink.record(
|
|
45
|
+
return sink.record({
|
|
46
|
+
...baseOf(job),
|
|
47
|
+
status: "released",
|
|
48
|
+
durationMs: null,
|
|
49
|
+
...opts?.error !== void 0 ? { error: opts.error } : {}
|
|
50
|
+
});
|
|
40
51
|
}
|
|
41
52
|
};
|
|
42
53
|
}
|
|
@@ -97,11 +97,22 @@ export interface ReleaseDurableJobOptions {
|
|
|
97
97
|
availableAt?: number;
|
|
98
98
|
error?: string;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Well-known `failJob` options, always passable regardless of a repository's own
|
|
102
|
+
* `FailOptions` — hence `Partial<FailOptions> & FailDurableJobOptions` on `failJob`
|
|
103
|
+
* (`Partial<unknown>` collapses to `{}`), so the runtime can supply just a `cause`
|
|
104
|
+
* without knowing a repository's bespoke options. `cause` is the ORIGINAL thrown
|
|
105
|
+
* value: the `error` string is a rendering for the `failed_jobs` row, but a telemetry
|
|
106
|
+
* hook wants the instance so it can report the real stack instead of rebuilding one.
|
|
107
|
+
*/
|
|
108
|
+
export interface FailDurableJobOptions {
|
|
109
|
+
cause?: unknown;
|
|
110
|
+
}
|
|
100
111
|
export interface DurableJobLifecycle<Job, CompleteResult = void, FailOptions = unknown, ReleaseResult = void> {
|
|
101
112
|
claimJob: (id: string) => Promise<Job | null>;
|
|
102
113
|
resolveClaimMiss?: (id: string) => Promise<DurableJobClaimMiss>;
|
|
103
114
|
completeJob: (job: Job, result?: unknown) => Promise<CompleteResult>;
|
|
104
|
-
failJob: (job: Job, error: string, opts?: FailOptions) => Promise<void>;
|
|
115
|
+
failJob: (job: Job, error: string, opts?: Partial<FailOptions> & FailDurableJobOptions) => Promise<void>;
|
|
105
116
|
releaseJob?: (job: Job, opts?: ReleaseDurableJobOptions) => Promise<ReleaseResult>;
|
|
106
117
|
}
|
|
107
118
|
export interface QueuePublisher<Queue extends string = string, Message extends QueueJobMessage<Queue> = QueueJobMessage<Queue>> {
|
|
@@ -228,7 +239,7 @@ export declare function createQueuePublisher<Env extends Record<string, unknown>
|
|
|
228
239
|
}): QueuePublisher<Queue, Message>;
|
|
229
240
|
export declare function claimDurableJob<Job>(lifecycle: Pick<DurableJobLifecycle<Job>, 'claimJob' | 'resolveClaimMiss'>, id: string): Promise<DurableJobClaimResult<Job>>;
|
|
230
241
|
export declare function completeDurableJob<Job, CompleteResult>(lifecycle: Pick<DurableJobLifecycle<Job, CompleteResult>, 'completeJob'>, job: Job, result?: unknown): Promise<CompleteResult>;
|
|
231
|
-
export declare function failDurableJob<Job, FailOptions>(lifecycle: Pick<DurableJobLifecycle<Job, unknown, FailOptions>, 'failJob'>, job: Job, error: string, opts?: FailOptions): Promise<void>;
|
|
242
|
+
export declare function failDurableJob<Job, FailOptions>(lifecycle: Pick<DurableJobLifecycle<Job, unknown, FailOptions>, 'failJob'>, job: Job, error: string, opts?: Partial<FailOptions> & FailDurableJobOptions): Promise<void>;
|
|
232
243
|
export declare function releaseDurableJob<Job, ReleaseResult>(lifecycle: Pick<DurableJobLifecycle<Job, unknown, unknown, ReleaseResult>, 'releaseJob'>, job: Job, opts?: ReleaseDurableJobOptions): Promise<ReleaseResult | undefined>;
|
|
233
244
|
export declare function findDispatchableDurableJobs<Queue extends string, Record extends Pick<DurableJobRecord<Queue>, 'id' | 'queue'>>(repository: Pick<DurableJobRecoveryRepository<Queue, Record>, 'findDispatchableJobs'>, query?: DurableJobRecoveryQuery): Promise<Record[]>;
|
|
234
245
|
export declare function releaseStaleReservedDurableJobs<Queue extends string, Record extends Pick<DurableJobRecord<Queue>, 'id' | 'queue'>>(repository: Pick<DurableJobRecoveryRepository<Queue, Record>, 'releaseStaleReservedJobs'>, query: DurableJobStaleRecoveryQuery): Promise<number>;
|
|
@@ -285,9 +296,9 @@ export declare function dispatchDurableJobBatch<Queue extends string>(publisher:
|
|
|
285
296
|
delaySeconds?: number;
|
|
286
297
|
}): Promise<Array<DispatchDurableJobBatchResult<Queue>>>;
|
|
287
298
|
export type DurableJobMessageStatus = 'invalid-message' | DurableJobClaimMiss | 'dispatch-failed' | 'released' | 'failed' | 'completed' | 'errored';
|
|
288
|
-
export interface RunDurableJobMessageOptions<StoredJob, Job extends DispatchableJob, Message extends QueueJobMessage = QueueJobMessage, Env = unknown, Db = unknown, Logger = unknown, CompleteResult = unknown
|
|
299
|
+
export interface RunDurableJobMessageOptions<StoredJob, Job extends DispatchableJob, Message extends QueueJobMessage = QueueJobMessage, Env = unknown, Db = unknown, Logger = unknown, CompleteResult = unknown> {
|
|
289
300
|
message: Pick<QueueMessage<Message>, 'body' | 'ack' | 'retry'>;
|
|
290
|
-
lifecycle: Pick<DurableJobLifecycle<StoredJob, CompleteResult,
|
|
301
|
+
lifecycle: Pick<DurableJobLifecycle<StoredJob, CompleteResult, FailDurableJobOptions>, 'claimJob' | 'resolveClaimMiss' | 'completeJob' | 'failJob' | 'releaseJob'>;
|
|
291
302
|
registry: {
|
|
292
303
|
getHandler: (name: string) => JobHandler<unknown, Env, Db, Logger> | undefined | Promise<JobHandler<unknown, Env, Db, Logger> | undefined>;
|
|
293
304
|
getJobDefinition?: (name: string) => JobDefinition<string, unknown, string, Env, Db, Logger> | undefined;
|
|
@@ -414,4 +425,4 @@ export type RunDurableJobMessageResult = {
|
|
|
414
425
|
status: 'claim-error';
|
|
415
426
|
error: JobError;
|
|
416
427
|
};
|
|
417
|
-
export declare function runDurableJobMessage<StoredJob, Job extends DispatchableJob, Message extends QueueJobMessage = QueueJobMessage, Env = unknown, Db = unknown, Logger = unknown, CompleteResult = unknown
|
|
428
|
+
export declare function runDurableJobMessage<StoredJob, Job extends DispatchableJob, Message extends QueueJobMessage = QueueJobMessage, Env = unknown, Db = unknown, Logger = unknown, CompleteResult = unknown>(opts: RunDurableJobMessageOptions<StoredJob, Job, Message, Env, Db, Logger, CompleteResult>): Promise<RunDurableJobMessageResult>;
|
|
@@ -338,7 +338,7 @@ export async function runDurableJobMessage(opts) {
|
|
|
338
338
|
const permanent = opts.isPermanentFailure ? opts.isPermanentFailure({ error, storedJob, attempts: job.attempts, maxAttempts }) : typeof maxAttempts === "number" && job.attempts >= maxAttempts;
|
|
339
339
|
if (permanent) {
|
|
340
340
|
try {
|
|
341
|
-
await failDurableJob(opts.lifecycle, storedJob, describeCauseWithStack(error));
|
|
341
|
+
await failDurableJob(opts.lifecycle, storedJob, describeCauseWithStack(error), { cause: error });
|
|
342
342
|
} catch (failError) {
|
|
343
343
|
const obsolete = obsoleteDelivery(failError);
|
|
344
344
|
if (obsolete)
|
|
@@ -35,6 +35,33 @@ export declare function runDurableJobBatchMessage<StoredJob, Job extends Dispatc
|
|
|
35
35
|
jobId: string;
|
|
36
36
|
queue: string;
|
|
37
37
|
}, Env = unknown, Db = unknown, Logger = unknown>(opts: RunDurableJobBatchMessageOptions<StoredJob, Job, Message, Env, Db, Logger>): Promise<RunDurableJobBatchMessageResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Every stage the runtime reports through {@link CfJobsLogEvent}. A union, not
|
|
40
|
+
* `string`: consumers switch on it to pick a log level / catalogued name, and a new
|
|
41
|
+
* stage should break their switch rather than fall silently into a default branch.
|
|
42
|
+
*
|
|
43
|
+
* - `invalid_payload` — no `_task`, or no handler registered for it.
|
|
44
|
+
* - `unexpected` — a lightweight handler threw; the message is retried.
|
|
45
|
+
* - `failed` — a handler called `ctx.fail()`.
|
|
46
|
+
* - `dlq` — a durable job exhausted its retries and was dead-lettered.
|
|
47
|
+
* - `onfinish-failed` — a batch's `onFinish` continuation threw.
|
|
48
|
+
*/
|
|
49
|
+
export type CfJobsLogStage = 'invalid_payload' | 'unexpected' | 'failed' | 'dlq' | 'onfinish-failed';
|
|
50
|
+
/**
|
|
51
|
+
* Diagnostic entry from the consumer loop (never a throw). When the stage came from a
|
|
52
|
+
* defect, `cause` is the original thrown value — report it directly rather than
|
|
53
|
+
* rebuilding `new Error(error)` — and `stack` is its rendering (stack + `cause`
|
|
54
|
+
* chain). `error` is always a single-line headline, safe for an issue title.
|
|
55
|
+
*/
|
|
56
|
+
export interface CfJobsLogEvent {
|
|
57
|
+
stage: CfJobsLogStage;
|
|
58
|
+
queue?: string;
|
|
59
|
+
taskName?: string;
|
|
60
|
+
jobId?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
stack?: string;
|
|
63
|
+
cause?: unknown;
|
|
64
|
+
}
|
|
38
65
|
export interface RunLightweightMessageOptions<Env = unknown, Db = unknown, Logger = unknown> {
|
|
39
66
|
message: Pick<QueueMessage, 'id' | 'body' | 'attempts' | 'ack' | 'retry'>;
|
|
40
67
|
registry: DurableJobsRuntimeRegistry<Env, Db, Logger>;
|
|
@@ -43,17 +70,8 @@ export interface RunLightweightMessageOptions<Env = unknown, Db = unknown, Logge
|
|
|
43
70
|
isDuplicate?: (id: string | undefined) => boolean;
|
|
44
71
|
/** Mark an id as terminally handled. Retried/released messages must not be marked. */
|
|
45
72
|
markDuplicate?: (id: string | undefined) => void;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
* when the entry came from a thrown defect, so a consumer can report the real
|
|
49
|
-
* stack rather than reconstructing `new Error(error)`.
|
|
50
|
-
*/
|
|
51
|
-
onLog?: (event: {
|
|
52
|
-
stage: string;
|
|
53
|
-
taskName?: string;
|
|
54
|
-
error?: string;
|
|
55
|
-
stack?: string;
|
|
56
|
-
}) => void;
|
|
73
|
+
/** Diagnostic sink for dropped/invalid messages (no throw). */
|
|
74
|
+
onLog?: (event: CfJobsLogEvent) => void;
|
|
57
75
|
}
|
|
58
76
|
export type RunLightweightMessageResult = {
|
|
59
77
|
status: 'invalid' | 'duplicate' | 'dispatch-failed' | 'released' | 'failed' | 'completed' | 'errored';
|
|
@@ -77,14 +95,7 @@ export interface ConsumeQueueBatchOptions<Queue extends string, Env, Db, Logger>
|
|
|
77
95
|
isDlqQueue?: (queue: string) => boolean;
|
|
78
96
|
isDuplicate?: (id: string | undefined) => boolean;
|
|
79
97
|
markDuplicate?: (id: string | undefined) => void;
|
|
80
|
-
onLog?: (event:
|
|
81
|
-
stage: string;
|
|
82
|
-
queue?: string;
|
|
83
|
-
taskName?: string;
|
|
84
|
-
jobId?: string;
|
|
85
|
-
error?: string;
|
|
86
|
-
stack?: string;
|
|
87
|
-
}) => void;
|
|
98
|
+
onLog?: (event: CfJobsLogEvent) => void;
|
|
88
99
|
createJobScope?: (storedJob: D1DurableJobRecord<Queue>) => DurableJobScope<D1DurableJobRecord<Queue>>;
|
|
89
100
|
isPermanentFailure?: (input: {
|
|
90
101
|
error: unknown;
|
|
@@ -170,14 +181,7 @@ export interface CreateDurableJobsRuntimeOptions<Queue extends string = string,
|
|
|
170
181
|
*/
|
|
171
182
|
dedup?: Set<string>;
|
|
172
183
|
/** Diagnostic log sink for DLQ / dropped lightweight messages. */
|
|
173
|
-
onLog?: (event:
|
|
174
|
-
stage: string;
|
|
175
|
-
queue?: string;
|
|
176
|
-
taskName?: string;
|
|
177
|
-
jobId?: string;
|
|
178
|
-
error?: string;
|
|
179
|
-
stack?: string;
|
|
180
|
-
}) => void;
|
|
184
|
+
onLog?: (event: CfJobsLogEvent) => void;
|
|
181
185
|
/** Per-job scope: wrap dispatch (e.g. telemetry) + observe each settlement. */
|
|
182
186
|
createJobScope?: (storedJob: D1DurableJobRecord<Queue>) => DurableJobScope<D1DurableJobRecord<Queue>>;
|
|
183
187
|
/** Decide whether a thrown error is terminal (default: attempts >= max). */
|
|
@@ -93,7 +93,7 @@ export async function runLightweightMessage(opts) {
|
|
|
93
93
|
return { status: "completed" };
|
|
94
94
|
} catch (error) {
|
|
95
95
|
const delaySeconds = resolveJobRetryDelay(definition, message.attempts);
|
|
96
|
-
opts.onLog?.({ stage: "unexpected", taskName, error: describeCause(error), stack: describeCauseWithStack(error) });
|
|
96
|
+
opts.onLog?.({ stage: "unexpected", taskName, error: describeCause(error), stack: describeCauseWithStack(error), cause: error });
|
|
97
97
|
message.retry({ delaySeconds });
|
|
98
98
|
return { status: "errored" };
|
|
99
99
|
}
|
|
@@ -246,7 +246,7 @@ export function createDurableJobsRuntime(opts) {
|
|
|
246
246
|
});
|
|
247
247
|
}
|
|
248
248
|
} catch (error) {
|
|
249
|
-
opts.onLog?.({ stage: "onfinish-failed", taskName: c.name, jobId: batch.id, error: describeCause(error), stack: describeCauseWithStack(error) });
|
|
249
|
+
opts.onLog?.({ stage: "onfinish-failed", taskName: c.name, jobId: batch.id, error: describeCause(error), stack: describeCauseWithStack(error), cause: error });
|
|
250
250
|
}
|
|
251
251
|
});
|
|
252
252
|
const dedup = opts.dedup ? createMessageDedup(opts.dedup) : void 0;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuxt-cf-jobs",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"description": "Nuxt module for typed Cloudflare queue jobs.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
"test:nitro": "vitest run --project nitro",
|
|
110
110
|
"test:types": "nuxt prepare tests/fixtures/nuxt-demo && vue-tsc --noEmit -p tests/fixtures/nuxt-demo/tsconfig.json",
|
|
111
111
|
"test:e2e": "CF_JOBS_E2E=1 vitest run --no-file-parallelism tests/wrangler-dev.e2e.test.ts tests/wrangler-d1.e2e.test.ts",
|
|
112
|
-
"typecheck": "tsc --noEmit -p tsconfig.json && pnpm test:types",
|
|
112
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tests/tsconfig.json && pnpm test:types",
|
|
113
113
|
"lint": "eslint .",
|
|
114
114
|
"lint:fix": "eslint . --fix"
|
|
115
115
|
}
|