effect-cf 0.33.0 → 0.34.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 (73) hide show
  1. package/CHANGELOG.md +12 -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 +1 -10
  10. package/dist/ComputerWorkspace.mjs +1 -2
  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-BpRjz6-q.d.mts → DurableObjectState-CrEunfQ-.d.mts} +82 -252
  14. package/dist/{DurableObjectState-CVLLse69.mjs → DurableObjectState-De4ZojhY.mjs} +1 -39
  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 +2 -17
  23. package/dist/{WorkflowBinding-lBR4_-op.d.mts → WorkflowBinding-BumVQTuW.d.mts} +2 -5
  24. package/dist/index.d.mts +34 -480
  25. package/dist/index.mjs +174 -312
  26. package/package.json +1 -1
  27. package/src/AiGateway.ts +0 -6
  28. package/src/AnalyticsEngine.ts +0 -9
  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 +3 -39
  34. package/src/ComputerArtifacts.ts +0 -2
  35. package/src/ComputerWorkspace.ts +0 -9
  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 +45 -25
  40. package/src/DurableObjectAlarm.ts +6 -210
  41. package/src/DurableObjectDefinition.ts +8 -20
  42. package/src/DurableObjectNamespace.ts +57 -112
  43. package/src/DurableObjectRpcWebSocket.ts +3 -19
  44. package/src/DurableObjectSqlite.ts +0 -12
  45. package/src/DurableObjectState.ts +0 -18
  46. package/src/DurableObjectStorage.ts +2 -35
  47. package/src/DurableObjectWebSocket.ts +0 -31
  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 +0 -2
  57. package/src/RpcTracing.ts +133 -0
  58. package/src/Sandbox.ts +0 -30
  59. package/src/ServiceBinding.ts +48 -125
  60. package/src/Vectorize.ts +0 -5
  61. package/src/Vitest.ts +0 -28
  62. package/src/WebTransport.ts +0 -15
  63. package/src/Worker.ts +36 -57
  64. package/src/WorkerDefinition.ts +5 -14
  65. package/src/WorkersAi.ts +0 -4
  66. package/src/Workflow.ts +0 -5
  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/RpcInvocation.ts +18 -1
  72. package/src/internal/Runtime.ts +12 -7
  73. package/src/internal/WorkerContext.ts +0 -11
package/src/Worker.ts CHANGED
@@ -1,26 +1,29 @@
1
1
  import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
2
- import { type Cause, Context, Effect, Layer, type ManagedRuntime, type Scope } from "effect";
2
+ import {
3
+ type Cause,
4
+ Context,
5
+ Effect,
6
+ Layer,
7
+ type ManagedRuntime,
8
+ type Scope,
9
+ Tracer,
10
+ } from "effect";
3
11
  import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
4
12
 
5
13
  import { WorkerEnvironment, type WorkerEnv } from "./Environment";
6
14
  import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
7
15
  import type * as RpcDefinition from "./RpcDefinition";
16
+ import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
8
17
  import * as Entrypoint from "./internal/Entrypoint";
9
18
  import * as Runtime from "./internal/Runtime";
10
19
  import * as Telemetry from "./internal/Telemetry";
11
20
  import { fromExecutionContext } from "./internal/WorkerContext";
12
21
 
13
- /**
14
- * Access to Cloudflare's native `ExecutionContext`.
15
- */
16
22
  export class ExecutionContext extends Context.Service<
17
23
  ExecutionContext,
18
24
  globalThis.ExecutionContext
19
25
  >()("effect-cf/ExecutionContext") {}
20
26
 
21
- /**
22
- * Options for {@link WorkerContextService.waitUntil}.
23
- */
24
27
  export interface WorkerContextWaitUntilOptions<E, R> {
25
28
  /**
26
29
  * Failure mode for the background effect: `"observe"` (default) logs or
@@ -28,13 +31,9 @@ export interface WorkerContextWaitUntilOptions<E, R> {
28
31
  * native `waitUntil` promise.
29
32
  */
30
33
  readonly mode?: "observe" | "propagate";
31
- /** Custom failure handler for the background effect. */
32
34
  readonly onFailure?: (cause: Cause.Cause<E>) => Effect.Effect<void, never, R>;
33
35
  }
