effect-cf 0.33.0 → 0.35.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 (74) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +31 -1079
  3. package/dist/{Artifacts-DnHQA-Ax.d.mts → Artifacts-B3r3RuTB.d.mts} +1 -48
  4. package/dist/{Artifacts-CSIk1Oml.mjs → Artifacts-CBxWGeZB.mjs} +2 -7
  5. package/dist/{Binding-pfilNJmP.mjs → Binding-C82jRhdr.mjs} +1 -2
  6. package/dist/{Binding-xJJJPmiD.d.mts → Binding-DKcLrQCM.d.mts} +0 -45
  7. package/dist/ComputerArtifacts.d.mts +1 -3
  8. package/dist/ComputerArtifacts.mjs +1 -2
  9. package/dist/ComputerWorkspace.d.mts +11 -12
  10. package/dist/ComputerWorkspace.mjs +31 -10
  11. package/dist/ComputerWorkspaceHost.d.mts +1 -9
  12. package/dist/{ContainerNamespace-CFjiLw8b.d.mts → ContainerNamespace-DbLaeFhN.d.mts} +1 -14
  13. package/dist/{DurableObjectState-CVLLse69.mjs → DurableObjectState-DcqElJ4e.mjs} +82 -89
  14. package/dist/{DurableObjectState-BpRjz6-q.d.mts → DurableObjectState-DpYdNZgT.d.mts} +102 -266
  15. package/dist/{ErrorMessage-BM0K-Nik.mjs → ErrorMessage-Byx2OgNd.mjs} +0 -1
  16. package/dist/{Hyperdrive-z-9fFz15.d.mts → Hyperdrive-HrwFlZ6x.d.mts} +1 -4
  17. package/dist/HyperdrivePg.d.mts +2 -11
  18. package/dist/HyperdrivePg.mjs +0 -8
  19. package/dist/Sandbox.d.mts +2 -32
  20. package/dist/Sandbox.mjs +2 -11
  21. package/dist/Vitest.d.mts +3 -31
  22. package/dist/Vitest.mjs +8 -26
  23. package/dist/{WorkflowBinding-lBR4_-op.d.mts → WorkflowBinding-Cn3AuUyP.d.mts} +2 -5
  24. package/dist/index.d.mts +42 -480
  25. package/dist/index.mjs +267 -380
  26. package/package.json +1 -1
  27. package/src/AiGateway.ts +0 -6
  28. package/src/AnalyticsEngine.ts +8 -10
  29. package/src/Artifacts.ts +0 -47
  30. package/src/Binding.ts +0 -8
  31. package/src/BrowserRendering.ts +0 -4
  32. package/src/Cache.ts +0 -19
  33. package/src/CloudflareOtlp.ts +6 -39
  34. package/src/ComputerArtifacts.ts +0 -2
  35. package/src/ComputerWorkspace.ts +53 -35
  36. package/src/ComputerWorkspaceHost.ts +0 -8
  37. package/src/ContainerNamespace.ts +0 -13
  38. package/src/D1.ts +0 -22
  39. package/src/DurableObject.ts +51 -32
  40. package/src/DurableObjectAlarm.ts +6 -210
  41. package/src/DurableObjectDefinition.ts +8 -20
  42. package/src/DurableObjectNamespace.ts +88 -115
  43. package/src/DurableObjectRpcWebSocket.ts +5 -46
  44. package/src/DurableObjectSqlite.ts +0 -12
  45. package/src/DurableObjectState.ts +18 -38
  46. package/src/DurableObjectStorage.ts +55 -70
  47. package/src/DurableObjectWebSocket.ts +23 -72
  48. package/src/Email.ts +0 -12
  49. package/src/Environment.ts +0 -37
  50. package/src/Hyperdrive.ts +0 -3
  51. package/src/HyperdrivePg.ts +0 -9
  52. package/src/Images.ts +0 -5
  53. package/src/Kv.ts +0 -15
  54. package/src/QueueBinding.ts +0 -2
  55. package/src/R2.ts +0 -5
  56. package/src/RpcDefinition.ts +5 -17
  57. package/src/RpcTracing.ts +133 -0
  58. package/src/Sandbox.ts +0 -30
  59. package/src/ServiceBinding.ts +83 -128
  60. package/src/Vectorize.ts +0 -5
  61. package/src/Vitest.ts +18 -43
  62. package/src/WebTransport.ts +0 -15
  63. package/src/Worker.ts +140 -108
  64. package/src/WorkerDefinition.ts +5 -14
  65. package/src/WorkersAi.ts +21 -18
  66. package/src/Workflow.ts +45 -54
  67. package/src/WorkflowBinding.ts +0 -3
  68. package/src/index.ts +1 -0
  69. package/src/internal/Entrypoint.ts +23 -4
  70. package/src/internal/ErrorMessage.ts +0 -1
  71. package/src/internal/NativeCallback.ts +55 -0
  72. package/src/internal/RpcInvocation.ts +18 -1
  73. package/src/internal/Runtime.ts +12 -7
  74. package/src/internal/WorkerContext.ts +5 -14
