effect-cf 0.37.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.39.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#140](https://github.com/danieljvdm/effect-cf/pull/140) [`3013c49`](https://github.com/danieljvdm/effect-cf/commit/3013c495a6e4392446a5661777f5eb0ffe4a828f) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Require the Effect 4.0.0-rc.112 family. Both packages now declare `effect@^4.0.0-rc.112`; effect-cf's SQL peers require the matching rc.112 line. Upgrade these dependencies together.
8
+
9
+ Fix Durable Object WebSocket RPC server startup with rc.112 and preserve the selected serialization's schema codecs for payloads, replies, and transport errors. JSON consumers can keep `RpcSerialization.layerJson`; no wire-format migration is required. The protocol also declares its existing ability to send server notifications. Hibernation attachments, resumable stream checkpoints, acknowledgements, and tracing behavior are unchanged.
10
+
11
+ The WebTransport socket adapter continues to use Effect's built-in RPC protocols and a framed serialization such as NDJSON. Its runtime API is unchanged.
12
+
13
+ ## 0.38.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [#137](https://github.com/danieljvdm/effect-cf/pull/137) [`8e35961`](https://github.com/danieljvdm/effect-cf/commit/8e3596157732b487f8fef5fd983a6c56a32e74ec) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add `CloudflareTracer.layer` to send existing Effect spans to Cloudflare Workers Observability. Provide it per invocation through `Worker.make`'s `eventLayer` and enable tracing in Wrangler to see Effect operations alongside automatic platform spans, with context preserved across concurrent fibers.
18
+
3
19
  ## 0.37.0
4
20
 
5
21
  ### Minor Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Cloudflare entrypoints and bindings as Effect services.
4
4
 
5
5
  ```sh
6
- npm install effect-cf "effect@^4.0.0-rc.110"
6
+ npm install effect-cf "effect@^4.0.0-rc.112"
7
7
  ```
8
8
 
9
9
  The repository tests against workerd `1.20260825.1` and `@cloudflare/workers-types@5.20260825.1`. Use `compatibility_date: "2026-08-25"` in Wrangler.