34
36
 
35
- /**
36
- * Effect wrapper around `ExecutionContext` background APIs.
37
- */
38
37
  export interface WorkerContextService {
39
38
  readonly raw: globalThis.ExecutionContext;
40
39
  waitUntil<A, E, R, R2 = never>(
@@ -46,36 +45,17 @@ export interface WorkerContextService {
46
45
  options?: Omit<WorkerContextWaitUntilOptions<E, R2>, "mode">,
47
46
  ): Effect.Effect<void, never, R | R2>;
48
47
  readonly passThroughOnException: Effect.Effect<void>;
49
- /** Abort the current stateless Worker invocation (`ExecutionContext.abort`). */
50
48
  abort(reason?: string): Effect.Effect<void>;
51
49
  }
52
50
 
53
- /**
54
- * Service used inside handlers to schedule background work via `waitUntil`.
55
- *
56
- * @example
57
- * ```ts
58
- * const handler = Effect.gen(function* () {
59
- * const ctx = yield* Worker.WorkerContext;
60
- * yield* ctx.waitUntil(Effect.log("flush analytics"));
61
- * return new Response("ok");
62
- * });
63
- * ```
64
- */
65
51
  export class WorkerContext extends Context.Service<WorkerContext, WorkerContextService>()(
66
52
  "effect-cf/WorkerContext",
67
53
  ) {}
68
54
 
69
- /**
70
- * Access to the incoming `Request` currently handled by a worker or Durable Object fetch.
71
- */
72
55
  export class NativeRequest extends Context.Service<NativeRequest, Request>()(
73
56
  "effect-cf/NativeRequest",
74
57
  ) {}
75
58
 
76
- /**
77
- * Returns `true` when the request is a websocket upgrade request.
78
- */
79
59
  export const isWebSocketUpgrade = (request: Request): boolean =>
80
60
  request.headers.get("Upgrade")?.toLowerCase() === "websocket";
81
61
 
@@ -120,14 +100,16 @@ type WorkerFetchContext<ROut> =
120
100
  type WorkerRpcContext<ROut> = WorkerBaseContext<ROut> | Scope.Scope;
121
101
 
122
102
  type RuntimeContext<ROut> = WorkerBaseContext<ROut>;
123
- const RunSymbol = Symbol.for("effect-cf/Worker/run");
103
+ /** Override to instrument the whole event, then call super[RunSymbol]. */
104
+ export const RunSymbol = Symbol.for("effect-cf/Worker/run");
124
105
  const FetchSymbol = Symbol.for("effect-cf/Worker/fetch");
125
106
 
126
- /**
127
- * Successful result of a worker fetch handler: either a native `Response`
128
- * (returned to Cloudflare untouched) or an Effect `HttpServerResponse`
129
- * (rendered through the Effect HTTP pipeline).
130
- */
107
+ /** Metadata available before an event effect or its event layer starts. */
108
+ export interface RunOptions {
109
+ readonly event?: "fetch" | "rpc" | "queue";
110
+ readonly rpc?: RpcInvocationInfo;
111
+ }
112
+
131
113
  export type WorkerFetchSuccess = Response | HttpServerResponse.HttpServerResponse;
132
114
 
133
115
  /**
@@ -173,9 +155,6 @@ export type RpcHandlers<ROut, Api> = {
173
155
  : never;
174
156
  };
175
157
 
176
- /**
177
- * Options for creating a worker class with Effect handlers.
178
- */
179
158
  export interface WorkerOptions<
180
159
  RRuntime,
181
160
  REvent = never,
@@ -191,13 +170,11 @@ export interface WorkerOptions<
191
170
  * flush capped at two seconds; queue handlers do not flush automatically.
192
171
  */
193
172
  readonly eventLayer?: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
194
- /** Main request handler. */
195
173
  readonly fetch?: Effect.Effect<
196
174
  WorkerFetchSuccess,
197
175
  unknown,
198
176
  WorkerFetchContext<RRuntime | REvent>
199
177
  >;
200
- /** Queue consumer handler invoked per message batch. */
201
178
  readonly queue?: QueueHandler<RRuntime | REvent>;
202
179
  /**
203
180
  * Optional RPC methods exposed as class instance methods.
@@ -207,6 +184,8 @@ export interface WorkerOptions<
207
184
  * flush silently settles within two seconds even if the exporter does not.
208
185
  */
209
186
  readonly rpc?: Rpc;
187
+ /** Accept live native trace metadata. The client must also enable rpcTracing. */
188
+ readonly rpcTracing?: ReceiverOptions;
210
189
  }