@@ -6,13 +6,11 @@ import * as RpcDefinition from "./RpcDefinition";
6
6
  import type * as WorkerDefinition from "./WorkerDefinition";
7
7
  import * as ErrorMessage from "./internal/ErrorMessage";
8
8
  import * as RpcInvocation from "./internal/RpcInvocation";
9
+ import * as RpcTracing from "./RpcTracing";
9
10
 
10
11
  const TypeId = "effect-cf/ServiceBinding" as const;
11
12
  const expectedServiceBinding = "Worker service binding with fetch()";
12
13
 
13
- /**
14
- * Minimum shape for a Cloudflare service binding.
15
- */
16
14
  export interface ServiceFetcher {
17
15
  fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
18
16
  }
@@ -27,9 +25,6 @@ type RpcClient<Api> = {
27
25
 
28
26
  type ReservedMethodName = WorkerDefinition.ReservedMethodName | "fetch";
29
27
 
30
- /**
31
- * Native Cloudflare service object including optional RPC methods.
32
- */
33
28
  export type ServiceBindingClient<Api extends object> = ServiceFetcher &
34
29
  RpcClient<CloudflareRpc.Provider<Api, ReservedMethodName>>;
35
30
 
@@ -47,10 +42,10 @@ type ApiOrDefinition<Api extends object, Definition> = [Api] extends [never]
47
42
  export interface ServiceBindingDefinition<
48
43
  Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
49
44
  > {
50
- /** Binding name as configured in `wrangler.jsonc`. */
51
45
  readonly binding: string;
52
- /** Optional RPC schema used for argument/result encoding. */
53
46
  readonly definition?: Definition;
47
+ /** Append live trace context only when the receiver also enables rpcTracing. */
48
+ readonly rpcTracing?: boolean;
54
49
  }
55
50
 
56
51
  /**
@@ -112,22 +107,6 @@ export type ServiceBindingEffectClient<
112
107
  Api extends object,
113
108
  Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
114
109
  > = DirectMethods<never, Definition> & {
115
- /**
116
- * Forwards an HTTP request to the bound Worker service.
117
- *
118
- * Use this when the service binding is acting as an HTTP origin rather than a
119
- * Cloudflare RPC target.
120
- *
121
- * @example
122
- * ```ts
123
- * import { Effect } from "effect";
124
- *
125
- * const program = Effect.gen(function* () {
126
- * const api = yield* ApiWorker;
127
- * return yield* api.fetch(new Request("https://internal.example/users"));
128
- * });
129
- * ```
130
- */
131
110
  readonly fetch: (
132
111
  input: RequestInfo | URL,
133
112
  init?: RequestInit,
@@ -140,20 +119,6 @@ export type ServiceBindingEffectClient<
140
119
  * promise-like value and it does not decode definition-backed success schemas.
141
120
  *
142
121
  * Most application code should use {@link call} instead.
143
- *
144
- * @example
145
- * ```ts
146
- * import { Effect } from "effect";
147
- *
148
- * const program = Effect.gen(function* () {
149
- * const counter = yield* CounterService;
150
- *
151
- * const result = yield* counter.rpc("increment", 41);
152
- * const value = yield* Effect.promise(() => result);
153
- *
154
- * return value;
155
- * });
156
- * ```
157
122
  */
158
123
  readonly rpc: <Method extends ServiceMethodKey<Api>>(
159
124
  method: Method,
@@ -164,18 +129,6 @@ export type ServiceBindingEffectClient<
164
129
  * the success value when the binding was created from a definition.
165
130
  *
166
131
  * This is the normal choice when application code wants the final typed value.
167
- *
168
- * @example
169
- * ```ts
170
- * import { Effect } from "effect";
171
- *
172
- * const program = Effect.gen(function* () {
173
- * const counter = yield* CounterService;
174
- * const value = yield* counter.call("increment", 41);
175
- *
176
- * return value;
177
- * });
178
- * ```
179
132
  */
180
133
  readonly call: <Method extends ServiceMethodKey<Api>>(
181
134
  method: Method,
@@ -188,25 +141,15 @@ export type ServiceBindingEffectClient<
188
141
  *
189
142
  * Use this for RPC methods that return Cloudflare RPC resources or other
190
143
  * disposable objects whose lifetime should be tied to an Effect scope.
191
- *
192
- * @example
193
- * ```ts
194
- * import { Effect } from "effect";
195
- *
196
- * const program = Effect.scoped(
197
- * Effect.gen(function* () {
198
- * const files = yield* FileService;
199
- * const handle = yield* files.scopedCall("open", "report.csv");
200
- *
201
- * return yield* handle.read();
202
- * }),
203
- * );
204
- * ```
205
144
  */
206
145
  readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
207
146
  method: Method,
208
147
  ...args: ServiceMethodArgs<Api, Method>
209
- ) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
148
+ ) => Effect.Effect<
149
+ Awaited<ServiceMethodSuccess<Api, Method>>,
150
+ ServiceBindingRpcError,
151
+ Scope.Scope
152
+ >;
210
153
  };
211
154
 
212
155
  export type ServiceBindingStaticClient<
@@ -229,7 +172,11 @@ export type ServiceBindingStaticClient<
229
172
  readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
230
173
  method: Method,
231
174
  ...args: ServiceMethodArgs<Api, Method>
232
- ) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope | R>;
175
+ ) => Effect.Effect<
176
+ Awaited<ServiceMethodSuccess<Api, Method>>,
177
+ ServiceBindingRpcError,
178
+ Scope.Scope | R
179
+ >;
233
180
  };
