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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-cf-jobs",
3
3
  "configKey": "cfJobs",
4
- "version": "0.12.2",
4
+ "version": "0.13.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
@@ -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: event.rowsFetched,
178
- rowsInserted: event.rowsInserted,
179
- d1RowsRead: event.d1RowsRead,
180
- d1RowsWritten: event.d1RowsWritten,
181
- result: event.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, event.error ?? null],
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
- event.rowsFetched ?? 0,
12
- event.rowsInserted ?? 0,
13
- event.d1RowsRead ?? 0,
14
- event.d1RowsWritten ?? 0
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
- /** Fire-and-forget hook invoked after `failJob` writes succeed. Errors are swallowed. */
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
- export type JobMetricStatus = 'completed' | 'failed' | 'released';
3
- /**
4
- * One terminal-ish job lifecycle event, engine-neutral. `released` is a
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 toEvent(job, status, extra) {
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
- const event = { ...toEvent(job, "completed", { durationMs }), ...readStats(result) };
31
- if (result !== void 0)
32
- event.result = result;
33
- return sink.record(event);
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(toEvent(job, "failed", { durationMs: job.duration_ms ?? null, error }));
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(toEvent(job, "released", { error: opts?.error }));
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, FailOptions = 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, FailOptions>, 'claimJob' | 'resolveClaimMiss' | 'completeJob' | 'failJob' | 'releaseJob'>;
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, FailOptions = unknown>(opts: RunDurableJobMessageOptions<StoredJob, Job, Message, Env, Db, Logger, CompleteResult, FailOptions>): Promise<RunDurableJobMessageResult>;
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
- * Diagnostic sink for dropped/invalid messages (no throw). `stack` is present
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.12.2",
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
  }