211
190
 
212
191
  type WorkerOptionsWithEventLayer<
@@ -241,6 +220,10 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
241
220
  ) => CloudflareWorkerEntrypoint<WorkerEnv> & {
242
221
  fetch(request: Request): Promise<Response>;
243
222
  queue(batch: globalThis.MessageBatch): Promise<void>;
223
+ [RunSymbol]<A, E>(
224
+ effect: Effect.Effect<A, E, WorkerRpcContext<ROut>>,
225
+ options?: RunOptions,
226
+ ): Promise<A>;
244
227
  [FetchSymbol](request: Request, requestContext: globalThis.ExecutionContext): Promise<Response>;
245
228
  } & WorkerRpcContract<Rpc, ROut>;
246
229
 
@@ -313,19 +296,8 @@ const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
313
296
  );
314
297
 
315
298
  /**
316
- * Creates a Cloudflare worker class backed by a single managed Effect runtime.
317
- *
318
- * The runtime layer is built when Cloudflare constructs the class and reused
319
- * for every event the instance handles. Layer finalizers do not run when the
320
- * isolate is evicted; Cloudflare provides no shutdown hook. Use `eventLayer`
321
- * for resources that must be finalized per event.
322
- *
323
- * @example
324
- * ```ts
325
- * export default Worker.make(Layer.empty, {
326
- * fetch: Effect.succeed(new Response("ok")),
327
- * });
328
- * ```
299
+ * The layer is built once per Worker instance and reused for its events.
300
+ * Eviction skips finalizers, so use `eventLayer` for per-event resources.
329
301
  */
330
302
  export function make<ROut, LayerError>(
331
303
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
@@ -387,8 +359,11 @@ export function make<
387
359
 
388
360
  [RunSymbol]<A, E>(
389
361
  effect: Effect.Effect<A, E, RuntimeContext<ROut> | REvent | Scope.Scope>,
362
+ runOptions: RunOptions = {},
390
363
  ): Promise<A> {
391
364
  const eventLayer = options.eventLayer;
365
+ const parent = runOptions.rpc?.parent;
366
+ const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
392
367
 
393
368
  if (eventLayer === undefined) {
394
369
  // SAFETY: the public make overloads permit an absent event layer only when REvent is never.
@@ -396,6 +371,8 @@ export function make<
396
371
  this.runtime,
397
372
  // @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
398
373
  effect as Effect.Effect<A, E, RuntimeContext<ROut> | Scope.Scope>,
374
+ undefined,
375
+ parentSpan,
399
376
  );
400
377
  }
401
378
 
@@ -406,7 +383,7 @@ export function make<
406
383
  REvent,
407
384
  EventLayerError,
408
385
  LayerError
409
- >(this.runtime, effect, eventLayer);
386
+ >(this.runtime, effect, eventLayer, parentSpan);
410
387
  }
411
388
 
412
389
  fetch(request: Request): Promise<Response> {
@@ -437,6 +414,7 @@ export function make<
437
414
  Effect.tap(() => scheduleTelemetryFlush),
438
415
  Effect.provide(requestServices),
439
416
  ) as Effect.Effect<Response, unknown, RuntimeContext<ROut> | REvent | Scope.Scope>,
417
+ { event: "fetch" },
440
418
  );
441
419
  }
442
420
 
@@ -449,7 +427,7 @@ export function make<
449
427
 
450
428
  const messages = batch.messages.map((message) => fromMessage(message, message.body));
451
429
 
452
- return this[RunSymbol](queueHandler(fromMessageBatch(batch, messages)));
430
+ return this[RunSymbol](queueHandler(fromMessageBatch(batch, messages)), { event: "queue" });
453
431
  }