@@ -49,6 +49,51 @@ The [counter example](https://github.com/danieljvdm/effect-cf/tree/main/examples
49
49
 
50
50
  For atomic application writes and alarm changes, see the [alarm transaction example](tests/fixtures/alarm-transaction-consumer.ts) and [API contract](src/DurableObjectAlarm.ts).
51
51
 
52
+ ## Cloudflare Observability traces
53
+
54
+ `CloudflareTracer.layer` sends existing `Effect.withSpan` and named `Effect.fn`
55
+ spans to Cloudflare's trace waterfall, alongside automatic platform spans.
56
+
57
+ ```ts
58
+ import { Effect, Layer } from "effect";
59
+ import { CloudflareTracer, Worker } from "effect-cf";
60
+
61
+ export default Worker.make(Layer.empty, {
62
+ eventLayer: CloudflareTracer.layer,
63
+ fetch: Effect.sync(() => new Response("Hello")).pipe(Effect.withSpan("greet")),
64
+ });
65
+ ```
66
+
67
+ Enable tracing in `wrangler.jsonc`:
68
+
69
+ ```jsonc
70
+ {
71
+ "compatibility_date": "2026-08-25",
72
+ "observability": {
73
+ "traces": { "enabled": true },
74
+ },
75
+ }
76
+ ```
77
+
78
+ Build the layer per invocation with `eventLayer`. It captures the current
79
+ Cloudflare async context, so do not put it in a runtime layer cached across
80
+ requests. Standalone Effect programs can provide it around each invocation.
81
+ Nested spans, concurrent fibers, and resumed work restore the appropriate span
82
+ context. Cloudflare handles sampling and export; no exporter endpoint or flush
83
+ is needed.
84
+
85
+ String, number, and boolean attributes are forwarded. Other attributes, span
86
+ events, and links remain Effect-local. Completion is recorded as `effect.exit`
87
+ with `success`, `failure`, or `interrupted`; Cloudflare does not expose a setter
88
+ for native outcome status. Effect IDs remain separate from Cloudflare's opaque
89
+ trace/span IDs. Explicit external parents cannot join a Cloudflare trace by ID,
90
+ and `root: true` starts under the invocation's captured context.
91
+
92
+ This layer replaces the active Effect tracer. When combining it with
93
+ `CloudflareOtlp`, select only `logs` and/or `metrics` in the OTLP layer. See
94
+ [Cloudflare's custom span API](https://developers.cloudflare.com/workers/observability/traces/custom-spans/)
95
+ for platform limitations.
96
+
52
97
  ## Native RPC tracing
53
98
 
54
99
  `call()`, `scopedCall()`, and definition methods create one CLIENT span named `binding/method`, covering argument encoding, the native RPC wait, and success decoding. Raw `rpc()` retains Cloudflare's pipelined result without creating a span. Wrap its complete lifetime with `RpcTracing.withRpcClientSpan` when tracing raw calls.
package/dist/index.d.mts CHANGED
@@ -8,8 +8,8 @@ import { a as WorkflowInstanceCreateOptions, i as WorkflowInstanceCreateBatchOpt
8
8
  import { Cause, Config, Context, Data, DateTime, Duration, Effect, Layer, Option, Predicate, Redacted, Schema, Scope } from "effect";
9
9
  import { Headers as Headers$1, HttpClient, HttpClientResponse } from "effect/unstable/http";
10
10
  import { OtlpExporter, OtlpResource } from "effect/unstable/observability";
11
- import { D1Client } from "@effect/sql-d1";
12
11
  import { DurableObject, WorkflowEntrypoint, WorkflowEvent, WorkflowSleepDuration, WorkflowStep, WorkflowStepConfig, WorkflowStepContext, WorkflowStepEvent, WorkflowTimeoutDuration } from "cloudflare:workers";
12
+ import { D1Client } from "@effect/sql-d1";
13
13
  import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
14
14
  import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
15
15
  import * as RpcServer from "effect/unstable/rpc/RpcServer";
@@ -17,7 +17,7 @@ import { SqliteClient } from "@effect/sql-sqlite-do";
17
17
  import { SqlClient } from "effect/unstable/sql";
18
18
  import * as Tracer$1 from "effect/Tracer";
19
19
  declare namespace WorkersAi_d_exports {
20
- export { LayerOptions$13 as LayerOptions, Tag$12 as Tag, TagClass$11 as TagClass, WorkersAiAsyncBatchResponse, WorkersAiBinding, WorkersAiClient, WorkersAiDefinition, WorkersAiEmbeddingInput, WorkersAiEmbeddingResponse, WorkersAiModelsSearchObject, WorkersAiModelsSearchParams, WorkersAiOperationError, WorkersAiOptions, WorkersAiService, embeddingResponse, isWorkersAiBinding, layer$13 as layer, make$12 as make, makeClient$10 as makeClient };
20
+ export { LayerOptions$13 as LayerOptions, Tag$12 as Tag, TagClass$11 as TagClass, WorkersAiAsyncBatchResponse, WorkersAiBinding, WorkersAiClient, WorkersAiDefinition, WorkersAiEmbeddingInput, WorkersAiEmbeddingResponse, WorkersAiModelsSearchObject, WorkersAiModelsSearchParams, WorkersAiOperationError, WorkersAiOptions, WorkersAiService, embeddingResponse, isWorkersAiBinding, layer$14 as layer, make$12 as make, makeClient$10 as makeClient };
21
21
  }
22
22
  declare const WorkersAiOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
23
23
  readonly _tag: "WorkersAiOperationError";
@@ -89,11 +89,11 @@ declare const embeddingResponse: (value: {
89
89
  readonly [embeddingDimensionsKey]?: ReadonlyArray<number>;
90
90
  }) => WorkersAiEmbeddingResponse;
91
91
  declare const makeClient$10: <ModelList extends AiModelListType = AiModels>(definition: WorkersAiDefinition) => (ai: WorkersAiBinding<ModelList>) => WorkersAiClient<ModelList>;
92
- declare const layer$13: <Self, ModelList extends AiModelListType = AiModels>(tag: Context.Service<Self, WorkersAiClient<ModelList>>, definition: WorkersAiDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
92
+ declare const layer$14: <Self, ModelList extends AiModelListType = AiModels>(tag: Context.Service<Self, WorkersAiClient<ModelList>>, definition: WorkersAiDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
93
93
  declare const make$12: <Id extends string>(id: Id) => TagClass$11<WorkersAiService<Id>, Id, AiModels>;
94
94
  declare const Tag$12: <Self>() => <Id extends string, ModelList extends AiModelListType = AiModels>(id: Id) => TagClass$11<Self, Id, ModelList>;
95
95
  declare namespace AiGateway_d_exports {
96
- export { AiGatewayBinding, AiGatewayClient, AiGatewayDefinition, AiGatewayFetchOptions, AiGatewayHeaders, AiGatewayLog, AiGatewayOperationError, AiGatewayPatchLog, AiGatewayProvider, AiGatewayRunOptions, AiGatewayService, AiGatewayUniversalRequest, LayerOptions$12 as LayerOptions, Tag$11 as Tag, TagClass$10 as TagClass, isAiGatewayBinding, layer$12 as layer, make$11 as make, makeClient$9 as makeClient, makeClientFromAi, makeHttpClient };
96
+ export { AiGatewayBinding, AiGatewayClient, AiGatewayDefinition, AiGatewayFetchOptions, AiGatewayHeaders, AiGatewayLog, AiGatewayOperationError, AiGatewayPatchLog, AiGatewayProvider, AiGatewayRunOptions, AiGatewayService, AiGatewayUniversalRequest, LayerOptions$12 as LayerOptions, Tag$11 as Tag, TagClass$10 as TagClass, isAiGatewayBinding, layer$13 as layer, make$11 as make, makeClient$9 as makeClient, makeClientFromAi, makeHttpClient };
97
97
  }
98
98
  declare const AiGatewayOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
99
99
  readonly _tag: "AiGatewayOperationError";
@@ -167,11 +167,11 @@ declare const makeHttpClient: (definition: Omit<AiGatewayDefinition, "binding">
167
167
  readonly accountId: string;
168
168
  readonly binding?: string;
169
169
  }, request?: typeof fetch) => AiGatewayClient;
170
- declare const layer$12: <Self>(tag: Context.Service<Self, AiGatewayClient>, definition: AiGatewayDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
170
+ declare const layer$13: <Self>(tag: Context.Service<Self, AiGatewayClient>, definition: AiGatewayDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
171
171
  declare const make$11: <Id extends string>(id: Id) => TagClass$10<AiGatewayService<Id>, Id>;
172
172
  declare const Tag$11: <Self>() => <Id extends string>(id: Id) => TagClass$10<Self, Id>;
173
173
  declare namespace AnalyticsEngine_d_exports {
174
- export { AnalyticsEngineBinding, AnalyticsEngineClient, AnalyticsEngineDataPoint, AnalyticsEngineDataPointSchema, AnalyticsEngineDefinition, AnalyticsEngineFieldValue, AnalyticsEngineFieldValueSchema, AnalyticsEngineInvalidWritePolicy, AnalyticsEngineOperationError, AnalyticsEngineQueryClient, AnalyticsEngineQueryColumn, AnalyticsEngineQueryDefinition, AnalyticsEngineQueryError, AnalyticsEngineQueryOptions, AnalyticsEngineQueryResult, AnalyticsEngineQueryRow, AnalyticsEngineQueryService, AnalyticsEngineService, AnalyticsEngineWriteError, AnalyticsEngineWriteOptions, AnalyticsEngineWritePolicy, AnalyticsEngineWriteValidationError, AnalyticsEngineWriteViolation, LayerOptions$11 as LayerOptions, QueryConfigOptions, QueryTag, QueryTagClass, Tag$10 as Tag, TagClass$9 as TagClass, isAnalyticsEngineDataset, layer$11 as layer, layerFetch, layerFetchConfig, make$10 as make, makeClient$8 as makeClient, makeQuery, makeQueryClient, queryConfig, queryLayer, queryLayerConfig, writeLimits };
174
+ export { AnalyticsEngineBinding, AnalyticsEngineClient, AnalyticsEngineDataPoint, AnalyticsEngineDataPointSchema, AnalyticsEngineDefinition, AnalyticsEngineFieldValue, AnalyticsEngineFieldValueSchema, AnalyticsEngineInvalidWritePolicy, AnalyticsEngineOperationError, AnalyticsEngineQueryClient, AnalyticsEngineQueryColumn, AnalyticsEngineQueryDefinition, AnalyticsEngineQueryError, AnalyticsEngineQueryOptions, AnalyticsEngineQueryResult, AnalyticsEngineQueryRow, AnalyticsEngineQueryService, AnalyticsEngineService, AnalyticsEngineWriteError, AnalyticsEngineWriteOptions, AnalyticsEngineWritePolicy, AnalyticsEngineWriteValidationError, AnalyticsEngineWriteViolation, LayerOptions$11 as LayerOptions, QueryConfigOptions, QueryTag, QueryTagClass, Tag$10 as Tag, TagClass$9 as TagClass, isAnalyticsEngineDataset, layer$12 as layer, layerFetch, layerFetchConfig, make$10 as make, makeClient$8 as makeClient, makeQuery, makeQueryClient, queryConfig, queryLayer, queryLayerConfig, writeLimits };
175
175
  }
176
176
  declare const writeLimits: {
177
177
  readonly maxBlobs: 20;
@@ -322,13 +322,13 @@ declare const queryLayer: <Self>(tag: Context.Service<Self, AnalyticsEngineQuery
322
322
  declare const layerFetch: <Self>(tag: Context.Service<Self, AnalyticsEngineQueryClient>, definition: AnalyticsEngineQueryDefinition) => Layer.Layer<Self, never, never>;
323
323
  declare const queryLayerConfig: <Self>(tag: Context.Service<Self, AnalyticsEngineQueryClient>, config?: Config.Config<AnalyticsEngineQueryDefinition>) => Layer.Layer<Self, Config.ConfigError, HttpClient.HttpClient>;
324
324
  declare const layerFetchConfig: <Self>(tag: Context.Service<Self, AnalyticsEngineQueryClient>, config?: Config.Config<AnalyticsEngineQueryDefinition>) => Layer.Layer<Self, Config.ConfigError, never>;
325
- declare const layer$11: <Self>(tag: Context.Service<Self, AnalyticsEngineClient>, definition: LayerOptions$11) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
325
+ declare const layer$12: <Self>(tag: Context.Service<Self, AnalyticsEngineClient>, definition: LayerOptions$11) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
326
326
  declare const make$10: <Id extends string>(id: Id) => TagClass$9<AnalyticsEngineService<Id>, Id>;
327
327
  declare const makeQuery: <Id extends string>(id: Id) => QueryTagClass<AnalyticsEngineQueryService<Id>, Id>;
328
328
  declare const Tag$10: <Self>() => <Id extends string>(id: Id) => TagClass$9<Self, Id>;
329
329
  declare const QueryTag: <Self>() => <Id extends string>(id: Id) => QueryTagClass<Self, Id>;
330
330
  declare namespace BrowserRendering_d_exports {
331
- export { BrowserRenderingBinding, BrowserRenderingBrowserClient, BrowserRenderingBrowserLike, BrowserRenderingClient, BrowserRenderingConnect, BrowserRenderingDefinition, BrowserRenderingExternalResult, BrowserRenderingLaunch, BrowserRenderingMethodOptions, BrowserRenderingOperationError, BrowserRenderingPageClient, BrowserRenderingPageFunction, BrowserRenderingPageLike, BrowserRenderingService, LayerOptions$10 as LayerOptions, Tag$9 as Tag, TagClass$8 as TagClass, isBrowserRenderingBinding, layer$10 as layer, make$9 as make, makeClient$7 as makeClient };
331
+ export { BrowserRenderingBinding, BrowserRenderingBrowserClient, BrowserRenderingBrowserLike, BrowserRenderingClient, BrowserRenderingConnect, BrowserRenderingDefinition, BrowserRenderingExternalResult, BrowserRenderingLaunch, BrowserRenderingMethodOptions, BrowserRenderingOperationError, BrowserRenderingPageClient, BrowserRenderingPageFunction, BrowserRenderingPageLike, BrowserRenderingService, LayerOptions$10 as LayerOptions, Tag$9 as Tag, TagClass$8 as TagClass, isBrowserRenderingBinding, layer$11 as layer, make$9 as make, makeClient$7 as makeClient };
332
332
  }
333
333
  declare const BrowserRenderingOperationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
334
334
  readonly _tag: "BrowserRenderingOperationError";
@@ -402,11 +402,11 @@ interface TagClass$8<Self, Id extends string> extends Context.ServiceClass<Self,
402
402
  }
403
403
  declare const isBrowserRenderingBinding: <Candidate>(value: Candidate) => value is Candidate & BrowserRenderingBinding;
404
404
  declare const makeClient$7: <RawBinding extends BrowserRenderingBinding = BrowserRenderingBinding>(definition: BrowserRenderingDefinition) => (binding: RawBinding) => BrowserRenderingClient<RawBinding>;
405
- declare const layer$10: <Self>(tag: Context.Service<Self, BrowserRenderingClient>, definition: BrowserRenderingDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
405
+ declare const layer$11: <Self>(tag: Context.Service<Self, BrowserRenderingClient>, definition: BrowserRenderingDefinition) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
406
406
  declare const make$9: <Id extends string>(id: Id) => TagClass$8<BrowserRenderingService<Id>, Id>;
407
407
  declare const Tag$9: <Self>() => <Id extends string>(id: Id) => TagClass$8<Self, Id>;
408
408
  declare namespace Cache_d_exports {
409
- export { CacheClient, CacheOperation, CacheOperationError, CacheQueryOptions, CacheRequest, CacheStorage, CacheStorageClient, layer$9 as layer, layerFrom, makeCacheClient, makeClient$6 as makeClient };
409
+ export { CacheClient, CacheOperation, CacheOperationError, CacheQueryOptions, CacheRequest, CacheStorage, CacheStorageClient, layer$10 as layer, layerFrom, makeCacheClient, makeClient$6 as makeClient };
410
410
  }
411
411
  type CacheRequest = CloudflareRequestInfo | URL;
412
412
  type CacheQueryOptions = CloudflareCacheQueryOptions;
@@ -438,9 +438,9 @@ declare class CacheStorage extends CacheStorage_base {}
438
438
  declare const makeCacheClient: (cache: CloudflareCache, name?: string) => CacheClient;
439
439
  declare const makeClient$6: (storage: CloudflareCacheStorage) => CacheStorageClient;
440
440
  declare const layerFrom: (storage: CloudflareCacheStorage) => Layer.Layer<CacheStorage>;
441
- declare const layer$9: Layer.Layer<CacheStorage>;
441
+ declare const layer$10: Layer.Layer<CacheStorage>;
442
442
  declare namespace CloudflareOtlp_d_exports {
443
- export { DurableObjectLayerOptions, LayerOptions$9 as LayerOptions, ResourceOptions, Serialization, Signal, WorkerLayerOptions, layer$8 as layer, layerDurableObject, layerJson, layerProtobuf, layerWorker };
443
+ export { DurableObjectLayerOptions, LayerOptions$9 as LayerOptions, ResourceOptions, Serialization, Signal, WorkerLayerOptions, layer$9 as layer, layerDurableObject, layerJson, layerProtobuf, layerWorker };
444
444
  }
445
445
  type ResourceAttributes = NonNullable<Parameters<typeof OtlpResource.make>[0]["attributes"]>;
446
446
  /** Telemetry signal groups supported by the Cloudflare OTLP layers. */
@@ -504,7 +504,7 @@ interface DurableObjectLayerOptions extends LayerOptions$9 {
504
504
  * The layer provides {@link OtlpExporter.Flusher} for draining buffered
505
505
  * telemetry before the isolate freezes.
506
506
  */
507
- declare const layer$8: (options?: LayerOptions$9) => Layer.Layer<OtlpExporter.Flusher>;
507
+ declare const layer$9: (options?: LayerOptions$9) => Layer.Layer<OtlpExporter.Flusher>;
508
508
  /** Base OTLP telemetry layer forced to JSON serialization. */
509
509
  declare const layerJson: (options?: Omit<LayerOptions$9, "serialization">) => Layer.Layer<OtlpExporter.Flusher>;
510
510
  /** Base OTLP telemetry layer forced to protobuf serialization. */
@@ -526,6 +526,23 @@ declare const layerWorker: (options?: WorkerLayerOptions) => Layer.Layer<OtlpExp
526
526
  * backend can tolerate high-cardinality resource attributes.
527
527
  */
528
528
  declare const layerDurableObject: (options?: DurableObjectLayerOptions) => Layer.Layer<OtlpExporter.Flusher, never, DurableObjectState>;
529
+ declare namespace CloudflareTracer_d_exports {
530
+ export { layer$8 as layer };
531
+ }
532
+ /**
533
+ * Sends Effect spans to Cloudflare Workers Observability using the runtime's
534
+ * custom span API. Build this layer inside each invocation, for example with
535
+ * `Worker.make(Services, { eventLayer: CloudflareTracer.layer, fetch })`.
536
+ * It captures the invocation's async context and must not be cached across requests.
537
+ *
538
+ * Enable `observability.traces.enabled` in Wrangler. Cloudflare owns sampling
539
+ * and export; no OTLP exporter or flush is required. Scalar attributes are
540
+ * forwarded; other attributes, events, and links remain Effect-local. Completion
541
+ * is recorded as `effect.exit`, since Cloudflare has no outcome-setting API.
542
+ * Effect trace/span IDs remain independent of Cloudflare's opaque IDs. External
543
+ * parents and `root: true` cannot override the invocation's Cloudflare trace.
544
+ */
545
+ declare const layer$8: Layer.Layer<never>;
529
546
  declare namespace D1_d_exports {
530
547
  export { D1Definition, D1Service, D1SqlLayerOptions, Service, TypeId$1 as TypeId, make$8 as make };
531
548
  }
@@ -1781,4 +1798,4 @@ declare const implement: typeof implement$1;
1781
1798
  type Handler<ROut, Self extends Definition$1.Any> = Handler$1<ROut, Self>;
1782
1799
  type Options<ROut, Self extends Definition$1.Any> = Options$1<ROut, Self>;
1783
1800
  //#endregion
1784
- export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTracing_d_exports as RpcTracing, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
1801
+ export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTracing_d_exports as RpcTracing, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
2
  import { a as WorkerEnvironment, i as WorkerConfig, n as violationsMessage, r as Environment_exports, t as causeMessage } from "./ErrorMessage-Byx2OgNd.mjs";
3
- import { n as Service$1, r as layer$17, t as Binding_exports } from "./Binding-C82jRhdr.mjs";
3
+ import { n as Service$1, r as layer$18, t as Binding_exports } from "./Binding-C82jRhdr.mjs";
4
4
  import { n as Artifacts_exports } from "./Artifacts-CBxWGeZB.mjs";
5
5
  import { t as fromExecutionContext } from "./WorkerContext-Yobnfw-K.mjs";
6
6
  import { a as DurableWebSocketAttachmentError, c as StorageOperationError, i as DurableObjectWebSocket_exports, n as DurableObjectState_exports, o as fromWebSocket, r as fromDurableObjectState, s as DurableObjectStorage_exports, t as DurableObjectState } from "./DurableObjectState-DLL0YgBd.mjs";
@@ -8,8 +8,9 @@ import { A as QueueDefinition_exports, C as decodeSuccess, D as method$1, E as m
8
8
  import { Cause, Clock, Config, ConfigProvider, Context, Data, DateTime, Duration, Effect, Exit, FiberSet, Function as Function$1, Layer, Option, Predicate, Queue, Result, Schema, Tracer } from "effect";
9
9
  import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http";
10
10
  import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
11
+ import { AsyncLocalStorage } from "node:async_hooks";
12
+ import { DurableObject, WorkflowEntrypoint, tracing } from "cloudflare:workers";
11
13
  import { D1Client } from "@effect/sql-d1";
12
- import { DurableObject, WorkflowEntrypoint } from "cloudflare:workers";
13
14
  import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
14
15
  import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
15
16
  import * as RpcServer from "effect/unstable/rpc/RpcServer";
@@ -21,7 +22,7 @@ var WorkersAi_exports = /* @__PURE__ */ __exportAll({
21
22
  WorkersAiOperationError: () => WorkersAiOperationError,
22
23
  embeddingResponse: () => embeddingResponse,
23
24
  isWorkersAiBinding: () => isWorkersAiBinding,
24
- layer: () => layer$16,
25
+ layer: () => layer$17,
25
26
  make: () => make$14,
26
27
  makeClient: () => makeClient$13
27
28
  });
@@ -78,11 +79,11 @@ const makeClient$13 = (definition) => (ai) => {
78
79
  rawUnsafe: Effect.succeed(ai)
79
80
  };
80
81
  };
81
- const layer$16 = (tag, definition) => layer$17(tag, definition.binding, (value) => isWorkersAiBinding(value), makeClient$13(definition), { expected: expectedWorkersAiBinding });
82
+ const layer$17 = (tag, definition) => layer$18(tag, definition.binding, (value) => isWorkersAiBinding(value), makeClient$13(definition), { expected: expectedWorkersAiBinding });
82
83
  const make$14 = (id) => Tag$13()(id);
83
84
  const Tag$13 = () => (id) => {
84
85
  const tag = Context.Service()(id);
85
- const makeLayer = (definition) => layer$16(tag, definition);
86
+ const makeLayer = (definition) => layer$17(tag, definition);
86
87
  return Object.assign(tag, {
87
88
  id,
88
89
  layer: makeLayer
@@ -94,7 +95,7 @@ var AiGateway_exports = /* @__PURE__ */ __exportAll({
94
95
  AiGatewayOperationError: () => AiGatewayOperationError,
95
96
  Tag: () => Tag$12,
96
97
  isAiGatewayBinding: () => isAiGatewayBinding,
97
- layer: () => layer$15,
98
+ layer: () => layer$16,
98
99
  make: () => make$13,
99
100
  makeClient: () => makeClient$12,
100
101
  makeClientFromAi: () => makeClientFromAi,
@@ -247,11 +248,11 @@ const makeHttpClient = (definition, request = fetch) => {
247
248
  rawUnsafe: Effect.fail(aiGatewayError(binding, "rawUnsafe", /* @__PURE__ */ new TypeError("HTTP AI Gateway clients have no raw binding")))
248
249
  };
249
250
  };
250
- const layer$15 = (tag, definition) => layer$17(tag, definition.binding, isAiGatewayBinding, (ai) => makeClientFromAi(definition, ai), { expected: expectedAiGatewayBinding });
251
+ const layer$16 = (tag, definition) => layer$18(tag, definition.binding, isAiGatewayBinding, (ai) => makeClientFromAi(definition, ai), { expected: expectedAiGatewayBinding });
251
252
  const make$13 = (id) => Tag$12()(id);
252
253
  const Tag$12 = () => (id) => {
253
254
  const tag = Context.Service()(id);
254
- const makeLayer = (definition) => layer$15(tag, definition);
255
+ const makeLayer = (definition) => layer$16(tag, definition);
255
256
  return Object.assign(tag, {
256
257
  id,
257
258
  layer: makeLayer
@@ -268,7 +269,7 @@ var AnalyticsEngine_exports = /* @__PURE__ */ __exportAll({
268
269
  QueryTag: () => QueryTag,
269
270
  Tag: () => Tag$11,
270
271
  isAnalyticsEngineDataset: () => isAnalyticsEngineDataset,
271
- layer: () => layer$14,
272
+ layer: () => layer$15,
272
273
  layerFetch: () => layerFetch,
273
274
  layerFetchConfig: () => layerFetchConfig,
274
275
  make: () => make$12,
@@ -491,12 +492,12 @@ const queryLayerConfig = (tag, config = queryConfig()) => Layer.effect(tag, Effe
491
492
  return yield* makeQueryClient(definition);
492
493
  }));
493
494
  const layerFetchConfig = (tag, config = queryConfig()) => queryLayerConfig(tag, config).pipe(Layer.provide(FetchHttpClient.layer));
494
- const layer$14 = (tag, definition) => layer$17(tag, definition.binding, isAnalyticsEngineDataset, makeClient$11(definition, definition.write), { expected: expectedAnalyticsEngineDataset });
495
+ const layer$15 = (tag, definition) => layer$18(tag, definition.binding, isAnalyticsEngineDataset, makeClient$11(definition, definition.write), { expected: expectedAnalyticsEngineDataset });
495
496
  const make$12 = (id) => Tag$11()(id);
496
497
  const makeQuery = (id) => QueryTag()(id);
497
498
  const Tag$11 = () => (id) => {
498
499
  const tag = Context.Service()(id);
499
- const makeLayer = (definition) => layer$14(tag, definition);
500
+ const makeLayer = (definition) => layer$15(tag, definition);
500
501
  return Object.assign(tag, {
501
502
  id,
502
503
  layer: makeLayer
@@ -522,7 +523,7 @@ var BrowserRendering_exports = /* @__PURE__ */ __exportAll({
522
523
  BrowserRenderingOperationError: () => BrowserRenderingOperationError,
523
524
  Tag: () => Tag$10,
524
525
  isBrowserRenderingBinding: () => isBrowserRenderingBinding,
525
- layer: () => layer$13,
526
+ layer: () => layer$14,
526
527
  make: () => make$11,
527
528
  makeClient: () => makeClient$10
528
529
  });
@@ -579,11 +580,11 @@ const makeClient$10 = (definition) => (binding) => ({
579
580
  connectWith: (connect, options) => tryBrowserRenderingPromise(definition.binding, "connect", () => connect(binding, options)).pipe(Effect.map((browser) => wrapBrowser(definition.binding, browser)), Effect.withSpan("BrowserRendering.connectWith")),
580
581
  rawUnsafe: Effect.succeed(binding)
581
582
  });
582
- const layer$13 = (tag, definition) => layer$17(tag, definition.binding, isBrowserRenderingBinding, makeClient$10(definition), { expected: expectedBrowserRenderingBinding });
583
+ const layer$14 = (tag, definition) => layer$18(tag, definition.binding, isBrowserRenderingBinding, makeClient$10(definition), { expected: expectedBrowserRenderingBinding });
583
584
  const make$11 = (id) => Tag$10()(id);
584
585
  const Tag$10 = () => (id) => {
585
586
  const tag = Context.Service()(id);
586
- const makeLayer = (definition) => layer$13(tag, definition);
587
+ const makeLayer = (definition) => layer$14(tag, definition);
587
588
  return Object.assign(tag, {
588
589
  id,
589
590
  layer: makeLayer
@@ -594,7 +595,7 @@ const Tag$10 = () => (id) => {
594
595
  var Cache_exports = /* @__PURE__ */ __exportAll({
595
596
  CacheOperationError: () => CacheOperationError,
596
597
  CacheStorage: () => CacheStorage,
597
- layer: () => layer$12,
598
+ layer: () => layer$13,
598
599
  layerFrom: () => layerFrom,
599
600
  makeCacheClient: () => makeCacheClient,
600
601
  makeClient: () => makeClient$9
@@ -631,7 +632,7 @@ const makeClient$9 = (storage) => ({
631
632
  rawUnsafe: Effect.succeed(storage)
632
633
  });
633
634
  const layerFrom = (storage) => Layer.succeed(CacheStorage, makeClient$9(storage));
634
- const layer$12 = Layer.sync(CacheStorage, () => makeClient$9(caches));
635
+ const layer$13 = Layer.sync(CacheStorage, () => makeClient$9(caches));
635
636
  //#endregion
636
637
  //#region src/CloudflareOtlp.ts
637
638
  /**
@@ -641,7 +642,7 @@ const layer$12 = Layer.sync(CacheStorage, () => makeClient$9(caches));
641
642
  * layer finalizers run separately and follow their configured shutdown timeout.
642
643
  */
643
644
  var CloudflareOtlp_exports = /* @__PURE__ */ __exportAll({
644
- layer: () => layer$11,
645
+ layer: () => layer$12,
645
646
  layerDurableObject: () => layerDurableObject,
646
647
  layerJson: () => layerJson,
647
648
  layerProtobuf: () => layerProtobuf,
@@ -707,14 +708,14 @@ const makeLayer = (options = {}, runtimeAttributes = {}) => {
707
708
  * The layer provides {@link OtlpExporter.Flusher} for draining buffered
708
709
  * telemetry before the isolate freezes.
709
710
  */
710
- const layer$11 = (options = {}) => makeLayer(options);
711
+ const layer$12 = (options = {}) => makeLayer(options);
711
712
  /** Base OTLP telemetry layer forced to JSON serialization. */
712
- const layerJson = (options = {}) => layer$11({
713
+ const layerJson = (options = {}) => layer$12({
713
714
  ...options,
714
715
  serialization: "json"
715
716
  });
716
717
  /** Base OTLP telemetry layer forced to protobuf serialization. */
717
- const layerProtobuf = (options = {}) => layer$11({
718
+ const layerProtobuf = (options = {}) => layer$12({
718
719
  ...options,
719
720
  serialization: "protobuf"
720
721
  });
@@ -743,6 +744,64 @@ const layerDurableObject = (options = {}) => Layer.unwrap(Effect.map(DurableObje
743
744
  "cloudflare.durable_object.id": options.includeObjectId ? state.id.toString() : void 0
744
745
  })));
745
746
  //#endregion
747
+ //#region src/CloudflareTracer.ts
748
+ var CloudflareTracer_exports = /* @__PURE__ */ __exportAll({ layer: () => layer$11 });
749
+ var Span = class extends Tracer.NativeSpan {
750
+ runInContext;
751
+ cloudflareSpan;
752
+ constructor(options, runInContext, cloudflareSpan) {
753
+ super({
754
+ ...options,
755
+ sampled: options.sampled && (cloudflareSpan?.isTraced ?? false)
756
+ });
757
+ this.runInContext = runInContext;
758
+ this.cloudflareSpan = cloudflareSpan;
759
+ }
760
+ attribute(key, value) {
761
+ super.attribute(key, value);
762
+ if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) this.cloudflareSpan?.setAttribute(key, value);
763
+ }
764
+ end(endTime, exit) {
765
+ if (this.status._tag === "Ended") return;
766
+ super.end(endTime, exit);
767
+ this.cloudflareSpan?.setAttribute("effect.exit", Exit.isSuccess(exit) ? "success" : Cause.hasInterruptsOnly(exit.cause) ? "interrupted" : "failure");
768
+ this.cloudflareSpan?.end();
769
+ }
770
+ };
771
+ /**
772
+ * Sends Effect spans to Cloudflare Workers Observability using the runtime's
773
+ * custom span API. Build this layer inside each invocation, for example with
774
+ * `Worker.make(Services, { eventLayer: CloudflareTracer.layer, fetch })`.
775
+ * It captures the invocation's async context and must not be cached across requests.
776
+ *
777
+ * Enable `observability.traces.enabled` in Wrangler. Cloudflare owns sampling
778
+ * and export; no OTLP exporter or flush is required. Scalar attributes are
779
+ * forwarded; other attributes, events, and links remain Effect-local. Completion
780
+ * is recorded as `effect.exit`, since Cloudflare has no outcome-setting API.
781
+ * Effect trace/span IDs remain independent of Cloudflare's opaque IDs. External
782
+ * parents and `root: true` cannot override the invocation's Cloudflare trace.
783
+ */
784
+ const layer$11 = Layer.effect(Tracer.Tracer, Effect.sync(() => {
785
+ const invocationContext = AsyncLocalStorage.snapshot();
786
+ const contextFor = (span) => {
787
+ while (span?._tag === "Span") {
788
+ if (span instanceof Span) return span.runInContext;
789
+ span = Option.getOrUndefined(span.parent);
790
+ }
791
+ return invocationContext;
792
+ };
793
+ return Tracer.make({
794
+ span(options) {
795
+ const parentContext = options.root ? invocationContext : contextFor(Option.getOrUndefined(options.parent));
796
+ if (!options.sampled) return new Span(options, parentContext);
797
+ return parentContext(() => tracing.startActiveSpan(options.name, (span) => new Span(options, AsyncLocalStorage.snapshot(), span)));
798
+ },
799
+ context(primitive, fiber) {
800
+ return contextFor(fiber.currentSpan)(() => primitive["~effect/Effect/evaluate"](fiber));
801
+ }
802
+ });
803
+ }));
804
+ //#endregion
746
805
  //#region src/ContainerNamespace.ts
747
806
  var ContainerNamespace_exports = /* @__PURE__ */ __exportAll({
748
807
  ContainerNamespace: () => ContainerNamespace,
@@ -864,7 +923,7 @@ const makeClient$8 = (definition) => (namespace) => {
864
923
  rawUnsafe: Effect.succeed(namespace)
865
924
  };
866
925
  };
867
- const layer$10 = (tag, definition) => layer$17(tag, definition.binding, (value) => isContainerNamespaceResource(value), makeClient$8(definition), { expected: expectedContainerNamespace });
926
+ const layer$10 = (tag, definition) => layer$18(tag, definition.binding, (value) => isContainerNamespaceResource(value), makeClient$8(definition), { expected: expectedContainerNamespace });
868
927
  const make$10 = (id) => Tag$9()(id);
869
928
  const Tag$9 = () => (id) => {
870
929
  const tag = Context.Service()(`effect-cf/Container/${id}`);
@@ -1033,7 +1092,7 @@ const makeClient$7 = (definition) => {
1033
1092
  });
1034
1093
  };
1035
1094
  };
1036
- const layer$9 = (tag, definition) => layer$17(tag, definition.binding, (value) => isDurableObjectNamespaceClient(value), makeClient$7(definition), { expected: expectedDurableObjectNamespace });
1095
+ const layer$9 = (tag, definition) => layer$18(tag, definition.binding, (value) => isDurableObjectNamespaceClient(value), makeClient$7(definition), { expected: expectedDurableObjectNamespace });
1037
1096
  const makeDirectMethods = (rpcDefinition, helpers) => {
1038
1097
  const methods = {};
1039
1098
  if (rpcDefinition !== void 0) {
@@ -1574,6 +1633,7 @@ const layer$8 = (options = {}) => Layer.effectContext(Effect.gen(function* () {
1574
1633
  const declarations = options.resumableStreams ?? [];
1575
1634
  const durableObjectState = yield* DurableObjectState;
1576
1635
  const serialization = yield* RpcSerialization.RpcSerialization;
1636
+ const encodeDefect = Schema.encodeSync(serialization.codecFor(Schema.Defect()));
1577
1637
  const disconnects = yield* Queue.make();
1578
1638
  const connectionsBySocket = /* @__PURE__ */ new Map();
1579
1639
  const connectionsById = /* @__PURE__ */ new Map();
@@ -1872,7 +1932,7 @@ const layer$8 = (options = {}) => Layer.effectContext(Effect.gen(function* () {
1872
1932
  connection.socket.raw.send(encoded);
1873
1933
  return true;
1874
1934
  } catch (cause) {
1875
- delivered = RpcMessage.ResponseDefectEncoded(cause);
1935
+ delivered = RpcMessage.ResponseDefectEncoded(encodeDefect(cause));
1876
1936
  const encoded = connection.parser.encode(delivered);
1877
1937
  if (encoded === void 0) return false;
1878
1938
  connection.socket.raw.send(encoded);
@@ -1902,7 +1962,9 @@ const layer$8 = (options = {}) => Layer.effectContext(Effect.gen(function* () {
1902
1962
  initialMessage: Effect.succeedNone,
1903
1963
  supportsAck: true,
1904
1964
  supportsTransferables: false,
1905
- supportsSpanPropagation: true
1965
+ supportsSpanPropagation: true,
1966
+ supportsNotifications: true,
1967
+ codecFor: serialization.codecFor
1906
1968
  });
1907
1969
  });
1908
1970
  const replay = writeRequest;
@@ -1970,7 +2032,7 @@ const layer$8 = (options = {}) => Layer.effectContext(Effect.gen(function* () {
1970
2032
  });
1971
2033
  const run = writeRequest;
1972
2034
  if (run === void 0) {
1973
- yield* send(connection, RpcMessage.ResponseDefectEncoded("RPC server is not ready"));
2035
+ yield* send(connection, RpcMessage.ResponseDefectEncoded(encodeDefect("RPC server is not ready")));
1974
2036
  return;
1975
2037
  }
1976
2038
  for (const current of decoded) {
@@ -1985,7 +2047,7 @@ const layer$8 = (options = {}) => Layer.effectContext(Effect.gen(function* () {
1985
2047
  }
1986
2048
  }).pipe(Effect.catch((cause) => {
1987
2049
  const connection = register(socket);
1988
- return Predicate.isTagged(cause, "MaxBufferSizeExceeded") ? Effect.ignore(connection.socket.close(1009, String(cause))) : send(connection, RpcMessage.ResponseDefectEncoded(cause));
2050
+ return Predicate.isTagged(cause, "MaxBufferSizeExceeded") ? Effect.ignore(connection.socket.close(1009, String(cause))) : send(connection, RpcMessage.ResponseDefectEncoded(encodeDefect(cause)));
1989
2051
  })),
1990
2052
  close: unregister,
1991
2053
  error: resetSocket,
@@ -2293,7 +2355,7 @@ const makeClient$6 = (definition, options) => (email) => {
2293
2355
  rawUnsafe: Effect.succeed(email)
2294
2356
  };
2295
2357
  };
2296
- const layer$6 = (tag, definition) => layer$17(tag, definition.binding, isEmailBinding, makeClient$6(definition, definition.send), { expected: expectedSendEmailBinding });
2358
+ const layer$6 = (tag, definition) => layer$18(tag, definition.binding, isEmailBinding, makeClient$6(definition, definition.send), { expected: expectedSendEmailBinding });
2297
2359
  const make$6 = (id) => Tag$7()(id);
2298
2360
  const Tag$7 = () => (id) => {
2299
2361
  const tag = Context.Service()(id);
@@ -2319,7 +2381,7 @@ const makeClient$5 = (definition) => (hyperdrive) => ({
2319
2381
  connectionString: hyperdrive.connectionString,
2320
2382
  rawUnsafe: Effect.succeed(hyperdrive)
2321
2383
  });
2322
- const layer$5 = (tag, definition) => layer$17(tag, definition.binding, isHyperdrive, makeClient$5(definition), { expected: expectedHyperdrive });
2384
+ const layer$5 = (tag, definition) => layer$18(tag, definition.binding, isHyperdrive, makeClient$5(definition), { expected: expectedHyperdrive });
2323
2385
  const make$5 = (id) => Tag$6()(id);
2324
2386
  const Tag$6 = () => (id) => {
2325
2387
  const tag = Context.Service()(id);
@@ -2421,7 +2483,7 @@ const makeClient$4 = (definition) => (images) => {
2421
2483
  rawUnsafe: Effect.succeed(images)
2422
2484
  };
2423
2485
  };
2424
- const layer$4 = (tag, definition) => layer$17(tag, definition.binding, isImagesBinding, makeClient$4(definition), { expected: expectedImagesBinding });
2486
+ const layer$4 = (tag, definition) => layer$18(tag, definition.binding, isImagesBinding, makeClient$4(definition), { expected: expectedImagesBinding });
2425
2487
  const make$4 = (id) => Tag$5()(id);
2426
2488
  const Tag$5 = () => (id) => {
2427
2489
  const tag = Context.Service()(id);
@@ -2529,7 +2591,7 @@ const makeClient$3 = (definition) => {
2529
2591
  rawUnsafe: Effect.succeed(kv)
2530
2592
  });
2531
2593
  };
2532
- const layer$3 = (tag, definition) => layer$17(tag, definition.binding, isKvNamespace, makeClient$3(definition), { expected: expectedKvNamespace });
2594
+ const layer$3 = (tag, definition) => layer$18(tag, definition.binding, isKvNamespace, makeClient$3(definition), { expected: expectedKvNamespace });
2533
2595
  const makeDefinition$1 = (id, definition) => {
2534
2596
  const kvDefinition = {
2535
2597
  id,
@@ -2650,7 +2712,7 @@ const makeClient$2 = (definition) => {
2650
2712
  };
2651
2713
  };
2652
2714
  };
2653
- const layer$2 = (tag, definition) => layer$17(tag, definition.binding, isR2Bucket, makeClient$2(definition), { expected: expectedR2Bucket });
2715
+ const layer$2 = (tag, definition) => layer$18(tag, definition.binding, isR2Bucket, makeClient$2(definition), { expected: expectedR2Bucket });
2654
2716
  const make$3 = (id) => Tag$3()(id);
2655
2717
  const Tag$3 = () => (id) => {
2656
2718
  const tag = Context.Service()(id);
@@ -2716,7 +2778,7 @@ const makeClient$1 = (definition) => (index) => {
2716
2778
  rawUnsafe: Effect.succeed(index)
2717
2779
  };
2718
2780
  };
2719
- const layer$1 = (tag, definition) => layer$17(tag, definition.binding, isVectorizeBinding, makeClient$1(definition), { expected: expectedVectorizeBinding });
2781
+ const layer$1 = (tag, definition) => layer$18(tag, definition.binding, isVectorizeBinding, makeClient$1(definition), { expected: expectedVectorizeBinding });
2720
2782
  const make$2 = (id) => Tag$2()(id);
2721
2783
  const Tag$2 = () => (id) => {
2722
2784
  const tag = Context.Service()(id);
@@ -2871,7 +2933,7 @@ const makeClient = (definition) => {
2871
2933
  rawUnsafe: Effect.succeed(workflow)
2872
2934
  });
2873
2935
  };
2874
- const layer = (tag, definition) => layer$17(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition), { expected: expectedWorkflow });
2936
+ const layer = (tag, definition) => layer$18(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition), { expected: expectedWorkflow });
2875
2937
  //#endregion
2876
2938
  //#region src/WorkflowDefinition.ts
2877
2939
  var WorkflowDefinition_exports = /* @__PURE__ */ __exportAll({
@@ -2892,7 +2954,7 @@ const make$1 = (id, definition) => Tag$1()(id, definition);
2892
2954
  const Tag$1 = () => (id, definition) => {
2893
2955
  const workflowDefinition = makeDefinition(id, definition);
2894
2956
  const tag = Context.Service()(`effect-cf/Workflow/${id}`);
2895
- const layer$18 = (binding) => layer(tag, {
2957
+ const layer$19 = (binding) => layer(tag, {
2896
2958
  ...binding,
2897
2959
  payload: definition.payload,
2898
2960
  result: definition.result
@@ -2912,7 +2974,7 @@ const Tag$1 = () => (id, definition) => {
2912
2974
  payload: workflowDefinition.payload,
2913
2975
  result: workflowDefinition.result,
2914
2976
  make: workflowDefinition.make,
2915
- layer: layer$18,
2977
+ layer: layer$19,
2916
2978
  create,
2917
2979
  createBatch,
2918
2980
  get,
@@ -3057,4 +3119,4 @@ const waitForEvent = (name, options) => Effect.flatMap(WorkflowStep$1, (workflow
3057
3119
  const Tag = Tag$1;
3058
3120
  const implement = implement$1;
3059
3121
  //#endregion
3060
- export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTracing_exports as RpcTracing, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
3122
+ export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTracing_exports as RpcTracing, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.37.0",
3
+ "version": "0.39.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -75,7 +75,7 @@
75
75
  "@cloudflare/sandbox": "0.13.0-next.738.2",
76
76
  "@cloudflare/vitest-plugin": "catalog:",
77
77
  "@cloudflare/workers-types": "catalog:",
78
- "@effect/platform-node": "^4.0.0-rc.110",
78
+ "@effect/platform-node": "catalog:",
79
79
  "@effect/sql-d1": "catalog:",
80
80
  "@effect/sql-pg": "catalog:",
81
81
  "@effect/sql-sqlite-do": "catalog:",
@@ -85,6 +85,7 @@
85
85
  "@platformatic/vfs": "^0.4.0",
86
86
  "effect": "catalog:",
87
87
  "esbuild": "^0.28.1",
88
+ "miniflare": "catalog:",
88
89
  "vite-plus": "catalog:",
89
90
  "vitest": "catalog:"
90
91
  },
@@ -93,11 +94,11 @@
93
94
  "@cloudflare/sandbox": "^0.13.0-next.738.2",
94
95
  "@cloudflare/vitest-plugin": "^1.0.0",
95
96
  "@cloudflare/workers-types": "^5.20260825.1",
96
- "@effect/sql-d1": "^4.0.0-rc.110",
97
- "@effect/sql-pg": "^4.0.0-rc.110",
98
- "@effect/sql-sqlite-do": "^4.0.0-rc.110",
97
+ "@effect/sql-d1": "^4.0.0-rc.112",
98
+ "@effect/sql-pg": "^4.0.0-rc.112",
99
+ "@effect/sql-sqlite-do": "^4.0.0-rc.112",
99
100
  "@modelcontextprotocol/server": "^2.0.0",
100
- "effect": "^4.0.0-rc.110"
101
+ "effect": "^4.0.0-rc.112"
101
102
  },
102
103
  "peerDependenciesMeta": {
103
104
  "@cloudflare/computer": {
@@ -0,0 +1,92 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { tracing } from "cloudflare:workers";
3
+ import { Cause, Effect, Exit, Layer, Option, Predicate, Tracer } from "effect";
4
+
5
+ type SpanOptions = Parameters<Tracer.Tracer["span"]>[0];
6
+ type RunInContext = ReturnType<typeof AsyncLocalStorage.snapshot>;
7
+ type CloudflareSpan = Parameters<Parameters<typeof tracing.startActiveSpan>[1]>[0];
8
+
9
+ class Span extends Tracer.NativeSpan {
10
+ constructor(
11
+ options: SpanOptions,
12
+ readonly runInContext: RunInContext,
13
+ readonly cloudflareSpan?: CloudflareSpan,
14
+ ) {
15
+ super({ ...options, sampled: options.sampled && (cloudflareSpan?.isTraced ?? false) });
16
+ }
17
+
18
+ // Effect's tracer contract accepts arbitrary attributes; only CF-supported scalars cross the boundary.
19
+ // oxlint-disable-next-line anti-slop/no-unknown-parameters
20
+ override attribute(key: string, value: unknown): void {
21
+ super.attribute(key, value);
22
+
23
+ if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) {
24
+ this.cloudflareSpan?.setAttribute(key, value);
25
+ }
26
+ }
27
+
28
+ override end(endTime: bigint, exit: Exit.Exit<unknown, unknown>): void {
29
+ if (this.status._tag === "Ended") return;
30
+
31
+ super.end(endTime, exit);
32
+ this.cloudflareSpan?.setAttribute(
33
+ "effect.exit",
34
+ Exit.isSuccess(exit)
35
+ ? "success"
36
+ : Cause.hasInterruptsOnly(exit.cause)
37
+ ? "interrupted"
38
+ : "failure",
39
+ );
40
+ this.cloudflareSpan?.end();
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Sends Effect spans to Cloudflare Workers Observability using the runtime's
46
+ * custom span API. Build this layer inside each invocation, for example with
47
+ * `Worker.make(Services, { eventLayer: CloudflareTracer.layer, fetch })`.
48
+ * It captures the invocation's async context and must not be cached across requests.
49
+ *
50
+ * Enable `observability.traces.enabled` in Wrangler. Cloudflare owns sampling
51
+ * and export; no OTLP exporter or flush is required. Scalar attributes are
52
+ * forwarded; other attributes, events, and links remain Effect-local. Completion
53
+ * is recorded as `effect.exit`, since Cloudflare has no outcome-setting API.
54
+ * Effect trace/span IDs remain independent of Cloudflare's opaque IDs. External
55
+ * parents and `root: true` cannot override the invocation's Cloudflare trace.
56
+ */
57
+ export const layer: Layer.Layer<never> = Layer.effect(
58
+ Tracer.Tracer,
59
+ Effect.sync(() => {
60
+ const invocationContext = AsyncLocalStorage.snapshot();
61
+ const contextFor = (span: Tracer.AnySpan | undefined): RunInContext => {
62
+ while (span?._tag === "Span") {
63
+ if (span instanceof Span) return span.runInContext;
64
+
65
+ // Effect can install a no-op span while tracing is locally disabled.
66
+ span = Option.getOrUndefined(span.parent);
67
+ }
68
+
69
+ return invocationContext;
70
+ };
71
+
72
+ return Tracer.make({
73
+ span(options) {
74
+ const parentContext = options.root
75
+ ? invocationContext
76
+ : contextFor(Option.getOrUndefined(options.parent));
77
+
78
+ if (!options.sampled) return new Span(options, parentContext);
79
+
80
+ return parentContext(() =>
81
+ tracing.startActiveSpan(
82
+ options.name,
83
+ (span) => new Span(options, AsyncLocalStorage.snapshot(), span),
84
+ ),
85
+ );
86
+ },
87
+ context(primitive, fiber) {
88
+ return contextFor(fiber.currentSpan)(() => primitive["~effect/Effect/evaluate"](fiber));
89
+ },
90
+ });
91
+ }),
92
+ );
@@ -227,6 +227,7 @@ export const layer = (
227
227
  const declarations = options.resumableStreams ?? [];
228
228
  const durableObjectState = yield* DurableObjectState;
229
229
  const serialization = yield* RpcSerialization.RpcSerialization;
230
+ const encodeDefect = Schema.encodeSync(serialization.codecFor(Schema.Defect()));
230
231
  const disconnects = yield* Queue.make<number>();
231
232
  const connectionsBySocket = new Map<WebSocket, RpcConnection>();
232
233
  const connectionsById = new Map<number, RpcConnection>();
@@ -777,7 +778,7 @@ export const layer = (
777
778
 
778
779
  return true;
779
780
  } catch (cause) {
780
- delivered = RpcMessage.ResponseDefectEncoded(cause);
781
+ delivered = RpcMessage.ResponseDefectEncoded(encodeDefect(cause));
781
782
  const encoded = connection.parser.encode(delivered);
782
783
 
783
784
  if (encoded === undefined) {
@@ -834,6 +835,8 @@ export const layer = (
834
835
  supportsAck: true,
835
836
  supportsTransferables: false,
836
837
  supportsSpanPropagation: true,
838
+ supportsNotifications: true,
839
+ codecFor: serialization.codecFor,
837
840
  });
838
841
  });
839
842
 
@@ -976,7 +979,10 @@ export const layer = (
976
979
  const run = writeRequest;
977
980
 
978
981
  if (run === undefined) {
979
- yield* send(connection, RpcMessage.ResponseDefectEncoded("RPC server is not ready"));
982
+ yield* send(
983
+ connection,
984
+ RpcMessage.ResponseDefectEncoded(encodeDefect("RPC server is not ready")),
985
+ );
980
986
 
981
987
  return;
982
988
  }
@@ -1021,7 +1027,7 @@ export const layer = (
1021
1027
 
1022
1028
  return Predicate.isTagged(cause, "MaxBufferSizeExceeded")
1023
1029
  ? Effect.ignore(connection.socket.close(1009, String(cause)))
1024
- : send(connection, RpcMessage.ResponseDefectEncoded(cause));
1030
+ : send(connection, RpcMessage.ResponseDefectEncoded(encodeDefect(cause)));
1025
1031
  }),
1026
1032
  ),
1027
1033
  close: unregister,
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * as Binding from "./Binding";
5
5
  export * as BrowserRendering from "./BrowserRendering";
6
6
  export * as Cache from "./Cache";
7
7
  export * as CloudflareOtlp from "./CloudflareOtlp";
8
+ export * as CloudflareTracer from "./CloudflareTracer";
8
9
  export * as ContainerNamespace from "./ContainerNamespace";
9
10
  export * as D1 from "./D1";
10
11
  export * as DurableObject from "./DurableObject";