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
@@ -100,6 +100,17 @@ export class WorkspaceAssetsError extends Schema.TaggedError<WorkspaceAssetsErro
100
100
  },
101
101
  ) {}
102
102
 
103
+ /** Schema-serializable error while acquiring the Computer workspace client. */
104
+ export class WorkspaceAcquireError extends Schema.TaggedError<WorkspaceAcquireError>()(
105
+ "WorkspaceAcquireError",
106
+ {
107
+ operation: Schema.String,
108
+ message: Schema.String,
109
+ code: Schema.optionalKey(Schema.String),
110
+ cause: Schema.optionalKey(Schema.Defect({ includeStack: true })),
111
+ },
112
+ ) {}
113
+
103
114
  export type WorkspaceReadFileOptions = Omit<CloudflareReadFileOptions, "encoding">;
104
115
  export type ReadTextFileOptions = WorkspaceReadFileOptions;
105
116
  export type ListDirectoryOptions = CloudflareReaddirOptions;
@@ -128,7 +139,6 @@ export interface WorkspaceReadFile {
128
139
  ): Effect.Effect<ReadableStream<Uint8Array>, WorkspaceFsError>;
129
140
  }
130
141
 
131
- /** Complete Effect wrapper of `WorkspaceClient.fs`. */
132
142
  export interface ComputerWorkspaceFilesystem {
133
143
  readonly readFile: WorkspaceReadFile;
134
144
  readonly stat: (path: string) => Effect.Effect<WorkspaceEntryStat, WorkspaceFsError>;
@@ -173,7 +183,6 @@ export type WorkspaceGitDiffOptions = CloudflareGitDiffOptions;
173
183
  export type WorkspaceGitCommitOptions = CloudflareGitCommitOptions;
174
184
 
175
185
  export interface WorkspaceGitLogOptions extends Omit<CloudflareGitLogOptions, "depth"> {
176
- /** Preferred upstream name for the history bound. */
177
186
  readonly depth?: number;
178
187
  /** Consumer-port compatibility alias for `depth`. */
179
188
  readonly maxCount?: number;
@@ -235,7 +244,6 @@ export type WorkspaceExecOptions<Encoding extends WorkspaceExecEncoding = "utf8"
235
244
  "encoding" | "timeoutMs"
236
245
  > & {
237
246
  readonly encoding?: Encoding;
238
- /** Upstream runtime option name. */
239
247
  readonly timeoutMs?: number;
240
248
  /** Consumer-port compatibility alias for `timeoutMs`. */
241
249
  readonly timeoutMillis?: number;
@@ -334,7 +342,6 @@ export interface WorkspaceExecCollect {
334
342
  ): Effect.Effect<WorkspaceExecResult<"binary">, WorkspaceExecError>;
335
343
  }
336
344
 
337
- /** Complete Effect wrapper of `WorkspaceClient.runtime`. */
338
345
  export interface ComputerWorkspaceRuntime {
339
346
  readonly exec: WorkspaceExec;
340
347
  readonly execCollect: WorkspaceExecCollect;
@@ -349,7 +356,6 @@ export interface ComputerWorkspaceRuntime {
349
356
  ) => Effect.Effect<void, WorkspaceExecError>;
350
357
  }
351
358
 
352
- /** Effect wrapper over a configured local `Workspace.assets` client. */
353
359
  export interface ComputerWorkspaceAssets {
354
360
  readonly share: (
355
361
  path: string,
@@ -368,7 +374,6 @@ export type ComputerWorkspaceThink = {
368
374
  >;
369
375
  };
370
376
 
371
- /** Effect service over every client surface exposed by Cloudflare Computer. */
372
377
  export interface ComputerWorkspaceService {
373
378
  readonly fs: ComputerWorkspaceFilesystem;
374
379
  readonly git: ComputerWorkspaceGit;
@@ -377,7 +382,6 @@ export interface ComputerWorkspaceService {
377
382
  readonly assets: ComputerWorkspaceAssets | undefined;
378
383
  readonly think: ComputerWorkspaceThink | undefined;
379
384
 
380
- /** Flat filesystem aliases retained for mechanical consumer migration. */
381
385
  readonly readFile: WorkspaceReadFile;
382
386
  readonly stat: ComputerWorkspaceFilesystem["stat"];
383
387
  readonly lstat: ComputerWorkspaceFilesystem["lstat"];
@@ -395,7 +399,6 @@ export interface ComputerWorkspaceService {
395
399
  readonly chmod: ComputerWorkspaceFilesystem["chmod"];
396
400
  readonly symlink: ComputerWorkspaceFilesystem["symlink"];
397
401
 
398
- /** Flat runtime aliases retained for mechanical consumer migration. */
399
402
  readonly exec: WorkspaceExec;
400
403
  readonly execCollect: WorkspaceExecCollect;
401
404
  readonly getExec: WorkspaceGetExec;
@@ -477,6 +480,21 @@ const execError = (operation: string, cause: unknown) => {
477
480
  return WorkspaceExecError.make(properties);
478
481
  };
479
482
 
483
+ const acquireError = (operation: string, cause: unknown) => {
484
+ const properties: WorkspaceOperationErrorProperties = {
485
+ operation,
486
+ message: errorMessage(cause),
487
+ cause,
488
+ };
489
+ const code = errorCode(cause);
490
+
491
+ if (code !== undefined) {
492
+ properties.code = code;
493
+ }
494
+
495
+ return WorkspaceAcquireError.make(properties);
496
+ };
497
+
480
498
  const assetsError = (operation: string, path: string, cause: unknown) => {
481
499
  const properties: WorkspaceFsErrorProperties = {
482
500
  operation,
@@ -895,9 +913,6 @@ const wrapRun = <Encoding extends WorkspaceExecEncoding>(
895
913
  return { id: handle.id, backend: handle.backend, events, result, kill };
896
914
  };
897
915
 
898
- const releaseRun = (handle: CloudflareWorkspaceRuntimeExecHandle<"utf8" | undefined>) =>
899
- Effect.sync(() => handle[Symbol.dispose]());
900
-
901
916
  const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntime => {
902
917
  const execImplementation = Effect.fn("ComputerWorkspace.runtime.exec", {
903
918
  attributes: { family: "runtime", operation: "exec" },
@@ -913,7 +928,7 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
913
928
  : undefined;
914
929
 
915
930
  yield* Effect.annotateCurrentSpan({ backend: options?.backend, runId: options?.id });
916
- const handle = yield* Effect.acquireRelease(
931
+ const handle = yield* Effect.acquireDisposable(
917
932
  Effect.tryPromise({
918
933
  try: () => {
919
934
  if (Predicate.isString(source)) {
@@ -934,7 +949,6 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
934
949
  },
935
950
  catch: (cause) => execError("exec", cause),
936
951
  }),
937
- releaseRun,
938
952
  );
939
953
 
940
954
  // SAFETY: runtimeExecOptions carries Encoding to Cloudflare's corresponding handle overload.
@@ -953,12 +967,11 @@ const makeRuntime = (client: CloudflareWorkspaceClient): ComputerWorkspaceRuntim
953
967
  options?: WorkspaceGetExecOptions<Encoding>,
954
968
  ) {
955
969
  yield* Effect.annotateCurrentSpan({ backend: options?.backend, runId: id });
956
- const handle = yield* Effect.acquireRelease(
970
+ const handle = yield* Effect.acquireDisposable(
957
971
  Effect.tryPromise({
958
972
  try: () => client.runtime.getExec(id, runtimeGetOptions(options)),
959
973
  catch: (cause) => execError("getExec", cause),
960
974
  }),
961
- releaseRun,
962
975
  );
963
976
 
964
977
  // SAFETY: runtimeGetOptions carries Encoding to Cloudflare's corresponding handle overload.
@@ -1169,7 +1182,6 @@ export const fromWorkspaceClient = (
1169
1182
  };
1170
1183
  };
1171
1184
 
1172
- /** Effect service for the workspace owned by the enclosing Durable Object. */
1173
1185
  export class ComputerWorkspace extends Context.Service<
1174
1186
  ComputerWorkspace,
1175
1187
  ComputerWorkspaceService
@@ -1178,25 +1190,31 @@ export class ComputerWorkspace extends Context.Service<
1178
1190
  * Acquires the single workspace registered by `withComputerWorkspace` and
1179
1191
  * releases its RPC client when the layer scope closes.
1180
1192
  */
1181
- static readonly layer: Layer.Layer<ComputerWorkspace, never, DurableObjectState> = Layer.effect(
1182
- ComputerWorkspace,
1183
- Effect.gen(function* () {
1184
- const state = yield* DurableObjectState;
1185
- const host = HostRegistry.lookup(state.raw);
1186
-
1187
- if (host === undefined) {
1188
- return yield* Effect.die(
1189
- "ComputerWorkspace.layer requires a Durable Object class built with withComputerWorkspace",
1193
+ static readonly layer: Layer.Layer<ComputerWorkspace, WorkspaceAcquireError, DurableObjectState> =
1194
+ Layer.effect(
1195
+ ComputerWorkspace,
1196
+ Effect.gen(function* () {
1197
+ const state = yield* DurableObjectState;
1198
+ const host = HostRegistry.lookup(state.raw);
1199
+
1200
+ if (host === undefined) {
1201
+ return yield* Effect.die(
1202
+ "ComputerWorkspace.layer requires a Durable Object class built with withComputerWorkspace",
1203
+ );
1204
+ }
1205
+
1206
+ const computer = yield* Effect.tryPromise({
1207
+ try: () => import("@cloudflare/computer"),
1208
+ catch: (cause) => acquireError("import", cause),
1209
+ });
1210
+ const client = yield* Effect.acquireDisposable(
1211
+ Effect.tryPromise({
1212
+ try: () => computer.getWorkspace(host),
1213
+ catch: (cause) => acquireError("getWorkspace", cause),
1214
+ }),
1190
1215
  );
1191
- }
1192
-
1193
- const computer = yield* Effect.promise(() => import("@cloudflare/computer"));
1194
- const client = yield* Effect.acquireRelease(
1195
- Effect.promise(() => computer.getWorkspace(host)),
1196
- (workspace) => Effect.sync(() => workspace[Symbol.dispose]()),
1197
- );
1198
1216
 
1199
- return ComputerWorkspace.of(fromWorkspaceClient(client));
1200
- }),
1201
- );
1217
+ return ComputerWorkspace.of(fromWorkspaceClient(client));
1218
+ }),
1219
+ );
1202
1220
  }
@@ -47,7 +47,6 @@ export type {
47
47
  WorkspaceSpan,
48
48
  };
49
49
 
50
- /** The value of a `worker_loaders` binding. */
51
50
  export type DynamicWorkerLoader = WorkerShellLoader;
52
51
 
53
52
  /** Default author and committer identity for workspace Git operations. */
@@ -75,21 +74,14 @@ export interface ComputerWorkspaceShellOptions extends Omit<
75
74
  "source" | "loader" | "workspace" | "ctx" | "egress"
76
75
  > {
77
76
  readonly loader: DynamicWorkerLoader;
78
- /** Durable Object namespace binding that points back to the wrapped class. */
79
77
  readonly hostBinding: string;
80
- /** Current Durable Object id, normally `ctx.id.toString()`. */
81
78
  readonly hostId: string;
82
- /** Current Durable Object execution context, normally `ctx`. */
83
79
  readonly executionContext: unknown;
84
- /** Dynamic Worker egress policy. Defaults to `{ mode: "none" }`. */
85
80
  readonly egress?: WorkspaceEgressPolicy;
86
81
  }
87
82
 
88
- /** Configuration accepted by {@link withComputerWorkspace}. */
89
83
  export interface ComputerWorkspaceHostConfig {
90
- /** Native SQLite-backed Durable Object storage (`ctx.storage`). */
91
84
  readonly storage: globalThis.DurableObjectStorage | DurableObjectStorageLike;
92
- /** Stable workspace id used by mounts and as the default Artifacts session. */
93
85
  readonly sessionId?: string;
94
86
  readonly gitIdentity?: ComputerWorkspaceGitIdentity;
95
87
  /** Override the default `createGitClient()` factory. */
@@ -6,13 +6,10 @@ import * as ErrorMessage from "./internal/ErrorMessage";
6
6
 
7
7
  const expectedContainerNamespace = "Container namespace binding with getByName()";
8
8
 
9
- /** Named signal accepted by Cloudflare Container instances. */
10
9
  export type ContainerSignal = "SIGKILL" | "SIGINT" | "SIGTERM";
11
10
 
12
- /** Named or numeric signal accepted by Container `stop()`. */
13
11
  export type ContainerStopSignal = ContainerSignal | number;
14
12
 
15
- /** Per-instance Container startup configuration. */
16
13
  export interface ContainerStartOptions {
17
14
  readonly envVars?: Record<string, string>;
18
15
  readonly entrypoint?: Array<string>;
@@ -34,7 +31,6 @@ export interface ContainerReadinessOptions {
34
31
  readonly waitInterval?: number;
35
32
  }
36
33
 
37
- /** Options for starting a Container and waiting for its ports. */
38
34
  export interface ContainerStartAndWaitForPortsOptions {
39
35
  readonly startOptions?: ContainerStartOptions;
40
36
  readonly ports?: number | Array<number>;
@@ -77,7 +73,6 @@ export interface ContainerStub {
77
73
  removeDeniedHost(hostname: string): Promise<void>;
78
74
  }
79
75
 
80
- /** Native Container namespace binding shape. */
81
76
  export interface ContainerNamespaceResource {
82
77
  getByName(
83
78
  name: string,
@@ -85,18 +80,14 @@ export interface ContainerNamespaceResource {
85
80
  ): ContainerStub;
86
81
  }
87
82
 
88
- /** Exact native stub type produced by a Container namespace binding. */
89
83
  export type ContainerStubOf<Namespace extends ContainerNamespaceResource> = ReturnType<
90
84
  Namespace["getByName"]
91
85
  >;
92
86
 
93
- /** Container namespace binding metadata. */
94
87
  export interface ContainerNamespaceDefinition {
95
- /** Binding name as configured in `wrangler.jsonc`. */
96
88
  readonly binding: string;
97
89
  }
98
90
 
99
- /** Failure raised when invoking a named Container operation. */
100
91
  export class ContainerOperationError extends Data.TaggedError("ContainerOperationError")<{
101
92
  readonly binding: string;
102
93
  readonly instance: string;
@@ -108,7 +99,6 @@ export class ContainerOperationError extends Data.TaggedError("ContainerOperatio
108
99
  }
109
100
  }
110
101
 
111
- /** Effect-wrapped client for one named Container instance. */
112
102
  export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerStub> {
113
103
  readonly rawUnsafe: Effect.Effect<Stub, ContainerOperationError>;
114
104
  readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
@@ -140,7 +130,6 @@ export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerS
140
130
  readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
141
131
  }
142
132
 
143
- /** Effect client for a Container namespace binding. */
144
133
  export interface ContainerNamespaceClient<
145
134
  Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
146
135
  > {
@@ -433,14 +422,12 @@ export const make = <Id extends string>(id: Id) => Tag<ContainerNamespaceService
433
422
 
434
423
  declare const ContainerNamespaceServiceTypeId: unique symbol;
435
424
 
436
- /** Nominal service marker for Container namespaces created with {@link make}. */
437
425
  export interface ContainerNamespaceService<Id extends string> {
438
426
  readonly [ContainerNamespaceServiceTypeId]: {
439
427
  readonly id: Id;
440
428
  };
441
429
  }
442
430
 
443
- /** Creates a typed Effect service for a Cloudflare Container namespace. */
444
431
  export const Tag =
445
432
  <Self, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource>() =>
446
433
  <Id extends string>(id: Id) => {
package/src/D1.ts CHANGED
@@ -10,18 +10,14 @@ const TypeId = "~effect-cf/D1" as const;
10
10
  export type TypeId = typeof TypeId;
11
11
  const expectedD1Database = "D1 database binding with prepare(), batch(), and exec()";
12
12
 
13
- /** Typed D1 binding definition. */
14
13
  export interface D1Definition {
15
- /** Binding name as configured in `wrangler.jsonc`. */
16
14
  readonly binding: string;
17
15
  }
18
16
 
19
- /** Options forwarded to `@effect/sql-d1` when building a SQL client layer. */
20
17
  export type D1SqlLayerOptions = Omit<D1Client.D1ClientConfig, "db">;
21
18
 
22
19
  declare const D1ServiceTypeId: unique symbol;
23
20
 
24
- /** Nominal service marker for D1 services created with {@link make}. */
25
21
  export interface D1Service<Id extends string> {
26
22
  readonly [D1ServiceTypeId]: {
27
23
  readonly id: Id;
@@ -36,27 +32,9 @@ const isD1Database = <Candidate>(value: Candidate): value is Candidate & D1Datab
36
32
  Predicate.hasProperty(value, "exec") &&
37
33
  Predicate.isFunction(value.exec);
38
34
 
39
- /**
40
- * Creates a typed D1 service tag plus Effect helpers.
41
- */
42
35
  export const make = <Id extends string>(id: Id, definition: D1Definition) =>
43
36
  Service<D1Service<Id>>()(id, definition);
44
37
 
45
- /**
46
- * Builds a D1 service around a Cloudflare D1 database binding.
47
- *
48
- * The returned service exposes the raw `D1Database` binding and `sqlLayer(...)`
49
- * for providing `effect/unstable/sql` via `@effect/sql-d1`.
50
- *
51
- * @example
52
- * ```ts
53
- * class TodoDatabase extends D1.Service<TodoDatabase>()("TodoDatabase", {
54
- * binding: "TODO_DB",
55
- * }) {}
56
- *
57
- * const SqlLive = TodoDatabase.sqlLayer();
58
- * ```
59
- */
60
38
  export const Service =
61
39
  <Self>() =>
62
40
  <Id extends string>(id: Id, definition: D1Definition) => {
@@ -1,34 +1,38 @@
1
1
  import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
2
- import { Effect, Layer, type ManagedRuntime, type Scope } from "effect";
2
+ import { Effect, Layer, type ManagedRuntime, type Scope, Tracer } from "effect";
3
3
 
4
4
  import { NativeRequest } from "./Worker";
5
5
  import { WorkerEnvironment, type WorkerEnv } from "./Environment";
6
6
  import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
7
7
  import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
8
+ import * as RpcDefinition from "./RpcDefinition";
8
9
  import * as Entrypoint from "./internal/Entrypoint";
9
10
  import * as Runtime from "./internal/Runtime";
10
11
  import * as Telemetry from "./internal/Telemetry";
12
+ import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
11
13
 
12
- const reservedMethodNames = new Set<string>([
13
- "constructor",
14
- "dup",
15
- "fetch",
16
- "alarm",
17
- "webSocketMessage",
18
- "webSocketClose",
19
- "webSocketError",
20
- ]);
14
+ const reservedMethodNames: ReadonlySet<string> = RpcDefinition.reservedMethodNames;
21
15
 
22
16
  type RuntimeContext<ROut> = DurableObjectState | WorkerEnvironment | ROut;
23
17
 
24
18
  type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
25
19
 
26
20
  type FetchContext<ROut> = HandlerContext<ROut> | NativeRequest;
27
- type RunOptions = {
21
+ /** Metadata available before an event effect or its event layer starts. */
22
+ export interface RunOptions {
28
23
  readonly eventLayer?: boolean;
29
- };
24
+ readonly event?:
25
+ | "fetch"
26
+ | "rpc"
27
+ | "alarm"
28
+ | "webSocketMessage"
29
+ | "webSocketClose"
30
+ | "webSocketError";
31
+ readonly rpc?: RpcInvocationInfo;
32
+ }
30
33
 
31
- const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
34
+ /** Override to instrument the whole event, then call super[RunSymbol]. */
35
+ export const RunSymbol = Symbol.for("effect-cf/DurableObject/run");
32
36
 
33
37
  const scheduleTelemetryFlush: Effect.Effect<void, never, DurableObjectState> =
34
38
  Telemetry.scheduleTelemetryFlush((flush) =>
@@ -74,9 +78,6 @@ export type RpcHandlers<ROut, Api> = {
74
78
  : never;
75
79
  };
76
80
 
77
- /**
78
- * Options for creating a Durable Object class backed by Effect handlers.
79
- */
80
81
  export interface DurableObjectOptions<
81
82
  RRuntime,
82
83
  REvent = never,
@@ -113,15 +114,10 @@ export interface DurableObjectOptions<
113
114
  * does not.
114
115
  */
115
116
  readonly rpc?: Rpc;
116
- /** Optional fetch handler for HTTP/WebSocket requests. */
117
+ /** Accept live native trace metadata. The client must also enable rpcTracing. */
118
+ readonly rpcTracing?: ReceiverOptions;
117
119
  readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
118
- /**
119
- * Optional logical alarm processing effect.
120
- *
121
- * This runs before `alarm` and should be built with helpers such as
122
- * `DurableObjectAlarm.processDue(...)` so the reusable scheduler stays inside
123
- * the Durable Object's single managed runtime boundary.
124
- */
120
+
125
121
  readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<RRuntime | REvent>>;
126
122
  /**
127
123
  * Optional raw alarm handler.
@@ -156,10 +152,19 @@ export interface DurableObjectOptions<
156
152
  export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
157
153
  state: globalThis.DurableObjectState,
158
154
  env: WorkerEnv,
159
- ) => CloudflareDurableObject<WorkerEnv> & DurableObjectRpcApi<Rpc, ROut>;
155
+ ) => CloudflareDurableObject<WorkerEnv> &
156
+ DurableObjectRpcApi<Rpc, ROut> & {
157
+ [RunSymbol]<A, E>(
158
+ effect: Effect.Effect<A, E, HandlerContext<ROut>>,
159
+ options?: RunOptions,
160
+ ): Promise<A>;
161
+ };
160
162
 
161
163
  /**
162
- * Creates a Durable Object class backed by a single managed Effect runtime.
164
+ * Builds a Durable Object whose base `layer` lives for the in-memory instance.
165
+ * Cloudflare does not expose an eviction or shutdown hook, so finalizers in
166
+ * that layer are not guaranteed to run. Put resources that require timely
167
+ * release in `eventLayer`, whose scope closes after each handled event.
163
168
  */
164
169
  export const make = <
165
170
  ROut,
@@ -195,6 +200,8 @@ export const make = <
195
200
  runOptions: RunOptions = {},
196
201
  ): Promise<A> {
197
202
  const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
203
+ const parent = runOptions.rpc?.parent;
204
+ const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
198
205
 
199
206
  if (eventLayer === undefined) {
200
207
  // SAFETY: event-layer bypass is used only for initialize; otherwise an absent layer means REvent is never.
@@ -202,6 +209,8 @@ export const make = <
202
209
  this.runtime,
203
210
  // @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
204
211
  effect as Effect.Effect<A, E, HandlerContext<ROut>>,
212
+ undefined,
213
+ parentSpan,
205
214
  );
206
215
  }
207
216
 
@@ -212,7 +221,7 @@ export const make = <
212
221
  REvent,
213
222
  EventLayerError,
214
223
  LayerError
215
- >(this.runtime, effect, eventLayer);
224
+ >(this.runtime, effect, eventLayer, parentSpan);
216
225
  }
217
226
 
218
227
  fetch(request: Request): Promise<Response> {
@@ -222,7 +231,9 @@ export const make = <
222
231
  return Promise.resolve(new Response("Not Found", { status: 404 }));
223
232
  }
224
233
 
225
- return this[RunSymbol](Effect.provideService(fetchHandler, NativeRequest, request));
234
+ return this[RunSymbol](Effect.provideService(fetchHandler, NativeRequest, request), {
235
+ event: "fetch",
236
+ });
226
237
  }
227
238
 
228
239
  alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void {
@@ -237,13 +248,17 @@ export const make = <
237
248
  : (logicalAlarms ?? rawAlarm);
238
249
 
239
250
  if (alarmEffect !== undefined) {
240
- return this[RunSymbol](alarmEffect.pipe(Effect.onExit(() => scheduleTelemetryFlush)));
251
+ return this[RunSymbol](alarmEffect.pipe(Effect.onExit(() => scheduleTelemetryFlush)), {
252
+ event: "alarm",
253
+ });
241
254
  }
242
255
  }
243
256
 
244
257
  webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void> | void {
245
258
  if (options.webSocketMessage !== undefined) {
246
- return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message));
259
+ return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message), {
260
+ event: "webSocketMessage",
261
+ });
247
262
  }
248
263
  }
249
264
 
@@ -256,13 +271,16 @@ export const make = <
256
271
  if (options.webSocketClose !== undefined) {
257
272
  return this[RunSymbol](
258
273
  options.webSocketClose(fromWebSocket(socket), code, reason, wasClean),
274
+ { event: "webSocketClose" },
259
275
  );
260
276
  }
261
277
  }
262
278
 
263
279
  webSocketError(socket: WebSocket, cause: unknown): Promise<void> | void {
264
280
  if (options.webSocketError !== undefined) {
265
- return this[RunSymbol](options.webSocketError(fromWebSocket(socket), cause));
281
+ return this[RunSymbol](options.webSocketError(fromWebSocket(socket), cause), {
282
+ event: "webSocketError",
283
+ });
266
284
  }
267
285
  }
268
286
  }
@@ -272,8 +290,9 @@ export const make = <
272
290
  EffectDurableObject.prototype,
273
291
  options.rpc,
274
292
  reservedMethodNames,
275
- (self, effect) => self[RunSymbol](effect),
293
+ (self, effect, rpc) => self[RunSymbol](effect, { event: "rpc", rpc }),
276
294
  () => scheduleTelemetryFlush,
295
+ options.rpcTracing,
277
296
  );
278
297
 
279
298
  return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(