454
432
  }
455
433
 
@@ -458,8 +436,9 @@ export function make<
458
436
  EffectWorker.prototype,
459
437
  options.rpc,
460
438
  reservedMethodNames,
461
- (self, effect) => self[RunSymbol](effect),
439
+ (self, effect, rpc) => self[RunSymbol](effect, { event: "rpc", rpc }),
462
440
  () => scheduleTelemetryFlush,
441
+ options.rpcTracing,
463
442
  );
464
443
 
465
444
  return Entrypoint.assumeEntrypointClass<WorkerClass<Rpc, ROut | REvent>>(EffectWorker);
@@ -6,6 +6,7 @@ import * as WorkerEntrypoint from "./Worker";
6
6
  import type { WorkerRpcHandler } from "./Worker";
7
7
  import type * as Rpc from "./Rpc";
8
8
  import * as RpcDefinition from "./RpcDefinition";
9
+ import { recordDecodedArgs } from "./internal/RpcInvocation";
9
10
  import * as ServiceBinding from "./ServiceBinding";
10
11
 
11
12
  /**
@@ -37,7 +38,6 @@ export namespace Method {
37
38
  ? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
38
39
  : Array<S.Schema.Type<Args[number]>>;
39
40
 
40
- /** Method schemas cross the wire through their canonical JSON codec. */
41
41
  type EncodedArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = {
42
42
  [Index in keyof Args]: S.Json;
43
43
  };
@@ -153,6 +153,7 @@ export type Options<
153
153
 
154
154
  export type LayerOptions = {
155
155
  readonly binding: string;
156
+ readonly rpcTracing?: boolean;
156
157
  };
157
158
 
158
159
  export type TagClass<
@@ -236,19 +237,6 @@ export const method: {
236
237
  }): Method<Args, Success>;
237
238
  } = RpcDefinition.method;
238
239
 