234
181
 
235
182
  type ServiceBindingValue = Schema.Schema.Type<typeof Schema.Unknown>;
@@ -256,15 +203,30 @@ export const makeClient = <
256
203
  init?: RequestInit,
257
204
  ) {
258
205
  return yield* Effect.tryPromise({
259
- try: () => service.fetch(input, init),
206
+ try: (signal) => {
207
+ const callerSignal =
208
+ init?.signal !== undefined
209
+ ? init.signal
210
+ : input instanceof Request
211
+ ? input.signal
212
+ : undefined;
213
+
214
+ return service.fetch(input, {
215
+ ...init,
216
+ signal:
217
+ callerSignal === null || callerSignal === undefined
218
+ ? signal
219
+ : AbortSignal.any([callerSignal, signal]),
220
+ });
221
+ },
260
222
  catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
261
223
  });
262
224
  });
263
225
 
264
- const rpc = Effect.fn("ServiceBinding.rpc")(function* <Method extends ServiceMethodKey<Api>>(
226
+ const rpc = Effect.fnUntraced(function* <Method extends ServiceMethodKey<Api>>(
265
227
  method: Method,
266
228
  ...args: ServiceMethodArgs<Api, Method>
267
- ) {
229
+ ): Effect.fn.Return<ServiceMethodCloudflareReturn<Api, Method>, ServiceBindingRpcError> {
268
230
  const methodName = String(method);
269
231
  // SAFETY: definition-backed clients derive Method and Args from this exact RPC definition.
270
232
  const encodedArgs =
@@ -285,11 +247,15 @@ export const makeClient = <
285
247
  ),
286
248
  );
287
249
 
288
- // SAFETY: encoding preserves the selected API method's positional argument contract.
250
+ const nativeArgs = definition.rpcTracing
251
+ ? yield* RpcTracing.withRpcTraceContext(encodedArgs)
252
+ : encodedArgs;
253
+
254
+ // SAFETY: domain arguments retain their encoded order; an opted-in receiver strips only valid trace metadata.
289
255
  return yield* RpcInvocation.invokeRpcMethod(
290
256
  service,
291
257
  method,
292
- encodedArgs as ServiceMethodArgs<Api, Method>,
258
+ nativeArgs as ServiceMethodArgs<Api, Method>,
293
259
  (cause) =>
294
260
  new ServiceBindingRpcError({
295
261
  binding: definition.binding,
@@ -302,7 +268,7 @@ export const makeClient = <
302
268
  const decodeSuccess = Effect.fnUntraced(function* <Method extends ServiceMethodKey<Api>>(
303
269
  methodName: string,
304
270
  value: Awaited<ServiceMethodCloudflareReturn<Api, Method>>,
305
- ) {
271
+ ): Effect.fn.Return<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError> {
306
272
  if (definition.definition === undefined) {
307
273
  // SAFETY: without a definition the resolved native RPC value is the API method's declared success.
308
274
  return value as ServiceMethodSuccess<Api, Method>;
@@ -328,34 +294,52 @@ export const makeClient = <
328
294
  return decoded as ServiceMethodSuccess<Api, Method>;
329
295
  });
330
296
 
331
- const call = Effect.fn("ServiceBinding.call")(function* <Method extends ServiceMethodKey<Api>>(
332
- method: Method,
333
- ...args: ServiceMethodArgs<Api, Method>
334
- ) {
335
- const methodName = String(method);
336
- const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
337
- Effect.mapError(
338
- (cause) =>
339
- new ServiceBindingRpcError({
340
- binding: definition.binding,
341
- method: methodName,
342
- cause,
343
- }),
344
- ),
345
- );
346
-
347
- return yield* decodeSuccess<Method>(methodName, value);
348
- });
349
-
350
- const scopedCall = Effect.fn("ServiceBinding.scopedCall")(function* <
351
- Method extends ServiceMethodKey<Api>,
352
- >(method: Method, ...args: ServiceMethodArgs<Api, Method>) {
353
- const methodName = String(method);
354
- const result = yield* rpc(method, ...args);
355
- const value = yield* CloudflareRpc.scoped(result);
297
+ const call = Effect.fnUntraced(
298
+ function* <Method extends ServiceMethodKey<Api>>(
299
+ method: Method,
300
+ ...args: ServiceMethodArgs<Api, Method>
301
+ ): Effect.fn.Return<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError> {
302
+ const methodName = String(method);
303
+ const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
304
+ Effect.mapError(
305
+ (cause) =>
306
+ new ServiceBindingRpcError({
307
+ binding: definition.binding,
308
+ method: methodName,
309
+ cause,
310
+ }),
311
+ ),
312
+ );
313
+
314
+ return yield* decodeSuccess<Method>(methodName, value);
315
+ },
316
+ (effect, ...args) =>
317
+ RpcTracing.withRpcClientSpan(effect, definition.binding, String(args[0])),
318
+ );
356
319
 
357
- return yield* decodeSuccess<Method>(methodName, value);
358
- });
320
+ const scopedCall = Effect.fnUntraced(
321
+ function* <Method extends ServiceMethodKey<Api>>(
322
+ method: Method,
323
+ ...args: ServiceMethodArgs<Api, Method>
324
+ ): Effect.fn.Return<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError, Scope.Scope> {
325
+ const methodName = String(method);
326
+ const result = yield* rpc(method, ...args);
327
+ const value = yield* CloudflareRpc.scoped(result).pipe(
328
+ Effect.mapError(
329
+ (cause) =>
330
+ new ServiceBindingRpcError({
331
+ binding: definition.binding,
332
+ method: methodName,
333
+ cause,
334
+ }),
335
+ ),
336
+ );
337
+
338
+ return yield* decodeSuccess<Method>(methodName, value);
339
+ },
340
+ (effect, ...args) =>
341
+ RpcTracing.withRpcClientSpan(effect, definition.binding, String(args[0])),
342
+ );
359
343
 
360
344
  const directMethods = makeDirectMethods<never, Api, Definition>(definition.definition, call);
361
345
 
@@ -385,35 +369,6 @@ export const layer = <
385
369
  { expected: expectedServiceBinding },
386
370
  );
387
371
 
388
- /**
389
- * Creates a typed Effect service for a Worker service binding.
390
- *
391
- * Returned value includes:
392
- * - a Context tag for dependency injection
393
- * - `fetch(...)` for raw HTTP forwarding
394
- * - `rpc(...)` for raw Cloudflare RPC results
395
- * - `call(...)` for resolved and decoded RPC results
396
- * - `scopedCall(...)` for scoped and decoded disposable RPC results
397
- * - direct RPC methods when `definition` is provided
398
- *
399
- * @example
400
- * ```ts
401
- * const Counter = WorkerDefinition.make("Counter", {
402
- * increment: WorkerDefinition.method({
403
- * args: [Schema.Number],
404
- * success: Schema.Number,
405
- * }),
406
- * });
407
- *
408
- * const CounterLive = Counter.layer({ binding: "COUNTER" });
409
- *
410
- * const program = Effect.gen(function* () {
411
- * const counter = yield* Counter;
412
- * const next = yield* counter.increment(1);
413
- * return next;
414
- * });
415
- * ```
416
- */
417
372
  export const Service =
418
373
  <Self, Api extends object = never>() =>
419
374
  <
package/src/Vectorize.ts CHANGED
@@ -22,7 +22,6 @@ import * as ErrorMessage from "./internal/ErrorMessage";
22
22
  const expectedVectorizeBinding =
23
23
  "Vectorize index binding with describe(), query(), insert(), upsert(), deleteByIds(), and getByIds()";
24
24
 
25
- /** Vectorize operation represented by {@link VectorizeOperationError}. */
26
25
  export type VectorizeOperation =
27
26
  | "describe"
28
27
  | "query"
@@ -32,7 +31,6 @@ export type VectorizeOperation =
32
31
  | "deleteByIds"
33
32
  | "getByIds";
34
33
 
35
- /** Error raised when a Vectorize operation fails. */
36
34
  export class VectorizeOperationError extends Data.TaggedError("VectorizeOperationError")<{
37
35
  readonly binding: string;
38
36
  readonly operation: VectorizeOperation;
@@ -43,9 +41,7 @@ export class VectorizeOperationError extends Data.TaggedError("VectorizeOperatio
43
41
  }
44
42
  }
45
43
 
46
- /** Typed Vectorize binding definition. */
47
44
  export interface VectorizeDefinition {
48
- /** Binding name as configured in `wrangler.jsonc`. */
49
45
  readonly binding: string;
50
46
  }
51
47
 
@@ -114,7 +110,6 @@ export interface VectorizeClient {
114
110
 
115
111
  declare const VectorizeServiceTypeId: unique symbol;
116
112
 
117
- /** Nominal service marker for Vectorize services created with {@link make}. */
118
113
  export interface VectorizeService<Id extends string> {
119
114
  readonly [VectorizeServiceTypeId]: {
120
115
  readonly id: Id;
package/src/Vitest.ts CHANGED
@@ -33,27 +33,19 @@ import {
33
33
  } from "./DurableObjectState";
34
34
  import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
35
35
  import type { WorkflowInstanceStatusName } from "./WorkflowBinding";
36
+ import { runNativeCallback } from "./internal/NativeCallback";
36
37
 
37
38
  const currentEnv: WorkerEnv = env;
38
39
 
39
- /**
40
- * Provides the Workers test environment as both {@link WorkerEnvironment} and
41
- * the active Effect `ConfigProvider`.
42
- */
43
40
  export const layer: Layer.Layer<WorkerEnvironment> = Layer.mergeAll(
44
41
  Layer.succeed(WorkerEnvironment, currentEnv),
45
42
  ConfigProvider.layer(WorkerConfig.providerFromEnv(currentEnv)),
46
43
  );
47
44
 
48
- /** Constructor shape shared by Effect-backed Worker entrypoints. */
49
45
  export interface WorkerConstructor<Instance> {
50
46
  new (context: globalThis.ExecutionContext, env: WorkerEnv): Instance;
51
47
  }
52
48
 
53
- /**
54
- * Creates an `ExecutionContext` and drains all `waitUntil` work before the
55
- * returned effect completes, including when `use` fails or is interrupted.
56
- */
57
49
  export const withExecutionContext = Effect.fn("effect-cf/vitest/withExecutionContext")(function* <
58
50
  A,
59
51
  E,
@@ -86,10 +78,8 @@ export const fetch = Effect.fn("effect-cf/vitest/fetch")(function* <Instance ext
86
78
  );
87
79
  });
88
80
 
89
- /** Modules-format scheduled handler accepted by {@link scheduled}. */
90
81
  export type ScheduledHandler = NonNullable<globalThis.ExportedHandler<WorkerEnv>["scheduled"]>;
91
82
 
92
- /** Options used to construct a scheduled event controller. */
93
83
  export interface ScheduledOptions {
94
84
  readonly scheduledTime?: number | Date;
95
85
  readonly cron?: string;
@@ -134,7 +124,6 @@ type PagesEventContextInitData<Context> =
134
124
  : { readonly data: Data }
135
125
  : never;
136
126
 
137
- /** Initialization accepted by the Workers Vitest plugin for a Pages Function. */
138
127
  export type PagesEventContextInit<Function extends AnyPagesFunction> = PagesEventContextInitBase &
139
128
  PagesEventContextInitParams<Parameters<Function>[0]> &
140
129
  PagesEventContextInitData<Parameters<Function>[0]>;
@@ -162,7 +151,6 @@ interface QueueWorker {
162
151
  queue(batch: globalThis.MessageBatch): Promise<void>;
163
152
  }
164
153
 
165
- /** Input message accepted by the Workers Vitest plugin's `createMessageBatch` helper. */
166
154
  export type QueueMessage<Body> = {
167
155
  readonly id: string;
168
156
  readonly timestamp: Date;
@@ -172,7 +160,6 @@ export type QueueMessage<Body> = {
172
160
  /** Acknowledgement and retry result returned by the Workers Vitest plugin. */
173
161
  export type QueueResult = Awaited<ReturnType<typeof getQueueResult>>;
174
162
 
175
- /** Result of invoking a queue consumer through {@link queue}. */
176
163
  export interface QueueRunResult<Body> {
177
164
  readonly batch: globalThis.MessageBatch<Body>;
178
165
  readonly result: QueueResult;
@@ -221,32 +208,34 @@ export const runInDurableObject = Effect.fn("effect-cf/vitest/runInDurableObject
221
208
  stub: globalThis.DurableObjectStub<Object>,
222
209
  use: (instance: Object, state: DurableObjectStateService) => Effect.Effect<A, E, R>,
223
210
  ): Effect.fn.Return<A, E, Exclude<R, DurableObjectState>> {
224
- const context = yield* Effect.context<Exclude<R, DurableObjectState>>();
225
- const exit = yield* Effect.promise(() =>
226
- runInDurableObjectPromise(stub, (instance, nativeState) => {
227
- const state = fromDurableObjectState(nativeState);
228
- const effect = Effect.provideService(use(instance, state), DurableObjectState, state);
229
-
230
- return Effect.runPromiseExitWith(context)(effect);
231
- }),
232
- );
211
+ const exit = yield* runNativeCallback<A, E, Exclude<R, DurableObjectState>, Exit.Exit<A, E>>(
212
+ (run) =>
213
+ runInDurableObjectPromise(stub, (instance, nativeState) => {
214
+ const state = fromDurableObjectState(nativeState);
215
+
216
+ return run(
217
+ Effect.provideService(
218
+ Effect.suspend(() => use(instance, state)),
219
+ DurableObjectState,
220
+ state,
221
+ ),
222
+ );
223
+ }),
224
+ ).pipe(Effect.orDie);
233
225
 
234
226
  return yield* exit;
235
227
  });
236
228
 
237
- /** Immediately runs and removes a scheduled Durable Object alarm. */
238
229
  export const runDurableObjectAlarm = Effect.fn("effect-cf/vitest/runDurableObjectAlarm")(function* (
239
230
  stub: globalThis.DurableObjectStub<RpcDurableObject>,
240
231
  ) {
241
232
  return yield* Effect.promise(() => runDurableObjectAlarmPromise(stub));
242
233
  });
243
234
 
244
- /** Options controlling how Durable Object eviction handles WebSockets. */
245
235
  export interface DurableObjectEvictionOptions {
246
236
  readonly webSockets?: "close" | "hibernate";
247
237
  }
248
238
 
249
- /** Gracefully evicts one running Durable Object while preserving durable storage. */
250
239
  export const evictDurableObject = Effect.fn("effect-cf/vitest/evictDurableObject")(function* (
251
240
  stub: globalThis.DurableObjectStub<RpcDurableObject>,
252
241
  options?: DurableObjectEvictionOptions,
@@ -254,39 +243,33 @@ export const evictDurableObject = Effect.fn("effect-cf/vitest/evictDurableObject
254
243
  yield* Effect.promise(() => evictDurableObjectPromise(stub, options));
255
244
  });
256
245
 
257
- /** Lists all IDs created in a Durable Object namespace. */
258
246
  export const listDurableObjectIds = Effect.fn("effect-cf/vitest/listDurableObjectIds")(function* <
259
247
  Object extends RpcDurableObject | undefined,
260
248
  >(namespace: globalThis.DurableObjectNamespace<Object>) {
261
249
  return yield* Effect.promise(() => listDurableObjectIdsPromise(namespace));
262
250
  });
263
251
 
264
- /** Deletes data from all bindings attached to the current test Worker. */
265
252
  export const reset = Effect.fn("effect-cf/vitest/reset")(function* () {
266
253
  yield* Effect.promise(resetPromise);
267
254
  });
268
255
 
269
- /** Aborts all Durable Object instances without deleting their persisted data. */
270
256
  export const abortAllDurableObjects = Effect.fn("effect-cf/vitest/abortAllDurableObjects")(
271
257
  function* () {
272
258
  yield* Effect.promise(abortAllDurableObjectsPromise);
273
259
  },
274
260
  );
275
261
 
276
- /** Gracefully evicts all running Durable Objects while preserving durable storage. */
277
262
  export const evictAllDurableObjects = Effect.fn("effect-cf/vitest/evictAllDurableObjects")(
278
263
  function* (options?: DurableObjectEvictionOptions) {
279
264
  yield* Effect.promise(() => evictAllDurableObjectsPromise(options));
280
265
  },
281
266
  );
282
267
 
283
- /** A D1 migration accepted by the Workers Vitest plugin. */
284
268
  export interface D1Migration {
285
269
  readonly name: string;
286
270
  readonly queries: ReadonlyArray<string>;
287
271
  }
288
272
 
289
- /** Applies all D1 migrations that have not already been recorded. */
290
273
  export const applyD1Migrations = Effect.fn("effect-cf/vitest/applyD1Migrations")(function* (
291
274
  database: globalThis.D1Database,
292
275
  migrations: ReadonlyArray<D1Migration>,
@@ -347,19 +330,16 @@ type NativeWorkflowInstanceModifier = Parameters<
347
330
  Parameters<NativeWorkflowInstanceIntrospector["modify"]>[0]
348
331
  >[0];
349
332
 
350
- /** Identifies one occurrence of a Workflow step or sleep. */
351
333
  export interface WorkflowStepTarget {
352
334
  readonly name: string;
353
335
  readonly index?: number;
354
336
  }
355
337
 
356
- /** Event delivered to a mocked Workflow `waitForEvent` operation. */
357
338
  export interface WorkflowMockEvent {
358
339
  readonly type: string;
359
340
  readonly payload: unknown;
360
341
  }
361
342
 
362
- /** Effect-native Workflow behavior modifier. */
363
343
  export interface WorkflowInstanceModifier {
364
344
  readonly disableSleeps: (steps?: ReadonlyArray<WorkflowStepTarget>) => Effect.Effect<void>;
365
345
  readonly disableRetryDelays: (steps?: ReadonlyArray<WorkflowStepTarget>) => Effect.Effect<void>;
@@ -379,7 +359,6 @@ export interface WorkflowInstanceModifier {
379
359
 
380
360
  type WorkflowMockResult = Schema.Schema.Type<typeof Schema.Unknown>;
381
361
 
382
- /** Effect-native introspector for one Workflow instance. */
383
362
  export interface WorkflowInstanceIntrospector {
384
363
  readonly raw: NativeWorkflowInstanceIntrospector;
385
364
  readonly modify: <A, E, R>(
@@ -391,7 +370,6 @@ export interface WorkflowInstanceIntrospector {
391
370
  readonly getError: Effect.Effect<{ readonly name: string; readonly message: string }>;
392
371
  }
393
372
 
394
- /** Effect-native introspector for subsequently created Workflow instances. */
395
373
  export interface WorkflowIntrospector {
396
374
  readonly raw: NativeWorkflowIntrospector;
397
375
  readonly modifyAll: <A, E, R>(
@@ -422,15 +400,12 @@ const runWorkflowModifier = Effect.fnUntraced(function* <A, E, R>(
422
400
  register: (use: (modifier: NativeWorkflowInstanceModifier) => Promise<void>) => Promise<any>,
423
401
  use: (modifier: WorkflowInstanceModifier) => Effect.Effect<A, E, R>,
424
402
  ): Effect.fn.Return<A, E, R> {
425
- const context = yield* Effect.context<R>();
426
- const exit = yield* Effect.promise(async () => {
403
+ const exit = yield* runNativeCallback<A, E, R, Exit.Exit<A, E>>(async (run) => {
427
404
  let callbackExit: Exit.Exit<A, E> | undefined;
428
405
 
429
406
  try {
430
407
  await register(async (modifier) => {
431
- callbackExit = await Effect.runPromiseExitWith(context)(
432
- use(wrapWorkflowModifier(modifier)),
433
- );
408
+ callbackExit = await run(Effect.suspend(() => use(wrapWorkflowModifier(modifier))));
434
409
 
435
410
  if (Exit.isFailure(callbackExit)) {
436
411
  throw callbackExit;
@@ -449,7 +424,7 @@ const runWorkflowModifier = Effect.fnUntraced(function* <A, E, R>(
449
424
  }
450
425
 
451
426
  return callbackExit;
452
- });
427
+ }).pipe(Effect.orDie);
453
428
 
454
429
  return yield* exit;
455
430
  });
@@ -42,11 +42,8 @@ import { Data, Effect, Option, Predicate, Result, Schema as S } from "effect";
42
42
  * `Record<string, unknown>` side of `request.cf`.
43
43
  */
44
44
  export class InboundTransport extends S.Class<InboundTransport>("InboundTransport")({
45
- /** The HTTP protocol the client used, e.g. `"HTTP/2"` or `"HTTP/3"`. */
46
45
  httpProtocol: S.String,
47
- /** Client round-trip time; only present for QUIC (HTTP/3) connections. */
48
46
  clientQuicRtt: S.optional(S.Number),
49
- /** Client round-trip time; only present for TCP (HTTP/1.x, HTTP/2) connections. */
50
47
  clientTcpRtt: S.optional(S.Number),
51
48
  }) {}
52
49
 
@@ -63,18 +60,9 @@ export const inboundTransport = (
63
60
  request: Pick<CloudflareRequest, "cf">,
64
61
  ): Option.Option<InboundTransport> => Result.getSuccess(decodeInboundTransport(request.cf));
65
62
 
66
- /**
67
- * Returns `true` when the client reached Cloudflare's edge over QUIC
68
- * (HTTP/3). Prefers the documented QUIC signal (`clientQuicRtt` is only
69
- * present for QUIC connections) and falls back to the `httpProtocol` string.
70
- *
71
- * Note this describes the browser→edge hop only: the edge always terminates
72
- * QUIC, and the Worker still handles an ordinary `fetch` Request.
73
- */
74
63
  export const isHttp3 = (transport: InboundTransport): boolean =>
75
64
  transport.clientQuicRtt !== undefined || transport.httpProtocol === "HTTP/3";
76
65
 
77
- /** WebTransport-related capabilities of the current Workers runtime. */
78
66
  export interface Capabilities {
79
67
  /**
80
68
  * Whether Worker code can accept inbound WebTransport sessions. Typed as
@@ -91,7 +79,6 @@ export interface Capabilities {
91
79
  readonly outboundSessions: boolean;
92
80
  }
93
81
 
94
- /** Feature-detects the WebTransport capabilities of the current runtime. */
95
82
  export const capabilities: Effect.Effect<Capabilities> = Effect.sync(() => {
96
83
  const runtime = globalThis;
97
84
 
@@ -103,10 +90,8 @@ export const capabilities: Effect.Effect<Capabilities> = Effect.sync(() => {
103
90
  };
104
91
  });
105
92
 
106
- /** WebTransport capability missing from the current Workers runtime. */
107
93
  export type WebTransportCapability = "inbound-sessions" | "outbound-sessions";
108
94
 
109
- /** Raised when a WebTransport capability is unavailable in this runtime. */
110
95
  export class WebTransportUnsupportedError extends Data.TaggedError("WebTransportUnsupportedError")<{
111
96
  readonly capability: WebTransportCapability;
112
97
  }> {