effect-cf 0.37.0 → 0.38.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,11 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.38.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#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.
8
+
3
9
  ## 0.37.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -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) {
@@ -2293,7 +2352,7 @@ const makeClient$6 = (definition, options) => (email) => {
2293
2352
  rawUnsafe: Effect.succeed(email)
2294
2353
  };
2295
2354
  };
2296
- const layer$6 = (tag, definition) => layer$17(tag, definition.binding, isEmailBinding, makeClient$6(definition, definition.send), { expected: expectedSendEmailBinding });
2355
+ const layer$6 = (tag, definition) => layer$18(tag, definition.binding, isEmailBinding, makeClient$6(definition, definition.send), { expected: expectedSendEmailBinding });
2297
2356
  const make$6 = (id) => Tag$7()(id);
2298
2357
  const Tag$7 = () => (id) => {
2299
2358
  const tag = Context.Service()(id);
@@ -2319,7 +2378,7 @@ const makeClient$5 = (definition) => (hyperdrive) => ({
2319
2378
  connectionString: hyperdrive.connectionString,
2320
2379
  rawUnsafe: Effect.succeed(hyperdrive)
2321
2380
  });
2322
- const layer$5 = (tag, definition) => layer$17(tag, definition.binding, isHyperdrive, makeClient$5(definition), { expected: expectedHyperdrive });
2381
+ const layer$5 = (tag, definition) => layer$18(tag, definition.binding, isHyperdrive, makeClient$5(definition), { expected: expectedHyperdrive });
2323
2382
  const make$5 = (id) => Tag$6()(id);
2324
2383
  const Tag$6 = () => (id) => {
2325
2384
  const tag = Context.Service()(id);
@@ -2421,7 +2480,7 @@ const makeClient$4 = (definition) => (images) => {
2421
2480
  rawUnsafe: Effect.succeed(images)
2422
2481
  };
2423
2482
  };
2424
- const layer$4 = (tag, definition) => layer$17(tag, definition.binding, isImagesBinding, makeClient$4(definition), { expected: expectedImagesBinding });
2483
+ const layer$4 = (tag, definition) => layer$18(tag, definition.binding, isImagesBinding, makeClient$4(definition), { expected: expectedImagesBinding });
2425
2484
  const make$4 = (id) => Tag$5()(id);
2426
2485
  const Tag$5 = () => (id) => {
2427
2486
  const tag = Context.Service()(id);
@@ -2529,7 +2588,7 @@ const makeClient$3 = (definition) => {
2529
2588
  rawUnsafe: Effect.succeed(kv)
2530
2589
  });
2531
2590
  };
2532
- const layer$3 = (tag, definition) => layer$17(tag, definition.binding, isKvNamespace, makeClient$3(definition), { expected: expectedKvNamespace });
2591
+ const layer$3 = (tag, definition) => layer$18(tag, definition.binding, isKvNamespace, makeClient$3(definition), { expected: expectedKvNamespace });
2533
2592
  const makeDefinition$1 = (id, definition) => {
2534
2593
  const kvDefinition = {
2535
2594
  id,
@@ -2650,7 +2709,7 @@ const makeClient$2 = (definition) => {
2650
2709
  };
2651
2710
  };
2652
2711
  };
2653
- const layer$2 = (tag, definition) => layer$17(tag, definition.binding, isR2Bucket, makeClient$2(definition), { expected: expectedR2Bucket });
2712
+ const layer$2 = (tag, definition) => layer$18(tag, definition.binding, isR2Bucket, makeClient$2(definition), { expected: expectedR2Bucket });
2654
2713
  const make$3 = (id) => Tag$3()(id);
2655
2714
  const Tag$3 = () => (id) => {
2656
2715
  const tag = Context.Service()(id);
@@ -2716,7 +2775,7 @@ const makeClient$1 = (definition) => (index) => {
2716
2775
  rawUnsafe: Effect.succeed(index)
2717
2776
  };
2718
2777
  };
2719
- const layer$1 = (tag, definition) => layer$17(tag, definition.binding, isVectorizeBinding, makeClient$1(definition), { expected: expectedVectorizeBinding });
2778
+ const layer$1 = (tag, definition) => layer$18(tag, definition.binding, isVectorizeBinding, makeClient$1(definition), { expected: expectedVectorizeBinding });
2720
2779
  const make$2 = (id) => Tag$2()(id);
2721
2780
  const Tag$2 = () => (id) => {
2722
2781
  const tag = Context.Service()(id);
@@ -2871,7 +2930,7 @@ const makeClient = (definition) => {
2871
2930
  rawUnsafe: Effect.succeed(workflow)
2872
2931
  });
2873
2932
  };
2874
- const layer = (tag, definition) => layer$17(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition), { expected: expectedWorkflow });
2933
+ const layer = (tag, definition) => layer$18(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition), { expected: expectedWorkflow });
2875
2934
  //#endregion
2876
2935
  //#region src/WorkflowDefinition.ts
2877
2936
  var WorkflowDefinition_exports = /* @__PURE__ */ __exportAll({
@@ -2892,7 +2951,7 @@ const make$1 = (id, definition) => Tag$1()(id, definition);
2892
2951
  const Tag$1 = () => (id, definition) => {
2893
2952
  const workflowDefinition = makeDefinition(id, definition);
2894
2953
  const tag = Context.Service()(`effect-cf/Workflow/${id}`);
2895
- const layer$18 = (binding) => layer(tag, {
2954
+ const layer$19 = (binding) => layer(tag, {
2896
2955
  ...binding,
2897
2956
  payload: definition.payload,
2898
2957
  result: definition.result
@@ -2912,7 +2971,7 @@ const Tag$1 = () => (id, definition) => {
2912
2971
  payload: workflowDefinition.payload,
2913
2972
  result: workflowDefinition.result,
2914
2973
  make: workflowDefinition.make,
2915
- layer: layer$18,
2974
+ layer: layer$19,
2916
2975
  create,
2917
2976
  createBatch,
2918
2977
  get,
@@ -3057,4 +3116,4 @@ const waitForEvent = (name, options) => Effect.flatMap(WorkflowStep$1, (workflow
3057
3116
  const Tag = Tag$1;
3058
3117
  const implement = implement$1;
3059
3118
  //#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 };
3119
+ 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.38.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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
  },
@@ -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
+ );
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";