239
- /**
240
- * Creates a typed worker RPC definition plus helpers for implementation and bindings.
241
- *
242
- * @example
243
- * ```ts
244
- * const CounterWorker = WorkerDefinition.make("CounterWorker", {
245
- * increment: WorkerDefinition.method({
246
- * args: [Schema.Number],
247
- * success: Schema.Number,
248
- * }),
249
- * });
250
- * ```
251
- */
252
240
  const makeDefinition = <Id extends string, const MethodDefinitions extends Methods>(
253
241
  id: Id,
254
242
  methods: MethodDefinitions & NoReservedMethods<MethodDefinitions>,
@@ -414,6 +402,9 @@ const wrapHandlers = <ROut, const Self extends Definition.Any>(
414
402
  wrapped[key] = (...args: Array<unknown>) =>
415
403
  Effect.gen(function* () {
416
404
  const decodedArgs = yield* RpcDefinition.decodeArgs(definition, key, args);
405
+
406
+ yield* recordDecodedArgs(decodedArgs);
407
+
417
408
  const value = yield* handler(...decodedArgs);
418
409
 
419
410
  return yield* RpcDefinition.encodeSuccess(definition, key, value);
package/src/WorkersAi.ts CHANGED
@@ -18,7 +18,6 @@ import * as ErrorMessage from "./internal/ErrorMessage";
18
18
 
19
19
  const expectedWorkersAiBinding = "Workers AI binding with run(), gateway(), and models()";
20
20
 
21
- /** Error raised when a Workers AI operation fails. */
22
21
  export class WorkersAiOperationError extends Data.TaggedError("WorkersAiOperationError")<{
23
22
  readonly binding: string;
24
23
  readonly operation: string;
@@ -29,9 +28,7 @@ export class WorkersAiOperationError extends Data.TaggedError("WorkersAiOperatio
29
28
  }
30
29
  }
31
30
 
32
- /** Typed Workers AI binding definition. */
33
31
  export interface WorkersAiDefinition {
34
- /** Binding name as configured in `wrangler.jsonc`. */
35
32
  readonly binding: string;
36
33
  }
37
34
 
@@ -117,7 +114,6 @@ export interface WorkersAiClient<ModelList extends AiModelListType = AiModels> {
117
114
 
118
115
  declare const WorkersAiServiceTypeId: unique symbol;
119
116
 
120
- /** Nominal service marker for Workers AI services created with {@link make}. */
121
117
  export interface WorkersAiService<Id extends string> {
122
118
  readonly [WorkersAiServiceTypeId]: {
123
119
  readonly id: Id;
package/src/Workflow.ts CHANGED
@@ -1,7 +1,3 @@
1
- /**
2
- * Effect wrapper for Cloudflare Workflows entrypoints and bindings; distinct
3
- * from Effect's runtime-agnostic `effect/unstable/workflow` cluster module.
4
- */
5
1
  import {
6
2
  WorkflowEntrypoint as CloudflareWorkflowEntrypoint,
7
3
  type WorkflowEvent as CloudflareWorkflowEvent,
@@ -46,7 +42,6 @@ export class WorkflowEvent extends Context.Service<WorkflowEvent, WorkflowEventS
46
42
 
47
43
  type RunWorkflowStepEffect = <A, E>(effect: Effect.Effect<A, E, never>) => Promise<A>;
48
44
 
49
- /** Failure raised when a Cloudflare Workflow step operation rejects. */
50
45
  export class WorkflowStepError extends Data.TaggedError("WorkflowStepError")<{
51
46
  readonly step?: string;
52
47
  readonly operation: string;
@@ -67,11 +67,8 @@ export interface WorkflowBindingDefinition<
67
67
  Payload extends RpcDefinition.ServiceFreeSchema,
68
68
  Result extends RpcDefinition.ServiceFreeSchema,
69
69
  > {
70
- /** Binding name as configured in `wrangler.jsonc`. */
71
70
  readonly binding: string;
72
- /** Codec used to encode payloads passed to `Workflow.create`. */
73
71
  readonly payload: Payload;
74
- /** Codec used to decode completed workflow status output. */
75
72
  readonly result: Result;
76
73
  }
77
74
 
package/src/index.ts CHANGED
@@ -27,6 +27,7 @@ export * as QueueDefinition from "./QueueDefinition";
27
27
  export * as R2 from "./R2";
28
28
  export * as Rpc from "./Rpc";
29
29
  export * as RpcDefinition from "./RpcDefinition";
30
+ export * as RpcTracing from "./RpcTracing";
30
31
  export * as ServiceBinding from "./ServiceBinding";
31
32
  export * as Vectorize from "./Vectorize";
32
33
  export * as WebTransport from "./WebTransport";
@@ -1,26 +1,44 @@
1
1
  import { Effect, Layer, Schema } from "effect";
2
2
 
3
3
  import * as RpcDefinition from "../RpcDefinition";
4
+ import { RpcTraceContext, type ReceiverOptions, type RpcInvocationInfo } from "../RpcTracing";
5
+ import { CurrentInvocation } from "./RpcInvocation";
4
6
 
5
7
  type AnyArgs = Array<any>;
6
8
  type EntrypointRpcMethod = (...args: AnyArgs) => Effect.Effect<any, any, any>;
7
9
 
8
10
  export type EntrypointRpc = Record<string, EntrypointRpcMethod>;
9
11
 
12
+ const isRpcTraceContext = Schema.is(RpcTraceContext);
13
+
10
14
  const invokeEntrypointRpcMethod = <Self>(
11
15
  self: Self,
12
16
  method: EntrypointRpcMethod,
13
17
  args: AnyArgs,
14
- run: (self: Self, effect: Effect.Effect<any, any, any>) => Promise<any>,
18
+ run: (self: Self, effect: Effect.Effect<any, any, any>, rpc: RpcInvocationInfo) => Promise<any>,
15
19
  onExit: ((self: Self) => Effect.Effect<void, never, any>) | undefined,
20
+ service: string,
21
+ key: string,
22
+ rpcTracing: ReceiverOptions | undefined,
16
23
  ): Promise<any> => {
17
- const handler = Effect.suspend(() => method(...args)).pipe(
24
+ const last = args.at(-1);
25
+ const parent = rpcTracing !== undefined && isRpcTraceContext(last) ? last : undefined;
26
+ const handlerArgs = parent === undefined ? args : args.slice(0, -1);
27
+ const invocation: CurrentInvocation["Service"] = {
28
+ service: rpcTracing?.service ?? service,
29
+ method: key,
30
+ args: handlerArgs,
31
+ parent,
32
+ };
33
+ const handler = Effect.suspend(() => method(...handlerArgs)).pipe(
34
+ Effect.provideService(CurrentInvocation, invocation),
18
35
  Effect.mapError(RpcDefinition.encodeWireError),
19
36
  );
20
37
 
21
38
  return run(
22
39
  self,
23
40
  onExit === undefined ? handler : handler.pipe(Effect.onExit(() => onExit(self))),
41
+ invocation,
24
42
  );
25
43
  };
26
44
 
@@ -36,8 +54,9 @@ export const defineEntrypointRpcMethods = <Self, Prototype extends object = obje
36
54
  prototype: Prototype,
37
55
  rpc: EntrypointRpc | undefined,
38
56
  reservedMethodNames: ReadonlySet<string>,
39
- run: (self: Self, effect: Effect.Effect<any, any, any>) => Promise<any>,
57
+ run: (self: Self, effect: Effect.Effect<any, any, any>, rpc: RpcInvocationInfo) => Promise<any>,
40
58
  onExit?: (self: Self) => Effect.Effect<void, never, any>,
59
+ rpcTracing?: ReceiverOptions,
41
60
  ): void => {
42
61
  const methods = rpc ?? {};
43
62
 
@@ -47,7 +66,7 @@ export const defineEntrypointRpcMethods = <Self, Prototype extends object = obje
47
66
  Object.defineProperty(prototype, key, {
48
67
  enumerable: true,
49
68
  value(this: Self, ...args: AnyArgs) {
50
- return invokeEntrypointRpcMethod(this, method, args, run, onExit);
69
+ return invokeEntrypointRpcMethod(this, method, args, run, onExit, target, key, rpcTracing);
51
70
  },
52
71
  });
53
72
  }
@@ -18,7 +18,6 @@ export const causeMessage = (cause: unknown): string => {
18
18
  }
19
19
  };
20
20
 
21
- /** Renders a violation list as a single `path: message; ...` line. */
22
21
  export const violationsMessage = (
23
22
  violations: ReadonlyArray<{ readonly path: string; readonly message: string }>,
24
23
  ): string => violations.map((violation) => `${violation.path}: ${violation.message}`).join("; ");
@@ -1,9 +1,26 @@
1
- import { Effect, Predicate, Schema } from "effect";
1
+ import { Context, Effect, Option, Predicate, Schema } from "effect";
2
2
 
3
3
  import type * as CloudflareRpc from "../Rpc";
4
+ import type { RpcInvocationInfo } from "../RpcTracing";
4
5
 
5
6
  type AnyArgs = Array<any>;
6
7
 
8
+ interface Invocation extends RpcInvocationInfo {
9
+ decodedArgs?: ReadonlyArray<unknown>;
10
+ }
11
+
12
+ /** Scoped to the incoming event effect, never the instance runtime or storage. */
13
+ export class CurrentInvocation extends Context.Service<CurrentInvocation, Invocation>()(
14
+ "effect-cf/internal/RpcInvocation",
15
+ ) {}
16
+
17
+ export const recordDecodedArgs = (args: ReadonlyArray<unknown>): Effect.Effect<void> =>
18
+ Effect.map(Effect.serviceOption(CurrentInvocation), (invocation) => {
19
+ if (Option.isSome(invocation)) {
20
+ invocation.value.decodedArgs = args;
21
+ }
22
+ });
23
+
7
24
  export type AsyncMethodKey<Api> = {
8
25
  [Key in keyof Api]-?: Key extends string
9
26
  ? Api[Key] extends (...args: AnyArgs) => Promise<any>
@@ -1,4 +1,4 @@
1
- import { ConfigProvider, Effect, Layer, ManagedRuntime, type Scope } from "effect";
1
+ import { ConfigProvider, Effect, Layer, ManagedRuntime, type Scope, type Tracer } from "effect";
2
2
 
3
3
  import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "../Environment";
4
4
  import { provideEntrypointServices } from "./Entrypoint";
@@ -26,17 +26,18 @@ export const makeEntrypointRuntime = <ROut, LayerError, Services>(
26
26
  return ManagedRuntime.make(provideEntrypointServices(layer, entrypointServices));
27
27
  };
28
28
 
29
- /** Runs an event effect whose requirements are already available in the runtime. */
30
29
  export function runEventPromise<A, E, R, LayerError>(
31
30
  runtime: ManagedRuntime.ManagedRuntime<R, LayerError>,
32
31
  effect: Effect.Effect<A, E, NoInfer<R> | Scope.Scope>,
33
32
  eventLayer?: undefined,
33
+ parent?: Tracer.AnySpan,
34
34
  ): Promise<A>;
35
35
  /** Builds and provides an event layer inside the event effect's scope. */
36
36
  export function runEventPromise<A, E, R, REvent, EventLayerError, LayerError>(
37
37
  runtime: ManagedRuntime.ManagedRuntime<R, LayerError>,
38
38
  effect: Effect.Effect<A, E, NoInfer<R> | REvent | Scope.Scope>,
39
39
  eventLayer: Layer.Layer<REvent, EventLayerError, NoInfer<R>>,
40
+ parent?: Tracer.AnySpan,
40
41
  ): Promise<A>;
41
42
  export function runEventPromise<A, E, R, REvent, EventLayerError, LayerError>(
42
43
  ...args:
@@ -44,20 +45,24 @@ export function runEventPromise<A, E, R, REvent, EventLayerError, LayerError>(
44
45
  runtime: ManagedRuntime.ManagedRuntime<R, LayerError>,
45
46
  effect: Effect.Effect<A, E, R | Scope.Scope>,
46
47
  eventLayer?: undefined,
48
+ parent?: Tracer.AnySpan,
47
49
  ]
48
50
  | readonly [
49
51
  runtime: ManagedRuntime.ManagedRuntime<R, LayerError>,
50
52
  effect: Effect.Effect<A, E, R | REvent | Scope.Scope>,
51
53
  eventLayer: Layer.Layer<REvent, EventLayerError, R>,
54
+ parent?: Tracer.AnySpan,
52
55
  ]
53
56
  ): Promise<A> {
54
- const [runtime, effect, eventLayer] = args;
57
+ const [runtime, effect, eventLayer, parent] = args;
55
58
 
56
59
  if (eventLayer === undefined) {
57
- return runtime.runPromise(Effect.scoped(effect));
60
+ const event = Effect.scoped(effect);
61
+
62
+ return runtime.runPromise(parent === undefined ? event : Effect.withParentSpan(event, parent));
58
63
  }
59
64
 
60
- return runtime.runPromise(
61
- Effect.scoped(effect.pipe(Effect.provide(eventLayer, { local: true }))),
62
- );
65
+ const event = Effect.scoped(effect.pipe(Effect.provide(eventLayer, { local: true })));
66
+
67
+ return runtime.runPromise(parent === undefined ? event : Effect.withParentSpan(event, parent));
63
68
  }
@@ -27,14 +27,6 @@ const failureHandler = <E, R>(
27
27
  ),
28
28
  );
29
29
 
30
- /**
31
- * Builds a `waitUntil` scheduler for any Cloudflare lifecycle that accepts
32
- * background promises (`ExecutionContext`, `DurableObjectState`).
33
- *
34
- * Background effects capture the calling fiber's context via `Effect.context`
35
- * before being registered, so the default module-level `Effect.runPromiseExit`
36
- * runner is sufficient; entrypoints may still pass a runtime-bound runner.
37
- */
38
30
  export const makeWaitUntilScheduler = (
39
31
  label: string,
40
32
  register: (promise: Promise<unknown>) => void,
@@ -77,9 +69,6 @@ export const makeWaitUntilScheduler = (
77
69
  );
78
70
  };
79
71
 
80
- /**
81
- * Builds the `WorkerContext` service from a native `ExecutionContext`.
82
- */
83
72
  export const fromExecutionContext = (
84
73
  ctx: globalThis.ExecutionContext,
85
74
  runPromiseExit: RunWaitUntilEffect = (effect) => Effect.runPromiseExit(effect),