@cloudflare/vitest-plugin 0.0.0 → 1.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.
@@ -0,0 +1,861 @@
1
+ declare module "cloudflare:test" {
2
+ /**
3
+ * @deprecated Instead, use `import { env } from "cloudflare:workers"`
4
+ */
5
+ export const env: Cloudflare.Env;
6
+
7
+ /**
8
+ * Service binding to the default export defined in the `main` worker. Note
9
+ * this `main` worker runs in the same isolate/context as tests, so any global
10
+ * mocks will apply to it too.
11
+ * @deprecated Instead, use `import { exports } from "cloudflare:workers"` and `exports.default.fetch()`
12
+ */
13
+ export const SELF: Fetcher;
14
+
15
+ /**
16
+ * Runs `callback` inside the Durable Object pointed-to by `stub`'s context.
17
+ * Conceptually, this temporarily replaces your Durable Object's `fetch()`
18
+ * handler with `callback`, then sends a request to it, returning the result.
19
+ * This can be used to call/spy-on Durable Object instance methods or seed/get
20
+ * persisted data. Note this can only be used with `stub`s pointing to Durable
21
+ * Objects defined in the `main` worker.
22
+ */
23
+ import type * as Rpc from "cloudflare:workers";
24
+ export function runInDurableObject<
25
+ O extends DurableObject | Rpc.DurableObject,
26
+ R,
27
+ >(
28
+ stub: DurableObjectStub<O>,
29
+ callback: (instance: O, state: DurableObjectState) => R | Promise<R>
30
+ ): Promise<R>;
31
+ /**
32
+ * Immediately runs and removes the Durable Object pointed-to by `stub`'s
33
+ * alarm if one is scheduled. Returns `true` if an alarm ran, and `false`
34
+ * otherwise. Note this can only be used with `stub`s pointing to Durable
35
+ * Objects defined in the `main` worker.
36
+ */
37
+ export function runDurableObjectAlarm(
38
+ stub: DurableObjectStub
39
+ ): Promise<boolean /* ran */>;
40
+ export interface DurableObjectEvictionOptions {
41
+ /**
42
+ * Controls what happens to hibernatable WebSockets when evicting a Durable
43
+ * Object. Defaults to `"hibernate"`.
44
+ */
45
+ webSockets?: "close" | "hibernate";
46
+ }
47
+ /**
48
+ * Evicts the currently-running Durable Object pointed-to by `stub`, tearing
49
+ * down its instance to reset in-memory state while preserving durable
50
+ * storage. By default, hibernatable WebSockets are hibernated rather than closed, and
51
+ * eviction waits for in-flight requests to drain (with a timeout).
52
+ *
53
+ * Useful for testing how a Durable Object behaves across evictions, e.g.
54
+ * recovering state from storage or resuming hibernated WebSockets.
55
+ *
56
+ * Rejects if `stub` is not a Durable Object stub, if the target Durable
57
+ * Object is not currently running, or if its namespace has eviction
58
+ * prevented. Note this can only be used with `stub`s pointing to Durable
59
+ * Objects defined in the `main` worker.
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * import { evictDurableObject } from "cloudflare:test";
64
+ *
65
+ * await evictDurableObject(stub);
66
+ * await evictDurableObject(stub, { webSockets: "close" });
67
+ * ```
68
+ */
69
+ export function evictDurableObject(
70
+ stub: DurableObjectStub,
71
+ options?: DurableObjectEvictionOptions
72
+ ): Promise<void>;
73
+ /**
74
+ * Gets the IDs of all objects that have been created in the `namespace`.
75
+ */
76
+ export function listDurableObjectIds<T>(
77
+ namespace: DurableObjectNamespace<T>
78
+ ): Promise<DurableObjectId[]>;
79
+
80
+ /**
81
+ * Deletes all data from all attached bindings. This is
82
+ * useful for resetting state between test blocks.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import { reset } from "cloudflare:test";
87
+ * import { afterEach } from "vitest";
88
+ *
89
+ * afterEach(async () => {
90
+ * await reset();
91
+ * });
92
+ * ```
93
+ */
94
+ export function reset(): Promise<void>;
95
+
96
+ /**
97
+ * Resets all Durable Object instances. Unlike `reset()`, this does not delete
98
+ * persisted data.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * import { abortAllDurableObjects } from "cloudflare:test";
103
+ * import { afterEach } from "vitest";
104
+ *
105
+ * afterEach(async () => {
106
+ * await abortAllDurableObjects();
107
+ * });
108
+ * ```
109
+ */
110
+ export function abortAllDurableObjects(): Promise<void>;
111
+
112
+ /**
113
+ * Evicts all currently-running Durable Objects in evictable namespaces.
114
+ * Unlike `abortAllDurableObjects()`, eviction is graceful: durable storage is
115
+ * preserved, hibernatable WebSockets are hibernated rather than closed by default, and
116
+ * eviction waits for in-flight requests to drain (with a timeout). In-memory
117
+ * state is reset by tearing down each instance.
118
+ *
119
+ * Non-running/idle actors are skipped, running facets are recursively
120
+ * evicted, and namespaces with eviction prevented are respected.
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * import { evictAllDurableObjects } from "cloudflare:test";
125
+ * import { afterEach } from "vitest";
126
+ *
127
+ * afterEach(async () => {
128
+ * await evictAllDurableObjects();
129
+ * });
130
+ * ```
131
+ */
132
+ export function evictAllDurableObjects(
133
+ options?: DurableObjectEvictionOptions
134
+ ): Promise<void>;
135
+
136
+ /**
137
+ * Creates an instance of `ExecutionContext` for use as the 3rd argument to
138
+ * modules-format exported handlers.
139
+ */
140
+ export function createExecutionContext(): ExecutionContext;
141
+ /**
142
+ * Waits for all `ExecutionContext#waitUntil()`ed `Promise`s to settle. Only
143
+ * accepts `ExecutionContext`s returned by `createExecutionContext()` or
144
+ * `EventContext`s return by `createPagesEventContext()`.
145
+ */
146
+ export function waitOnExecutionContext(
147
+ ctx: ExecutionContext | EventContext<Cloudflare.Env, string, unknown>
148
+ ): Promise<void>;
149
+ /**
150
+ * Creates an instance of `ScheduledController` for use as the 1st argument to
151
+ * modules-format `scheduled()` exported handlers.
152
+ */
153
+ export function createScheduledController(
154
+ options?: FetcherScheduledOptions
155
+ ): ScheduledController;
156
+ /**
157
+ * Creates an instance of `MessageBatch` for use as the 1st argument to
158
+ * modules-format `queue()` exported handlers.
159
+ */
160
+ export function createMessageBatch<Body = unknown>(
161
+ queueName: string,
162
+ messages: ServiceBindingQueueMessage<Body>[]
163
+ ): MessageBatch<Body>;
164
+ /**
165
+ * Gets the ack/retry state of messages in the `MessageBatch`, and waits for
166
+ * all `ExecutionContext#waitUntil()`ed `Promise`s to settle. Only accepts
167
+ * instances of `MessageBatch` returned by `createMessageBatch()`, and
168
+ * instances of `ExecutionContext` returned by `createExecutionContext()`.
169
+ */
170
+ export function getQueueResult(
171
+ batch: MessageBatch,
172
+ ctx: ExecutionContext
173
+ ): Promise<FetcherQueueResult>;
174
+
175
+ export interface D1Migration {
176
+ name: string;
177
+ queries: string[];
178
+ }
179
+
180
+ /**
181
+ * Applies all un-applied `migrations` to database `db`, recording migrations
182
+ * state in the `migrationsTableName` table. `migrationsTableName` defaults to
183
+ * `d1_migrations`. Call the `readD1Migrations()` function from the
184
+ * `@cloudflare/vitest-plugin/config` package inside Node.js to get the
185
+ * `migrations` array.
186
+ */
187
+ export function applyD1Migrations(
188
+ db: D1Database,
189
+ migrations: D1Migration[],
190
+ migrationsTableName?: string
191
+ ): Promise<void>;
192
+
193
+ /**
194
+ * Admin API for a secrets store binding. Returned by `adminSecretsStore()`.
195
+ */
196
+ export interface SecretsStoreSecretAdmin {
197
+ /** Create a new secret with the given value. Returns the secret's ID. */
198
+ create(value: string): Promise<string>;
199
+ /** Update an existing secret (identified by ID) with a new value. Returns the secret's ID. */
200
+ update(value: string, id: string): Promise<string>;
201
+ /** Duplicate a secret (identified by ID) under a new name. Returns the new secret's ID. */
202
+ duplicate(id: string, newName: string): Promise<string>;
203
+ /** Delete a secret by ID. */
204
+ delete(id: string): Promise<void>;
205
+ /** List all secrets in the store. */
206
+ list(): Promise<{ name: string; metadata?: { uuid: string } }[]>;
207
+ /** Get a secret's name by ID. */
208
+ get(id: string): Promise<string>;
209
+ }
210
+
211
+ /**
212
+ * Returns the admin API for a secrets store binding, allowing tests to
213
+ * create, update, and delete secrets that would otherwise be read-only
214
+ * via `binding.get()`.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * import { adminSecretsStore } from "cloudflare:test";
219
+ * import { env } from "cloudflare:workers";
220
+ *
221
+ * const admin = adminSecretsStore(env.MY_SECRET);
222
+ * await admin.create("my-secret-value");
223
+ *
224
+ * // Now env.MY_SECRET.get() will return "my-secret-value"
225
+ * ```
226
+ */
227
+ export function adminSecretsStore(binding: {
228
+ get(): Promise<string>;
229
+ }): SecretsStoreSecretAdmin;
230
+
231
+ /**
232
+ * Creates an introspector for a specific Workflow instance, used to
233
+ * modify its behavior and await outcomes.
234
+ * This is the primary entry point for testing individual Workflow instances.
235
+ *
236
+ * @param workflow - The Workflow binding, e.g., `env.MY_WORKFLOW`.
237
+ * @param instanceId - The known ID of the Workflow instance to target.
238
+ * @returns A `WorkflowInstanceIntrospector` to control the instance's behavior.
239
+ *
240
+ * @remarks
241
+ * ### Dispose
242
+ *
243
+ * The introspector must be disposed after the test to remove mocks and release
244
+ * resources. This can be handled in two ways:
245
+ *
246
+ * 1. **Implicit dispose**: With the `await using` syntax.
247
+ * `await using instance = await introspectWorkflowInstance(...)`
248
+ *
249
+ * 2. **Explicit dispose**: Manually call `await instance.dispose()` at the end of the
250
+ * test.
251
+ *
252
+ * @example
253
+ * // Full test of a Workflow instance using implicit dispose
254
+ * it("should disable all sleeps and complete", async () => {
255
+ * // 1. CONFIGURATION
256
+ * // `await using` ensures .dispose() is automatically called at the end of the block.
257
+ * await using instance = await introspectWorkflowInstance(env.MY_WORKFLOW, "123456");
258
+ *
259
+ * await instance.modify(async (m) => {
260
+ * await m.disableSleeps();
261
+ * });
262
+ *
263
+ * // 2. EXECUTION
264
+ * await env.MY_WORKFLOW.create({ id: "123456" });
265
+ *
266
+ * // 3. ASSERTION
267
+ * await instance.waitForStatus("complete");
268
+ *
269
+ * const output = await instance.getOutput();
270
+ * expect(output).toEqual({ success: true });
271
+ *
272
+ * // 4. DISPOSE is implicit and automatic here.
273
+ * });
274
+ */
275
+ export function introspectWorkflowInstance(
276
+ workflow: Workflow,
277
+ instanceId: string
278
+ ): Promise<WorkflowInstanceIntrospector>;
279
+
280
+ /**
281
+ * Provides methods to control a single Workflow instance.
282
+ */
283
+ export interface WorkflowInstanceIntrospector {
284
+ /**
285
+ * Applies modifications to the Workflow instance's behavior.
286
+ * Takes a callback function to apply modifications.
287
+ *
288
+ * @param fn - An async callback that receives a `WorkflowInstanceModifier` object.
289
+ * @returns The `WorkflowInstanceIntrospector` instance for chaining.
290
+ */
291
+ modify(
292
+ fn: (m: WorkflowInstanceModifier) => Promise<void>
293
+ ): Promise<WorkflowInstanceIntrospector>;
294
+
295
+ /**
296
+ * Waits for a specific step to complete and return a result.
297
+ * If the step has already completed, this promise resolves immediately.
298
+ *
299
+ * @param step - An object specifying the step `name` and optional `index` (1-based).
300
+ * If multiple steps share the same name, `index` targets a specific one.
301
+ * Defaults to the first step found (`index: 1`).
302
+ * @returns A promise that resolves with the step's result,
303
+ * or rejects with an error if the step fails.
304
+ */
305
+ waitForStepResult(step: { name: string; index?: number }): Promise<unknown>;
306
+
307
+ /**
308
+ * Waits for the Workflow instance to reach a specific InstanceStatus status
309
+ * (e.g., 'running', 'complete').
310
+ * If the instance is already in the target status, this promise resolves immediately.
311
+ * Throws an error if the Workflow instance reaches a finite state
312
+ * (e.g., complete, errored) that is different from the target status.
313
+ *
314
+ * @param status - The target `InstanceStatus` to wait for.
315
+ */
316
+ waitForStatus(status: InstanceStatus["status"]): Promise<void>;
317
+
318
+ /**
319
+ * Retrieves the output value returned by the Workflow instance upon successful completion.
320
+ *
321
+ * This method should only be called after the Workflow instance has completed successfully.
322
+ * It's recommended to use `waitForStatus("complete")` before calling this method.
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * it('my workflow test', async () => {
327
+ * await using instance = await introspectWorkflowInstance(env.MY_WORKFLOW, "123");
328
+ * await env.MY_WORKFLOW.create({ id: "123" });
329
+ *
330
+ * await instance.waitForStatus("complete");
331
+ *
332
+ * const output = await instance.getOutput();
333
+ * expect(output).toEqual({ success: true });
334
+ * });
335
+ * ```
336
+ */
337
+ getOutput(): Promise<unknown>;
338
+
339
+ /**
340
+ * Retrieves the error information from a failed Workflow instance.
341
+ *
342
+ * This method should only be called after the Workflow instance has failed.
343
+ * It's recommended to use `waitForStatus("errored")` before calling this method.
344
+ *
345
+ * @example
346
+ * ```ts
347
+ * it('my workflow test', async () => {
348
+ * await using instance = await introspectWorkflowInstance(env.MY_WORKFLOW, "123");
349
+ * await env.MY_WORKFLOW.create({ id: "123" });
350
+ *
351
+ * await instance.waitForStatus("errored");
352
+ *
353
+ * const error = await instance.getError();
354
+ * expect(error.name).toBe("Error");
355
+ * expect(error.message).toContain("some error");
356
+ * });
357
+ * ```
358
+ */
359
+ getError(): Promise<{ name: string; message: string }>;
360
+
361
+ /**
362
+ * Disposes the Workflow instance introspector.
363
+ *
364
+ * This is crucial for ensuring test isolation by preventing state from
365
+ * leaking between tests. It should be called at the end of each test.
366
+ */
367
+ dispose(): Promise<void>;
368
+
369
+ /**
370
+ * An alias for {@link dispose} to support automatic disposal with the `using` keyword.
371
+ *
372
+ * @see {@link dispose}
373
+ * @example
374
+ * ```ts
375
+ * it('my workflow test', async () => {
376
+ * await using instance = await introspectWorkflowInstance(env.WORKFLOW, "123456");
377
+ *
378
+ * // ... your test logic ...
379
+ *
380
+ * // .dispose() is automatically called here at the end of the scope
381
+ * });
382
+ * ```
383
+ */
384
+ [Symbol.asyncDispose](): Promise<void>;
385
+ }
386
+
387
+ /**
388
+ * Provides methods to mock or alter the behavior of a Workflow instance's
389
+ * steps, events, and sleeps.
390
+ */
391
+ interface WorkflowInstanceModifier {
392
+ /**
393
+ * Disables sleeps, causing `step.sleep()` and `step.sleepUntil()` to
394
+ * resolve immediately.
395
+ *
396
+ * @example Disable all sleeps:
397
+ * ```ts
398
+ * await instance.modify(m => {
399
+ * m.disableSleeps();
400
+ * });
401
+ * ```
402
+ *
403
+ * @example Disable a specific set of sleeps by their step names:
404
+ * ```ts
405
+ * await instance.modify(m => {
406
+ * m.disableSleeps([{ name: "sleep1" }, { name: "sleep5" }, { name: "sleep7" }]);
407
+ * });
408
+ * ```
409
+ *
410
+ * @param steps - Optional array of specific steps to disable sleeps for.
411
+ * If omitted, **all sleeps** in the Workflow will be disabled.
412
+ * A step is an object specifying the step `name` and optional `index` (1-based).
413
+ * If multiple steps share the same name, `index` targets a specific one.
414
+ * Defaults to the first step found (`index: 1`).
415
+ */
416
+ disableSleeps(steps?: { name: string; index?: number }[]): Promise<void>;
417
+
418
+ /**
419
+ * Disables retry backoff delays, causing retry attempts of a failing
420
+ * `step.do()` to execute immediately without waiting.
421
+ *
422
+ * By default, when a step fails and has retries configured, the engine
423
+ * waits according to the retry config (e.g., exponential backoff).
424
+ * This method eliminates those delays while preserving retry behavior
425
+ * (all attempts still execute, just without waiting between them).
426
+ *
427
+ * @example Disable all retry delays:
428
+ * ```ts
429
+ * await instance.modify(m => {
430
+ * m.disableRetryDelays();
431
+ * });
432
+ * ```
433
+ *
434
+ * @example Disable retry delays for specific steps:
435
+ * ```ts
436
+ * await instance.modify(m => {
437
+ * m.disableRetryDelays([{ name: "fetch-data" }, { name: "call-api" }]);
438
+ * });
439
+ * ```
440
+ *
441
+ * @param steps - Optional array of specific steps to disable retry delays for.
442
+ * If omitted, **all retry delays** in the Workflow will be disabled.
443
+ * A step is an object specifying the step `name` and optional `index` (1-based).
444
+ * If multiple steps share the same name, `index` targets a specific one.
445
+ * Defaults to the first step found (`index: 1`).
446
+ */
447
+ disableRetryDelays(
448
+ steps?: { name: string; index?: number }[]
449
+ ): Promise<void>;
450
+
451
+ /**
452
+ * Mocks the result of a `step.do()`, causing it to return a specified
453
+ * value instantly without executing the step's actual implementation.
454
+ *
455
+ * If called multiple times for the same step, an error will be thrown.
456
+ *
457
+ * @param step - An object specifying the step `name` and optional `index` (1-based).
458
+ * If multiple steps share the same name, `index` targets a specific one.
459
+ * Defaults to the first step found (`index: 1`).
460
+ * @param stepResult - The mock value to be returned by the step.
461
+ *
462
+ * @example Mock the result of the third step named "fetch-data":
463
+ * ```ts
464
+ * await instance.modify(m => {
465
+ * m.mockStepResult(
466
+ * { name: "fetch-data", index: 3 },
467
+ * { success: true, data: [1, 2, 3] }
468
+ * );
469
+ * });
470
+ * ```
471
+ */
472
+ mockStepResult(
473
+ step: { name: string; index?: number },
474
+ stepResult: unknown
475
+ ): Promise<void>;
476
+
477
+ /**
478
+ * Forces a `step.do()` to throw an error, simulating a failure without
479
+ * executing the step's actual implementation. Useful for testing retry logic
480
+ * and error handling.
481
+ *
482
+ * @example Mock a step that errors 3 times before succeeding:
483
+ * ```ts
484
+ * // This example assumes the "fetch-data" step is configured with at least 3 retries.
485
+ * await instance.modify(m => {
486
+ * m.mockStepError(
487
+ * { name: "fetch-data" },
488
+ * new Error("Failed!"),
489
+ * 3
490
+ * );
491
+ * m.mockStepResult(
492
+ * { name: "fetch-data" },
493
+ * { success: true, data: [1, 2, 3] }
494
+ * );
495
+ * });
496
+ * ```
497
+ *
498
+ * @param step - An object specifying the step `name` and optional `index` (1-based).
499
+ * If multiple steps share the same name, `index` targets a specific one.
500
+ * Defaults to the first step found (`index: 1`).
501
+ * @param error - The `Error` object to be thrown.
502
+ * @param times - Optional number of times to throw the error. If a step has
503
+ * retries configured, it will fail this many times before potentially
504
+ * succeeding on a subsequent attempt. If omitted, it will throw on **every attempt**.
505
+ */
506
+ mockStepError(
507
+ step: { name: string; index?: number },
508
+ error: Error,
509
+ times?: number
510
+ ): Promise<void>;
511
+
512
+ /**
513
+ * Forces a `step.do()` to fail by timing out immediately, without executing
514
+ * the step's actual implementation. Default step timeout is 10 minutes.
515
+ *
516
+ * @example Mock a step that times out 3 times before succeeding:
517
+ * ```ts
518
+ * // This example assumes the "fetch-data" step is configured with at least 3 retries.
519
+ * await instance.modify(m => {
520
+ * m.forceStepTimeout(
521
+ * { name: "fetch-data" },
522
+ * 3
523
+ * );
524
+ * m.mockStepResult(
525
+ * { name: "fetch-data" },
526
+ * { success: true, data: [1, 2, 3] }
527
+ * );
528
+ * });
529
+ * ```
530
+ *
531
+ * @param step - An object specifying the step `name` and optional `index` (1-based).
532
+ * If multiple steps share the same name, `index` targets a specific one.
533
+ * Defaults to the first step found (`index: 1`).
534
+ * @param times - Optional number of times the step will time out. Useful for
535
+ * testing retry logic. If omitted, it will time out on **every attempt**.
536
+ */
537
+ forceStepTimeout(step: { name: string; index?: number }, times?: number);
538
+
539
+ /**
540
+ * Sends a mock event to the Workflow instance. This causes a `step.waitForEvent()`
541
+ * to resolve with the provided payload, as long as the step's timeout has not
542
+ * yet expired. Default event timeout is 24 hours.
543
+ *
544
+ * @example Mock a step event:
545
+ * ```ts
546
+ * await instance.modify(m => {
547
+ * m.mockEvent(
548
+ * { type: "user-approval", payload: { approved: true } },
549
+ * );
550
+ * ```
551
+ *
552
+ * @param event - The event to send, including its `type` and `payload`.
553
+ */
554
+ mockEvent(event: { type: string; payload: unknown }): Promise<void>;
555
+
556
+ /**
557
+ * Forces a `step.waitForEvent()` to time out instantly, causing the step to fail.
558
+ * This simulates a scenario where an expected event never arrives.
559
+ * Default event timeout is 24 hours.
560
+ *
561
+ * @example Mock a step to time out:
562
+ * ```ts
563
+ * await instance.modify(m => {
564
+ * m.forceEventTimeout(
565
+ * { name: "user-approval" },
566
+ * );
567
+ * ```
568
+ *
569
+ * @param step - An object specifying the step `name` and optional `index` (1-based).
570
+ * If multiple steps share the same name, `index` targets a specific one.
571
+ * Defaults to the first step found (`index: 1`).
572
+ */
573
+ forceEventTimeout(step: { name: string; index?: number }): Promise<void>;
574
+ }
575
+
576
+ /**
577
+ * Creates an **introspector** for a Workflow, where instance IDs are unknown
578
+ * beforehand. This allows for defining modifications that will apply to
579
+ * **all subsequently created instances**.
580
+ *
581
+ * This is the primary entry point for testing Workflow instances where the id
582
+ * is unknown before their creation.
583
+ *
584
+ * @param workflow - The Workflow binding, e.g., `env.MY_WORKFLOW`.
585
+ * @returns A `WorkflowIntrospector` to control the instances behavior.
586
+ *
587
+ * @remarks
588
+ * ### Dispose
589
+ *
590
+ * The introspector must be disposed after the test to remove mocks and release
591
+ * resources. This can be handled in two ways:
592
+ *
593
+ * 1. **Implicit dispose**: With the `await using` syntax.
594
+ * `await using introspector = await introspectWorkflow(...)`
595
+ *
596
+ * 2. **Explicit dispose**: Manually call `await introspector.dispose()` at the end of the
597
+ * test.
598
+ *
599
+ * @example
600
+ * ```ts
601
+ * // Full test of a Workflow instance using implicit dispose
602
+ * it("should disable all sleeps and complete", async () => {
603
+ * // 1. CONFIGURATION
604
+ * await using introspector = await introspectWorkflow(env.MY_WORKFLOW);
605
+ * await introspector.modifyAll(async (m) => {
606
+ * await m.disableSleeps();
607
+ * });
608
+ *
609
+ * // 2. EXECUTION
610
+ * await env.MY_WORKFLOW.create();
611
+ *
612
+ * // 3. ASSERTION
613
+ * const instances = await introspector.get();
614
+ * for(const instance of instances) {
615
+ * await instance.waitForStatus("complete");
616
+ *
617
+ * const output = await instance.getOutput();
618
+ * expect(output).toEqual({ success: true });
619
+ * }
620
+ *
621
+ * // 4. DISPOSE is implicit and automatic here.
622
+ * });
623
+ * ```
624
+ */
625
+ export function introspectWorkflow(
626
+ workflow: Workflow
627
+ ): Promise<WorkflowIntrospector>;
628
+
629
+ /**
630
+ * Provides methods to control all instances created by a Worflow.
631
+ */
632
+ export interface WorkflowIntrospector {
633
+ /**
634
+ * Applies modifications to all Workflow instances created after calling
635
+ * `introspectWorkflow`. Takes a callback function to apply modifications.
636
+ *
637
+ * @param fn - An async callback that receives a `WorkflowInstanceModifier` object.
638
+ */
639
+ modifyAll(
640
+ fn: (m: WorkflowInstanceModifier) => Promise<void>
641
+ ): Promise<void>;
642
+
643
+ /**
644
+ * Returns all `WorkflowInstanceIntrospector`s from Workflow instances
645
+ * created after calling `introspectWorkflow`.
646
+ */
647
+ get(): Promise<WorkflowInstanceIntrospector[]>;
648
+
649
+ /**
650
+ *
651
+ * Disposes the introspector and every `WorkflowInstanceIntrospector` from Workflow
652
+ * instances created after calling `introspectWorkflow`.
653
+ *
654
+ * This function is essential for test isolation, ensuring that results from one
655
+ * test do not leak into the next. It should be called at the end or after each test.
656
+ *
657
+ * **Note:** After dispose, `introspectWorkflow()` must be called again to begin
658
+ * a new introspection.
659
+ *
660
+ */
661
+ dispose(): Promise<void>;
662
+
663
+ /**
664
+ * An alias for {@link dispose} to support automatic disposal with the `using` keyword.
665
+ * This is an alternative to calling `dispose()` in an `afterEach` hook.
666
+ *
667
+ * @see {@link dispose}
668
+ * @example
669
+ * it('my workflow test', async () => {
670
+ * await using workflowIntrospector = await introspectWorkflow(env.WORKFLOW);
671
+ *
672
+ * // ... your test logic ...
673
+ *
674
+ * // .dispose() is automatically called here at the end of the scope
675
+ * });
676
+ */
677
+ [Symbol.asyncDispose](): Promise<void>;
678
+ }
679
+
680
+ // Only require `params` and `data` to be specified if they're non-empty
681
+ interface EventContextInitBase {
682
+ request: Request<unknown, IncomingRequestCfProperties>;
683
+ functionPath?: string;
684
+ next?(request: Request): Response | Promise<Response>;
685
+ }
686
+ type EventContextInitParams<Params extends string> = [Params] extends [never]
687
+ ? { params?: Record<string, never> }
688
+ : { params: Record<Params, string | string[]> };
689
+ type EventContextInitData<Data> =
690
+ Data extends Record<string, never> ? { data?: Data } : { data: Data };
691
+ type EventContextInit<E extends EventContext<unknown, unknown, unknown>> =
692
+ E extends EventContext<unknown, infer Params, infer Data>
693
+ ? EventContextInitBase &
694
+ EventContextInitParams<Params> &
695
+ EventContextInitData<Data>
696
+ : never;
697
+
698
+ /**
699
+ * Creates an instance of `EventContext` for use as the argument to Pages
700
+ * Functions.
701
+ */
702
+ export function createPagesEventContext<
703
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- any is required: PagesFunction wraps Data in a function parameter, which flips subtyping — unknown would reject PagesFunction types with specific Data
704
+ F extends PagesFunction<Cloudflare.Env, string, any>,
705
+ >(init: EventContextInit<Parameters<F>[0]>): Parameters<F>[0];
706
+
707
+ // Taken from `undici` (https://github.com/nodejs/undici/tree/main/types) with
708
+ // no dependency on `@types/node` and with unusable functions removed
709
+ //
710
+ // MIT License
711
+ //
712
+ // Copyright (c) Matteo Collina and Undici contributors
713
+ //
714
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
715
+ // of this software and associated documentation files (the "Software"), to deal
716
+ // in the Software without restriction, including without limitation the rights
717
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
718
+ // copies of the Software, and to permit persons to whom the Software is
719
+ // furnished to do so, subject to the following conditions:
720
+ //
721
+ // The above copyright notice and this permission notice shall be included in all
722
+ // copies or substantial portions of the Software.
723
+ //
724
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
725
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
726
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
727
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
728
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
729
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
730
+ // SOFTWARE.
731
+
732
+ type IncomingHttpHeaders = Record<string, string | string[] | undefined>;
733
+ type Buffer = Uint8Array;
734
+
735
+ /** The scope associated with a mock dispatch. */
736
+ abstract class MockScope<TData extends object = object> {
737
+ /** Delay a reply by a set amount of time in ms. */
738
+ delay(waitInMs: number): MockScope<TData>;
739
+ /** Persist the defined mock data for the associated reply. It will return the defined mock data indefinitely. */
740
+ persist(): MockScope<TData>;
741
+ /** Define a reply for a set amount of matching requests. */
742
+ times(repeatTimes: number): MockScope<TData>;
743
+ }
744
+
745
+ /** The interceptor for a Mock. */
746
+ abstract class MockInterceptor {
747
+ /** Mock an undici request with the defined reply. */
748
+ reply<TData extends object = object>(
749
+ replyOptionsCallback: MockInterceptor.MockReplyOptionsCallback<TData>
750
+ ): MockScope<TData>;
751
+ reply<TData extends object = object>(
752
+ statusCode: number,
753
+ data?:
754
+ | TData
755
+ | Buffer
756
+ | string
757
+ | MockInterceptor.MockResponseDataHandler<TData>,
758
+ responseOptions?: MockInterceptor.MockResponseOptions
759
+ ): MockScope<TData>;
760
+ /** Mock an undici request by throwing the defined reply error. */
761
+ replyWithError<TError extends Error = Error>(error: TError): MockScope;
762
+ /** Set default reply headers on the interceptor for subsequent mocked replies. */
763
+ defaultReplyHeaders(headers: IncomingHttpHeaders): MockInterceptor;
764
+ /** Set default reply trailers on the interceptor for subsequent mocked replies. */
765
+ defaultReplyTrailers(trailers: Record<string, string>): MockInterceptor;
766
+ /** Set automatically calculated content-length header on subsequent mocked replies. */
767
+ replyContentLength(): MockInterceptor;
768
+ }
769
+ namespace MockInterceptor {
770
+ /** MockInterceptor options. */
771
+ export interface Options {
772
+ /** Path to intercept on. */
773
+ path: string | RegExp | ((path: string) => boolean);
774
+ /** Method to intercept on. Defaults to GET. */
775
+ method?: string | RegExp | ((method: string) => boolean);
776
+ /** Body to intercept on. */
777
+ body?: string | RegExp | ((body: string) => boolean);
778
+ /** Headers to intercept on. */
779
+ headers?:
780
+ | Record<string, string | RegExp | ((body: string) => boolean)>
781
+ | ((headers: Record<string, string>) => boolean);
782
+ /** Query params to intercept on */
783
+ query?: Record<string, unknown>;
784
+ }
785
+ export interface MockDispatch<
786
+ TData extends object = object,
787
+ TError extends Error = Error,
788
+ > extends Options {
789
+ times: number | null;
790
+ persist: boolean;
791
+ consumed: boolean;
792
+ data: MockDispatchData<TData, TError>;
793
+ }
794
+ export interface MockDispatchData<
795
+ TData extends object = object,
796
+ TError extends Error = Error,
797
+ > extends MockResponseOptions {
798
+ error: TError | null;
799
+ statusCode?: number;
800
+ data?: TData | string;
801
+ }
802
+ export interface MockResponseOptions {
803
+ headers?: IncomingHttpHeaders;
804
+ trailers?: Record<string, string>;
805
+ }
806
+ export interface MockResponseCallbackOptions {
807
+ path: string;
808
+ origin: string;
809
+ method: string;
810
+ body?: BodyInit;
811
+ headers: Headers | Record<string, string>;
812
+ maxRedirections: number;
813
+ }
814
+ export type MockResponseDataHandler<TData extends object = object> = (
815
+ opts: MockResponseCallbackOptions
816
+ ) => TData | Buffer | string;
817
+ export type MockReplyOptionsCallback<TData extends object = object> = (
818
+ opts: MockResponseCallbackOptions
819
+ ) => {
820
+ statusCode: number;
821
+ data?: TData | Buffer | string;
822
+ responseOptions?: MockResponseOptions;
823
+ };
824
+ }
825
+
826
+ interface Interceptable {
827
+ /** Intercepts any matching requests that use the same origin as this mock client. */
828
+ intercept(options: MockInterceptor.Options): MockInterceptor;
829
+ }
830
+
831
+ interface PendingInterceptor extends MockInterceptor.MockDispatch {
832
+ origin: string;
833
+ }
834
+ interface PendingInterceptorsFormatter {
835
+ format(pendingInterceptors: readonly PendingInterceptor[]): string;
836
+ }
837
+
838
+ /** A mocked Agent class that implements the Agent API. It allows one to intercept HTTP requests made through undici and return mocked responses instead. */
839
+ abstract class MockAgent {
840
+ /** Creates and retrieves mock Dispatcher instances which can then be used to intercept HTTP requests. If the number of connections on the mock agent is set to 1, a MockClient instance is returned. Otherwise a MockPool instance is returned. */
841
+
842
+ get(origin: string | RegExp | ((origin: string) => boolean)): Interceptable;
843
+
844
+ /** Disables mocking in MockAgent. */
845
+ deactivate(): void;
846
+ /** Enables mocking in a MockAgent instance. When instantiated, a MockAgent is automatically activated. Therefore, this method is only effective after MockAgent.deactivate has been called. */
847
+ activate(): void;
848
+
849
+ /** Define host matchers so only matching requests that aren't intercepted by the mock dispatchers will be attempted. */
850
+ enableNetConnect(
851
+ host?: string | RegExp | ((host: string) => boolean)
852
+ ): void;
853
+ /** Causes all requests to throw when requests are not matched in a MockAgent intercept. */
854
+ disableNetConnect(): void;
855
+
856
+ pendingInterceptors(): PendingInterceptor[];
857
+ assertNoPendingInterceptors(options?: {
858
+ pendingInterceptorsFormatter?: PendingInterceptorsFormatter;
859
+ }): void;
860
+ }
861
+ }