effect-cf 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +2 -2
  3. package/dist/{Hyperdrive-RTjgirMV.d.mts → Hyperdrive-COyg7R26.d.mts} +40 -13
  4. package/dist/HyperdrivePg.d.mts +13 -5
  5. package/dist/HyperdrivePg.mjs +12 -7
  6. package/dist/index.d.mts +708 -549
  7. package/dist/index.mjs +924 -587
  8. package/package.json +10 -5
  9. package/src/AiGateway.ts +82 -47
  10. package/src/AnalyticsEngine.ts +74 -58
  11. package/src/Binding.ts +66 -13
  12. package/src/BrowserRendering.ts +30 -7
  13. package/src/Cache.ts +32 -8
  14. package/src/CloudflareOtlp.ts +32 -9
  15. package/src/ContainerNamespace.ts +87 -55
  16. package/src/D1.ts +13 -3
  17. package/src/DurableObject.ts +27 -164
  18. package/src/DurableObjectAlarm.ts +30 -9
  19. package/src/DurableObjectDefinition.ts +58 -54
  20. package/src/DurableObjectNamespace.ts +99 -96
  21. package/src/DurableObjectRpcWebSocket.ts +14 -6
  22. package/src/DurableObjectSqlite.ts +6 -3
  23. package/src/DurableObjectState.ts +6 -9
  24. package/src/DurableObjectStorage.ts +7 -1
  25. package/src/DurableObjectWebSocket.ts +51 -41
  26. package/src/Email.ts +40 -13
  27. package/src/Environment.ts +26 -6
  28. package/src/Hyperdrive.ts +2 -2
  29. package/src/HyperdrivePg.ts +25 -13
  30. package/src/Images.ts +77 -82
  31. package/src/Kv.ts +37 -27
  32. package/src/Queue.ts +6 -1
  33. package/src/QueueBinding.ts +18 -21
  34. package/src/QueueDefinition.ts +15 -14
  35. package/src/R2.ts +107 -29
  36. package/src/Rpc.ts +3 -1
  37. package/src/RpcDefinition.ts +177 -133
  38. package/src/ServiceBinding.ts +112 -108
  39. package/src/Vectorize.ts +59 -14
  40. package/src/Worker.ts +172 -192
  41. package/src/WorkerDefinition.ts +5 -10
  42. package/src/WorkersAi.ts +27 -17
  43. package/src/Workflow.ts +54 -49
  44. package/src/WorkflowBinding.ts +19 -11
  45. package/src/WorkflowDefinition.ts +24 -27
  46. package/src/index.ts +4 -3
  47. package/src/internal/Entrypoint.ts +3 -1
  48. package/src/internal/ErrorMessage.ts +24 -0
  49. package/src/internal/RpcInvocation.ts +11 -8
  50. package/src/internal/Runtime.ts +52 -0
  51. package/src/internal/WorkerContext.ts +14 -2
  52. package/src/Entry.ts +0 -259
  53. package/src/internal/Clock.ts +0 -37
@@ -2,6 +2,7 @@ import { Context, Data, Effect, type Layer } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
4
  import type { WorkerEnvironment } from "./Environment";
5
+ import * as ErrorMessage from "./internal/ErrorMessage";
5
6
 
6
7
  const expectedBrowserRenderingBinding = "Browser Rendering binding resource";
7
8
 
@@ -12,7 +13,11 @@ export class BrowserRenderingOperationError extends Data.TaggedError(
12
13
  readonly binding: string;
13
14
  readonly operation: string;
14
15
  readonly cause: unknown;
15
- }> {}
16
+ }> {
17
+ override get message(): string {
18
+ return `Browser Rendering ${this.operation} failed for binding "${this.binding}": ${ErrorMessage.causeMessage(this.cause)}`;
19
+ }
20
+ }
16
21
 
17
22
  /** Typed Browser Rendering binding definition. */
18
23
  export interface BrowserRenderingDefinition {
@@ -106,7 +111,7 @@ export interface BrowserRenderingClient<
106
111
  connect: BrowserRenderingConnect<RawBinding, Browser, ConnectOptions>,
107
112
  options?: ConnectOptions,
108
113
  ) => Effect.Effect<BrowserRenderingBrowserClient<Browser, Page>, BrowserRenderingOperationError>;
109
- readonly unsafeRaw: Effect.Effect<RawBinding>;
114
+ readonly rawUnsafe: Effect.Effect<RawBinding>;
110
115
  readonly definition: BrowserRenderingDefinition;
111
116
  }
112
117
 
@@ -251,15 +256,33 @@ export const makeClient =
251
256
  ) =>
252
257
  (binding: RawBinding): BrowserRenderingClient<RawBinding> => ({
253
258
  definition,
254
- launchWith: (launch, options) =>
259
+ launchWith: <
260
+ Browser extends BrowserRenderingBrowserLike<Page>,
261
+ Page extends BrowserRenderingPageLike = BrowserRenderingPageLike,
262
+ LaunchOptions = unknown,
263
+ >(
264
+ launch: BrowserRenderingLaunch<RawBinding, Browser, LaunchOptions>,
265
+ options?: LaunchOptions,
266
+ ) =>
255
267
  tryBrowserRenderingPromise(definition.binding, "launch", () => launch(binding, options)).pipe(
256
- Effect.map((browser) => wrapBrowser(definition.binding, browser)),
268
+ Effect.map((browser) => wrapBrowser<Browser, Page>(definition.binding, browser)),
269
+ Effect.withSpan("BrowserRendering.launchWith"),
257
270
  ),
258
- connectWith: (connect, options) =>
271
+ connectWith: <
272
+ Browser extends BrowserRenderingBrowserLike<Page>,
273
+ Page extends BrowserRenderingPageLike = BrowserRenderingPageLike,
274
+ ConnectOptions = unknown,
275
+ >(
276
+ connect: BrowserRenderingConnect<RawBinding, Browser, ConnectOptions>,
277
+ options?: ConnectOptions,
278
+ ) =>
259
279
  tryBrowserRenderingPromise(definition.binding, "connect", () =>
260
280
  connect(binding, options),
261
- ).pipe(Effect.map((browser) => wrapBrowser(definition.binding, browser))),
262
- unsafeRaw: Effect.succeed(binding),
281
+ ).pipe(
282
+ Effect.map((browser) => wrapBrowser<Browser, Page>(definition.binding, browser)),
283
+ Effect.withSpan("BrowserRendering.connectWith"),
284
+ ),
285
+ rawUnsafe: Effect.succeed(binding),
263
286
  });
264
287
 
265
288
  export const layer = <Self>(
package/src/Cache.ts CHANGED
@@ -6,6 +6,8 @@ import type {
6
6
  } from "@cloudflare/workers-types";
7
7
  import { Context, Data, Effect, Layer, Option } from "effect";
8
8
 
9
+ import * as ErrorMessage from "./internal/ErrorMessage";
10
+
9
11
  /** Request key accepted by Cloudflare's Cache API. */
10
12
  export type CacheRequest = CloudflareRequestInfo | URL;
11
13
 
@@ -20,7 +22,11 @@ export class CacheOperationError extends Data.TaggedError("CacheOperationError")
20
22
  readonly cache: string;
21
23
  readonly operation: CacheOperation;
22
24
  readonly cause: unknown;
23
- }> {}
25
+ }> {
26
+ override get message(): string {
27
+ return `Cache ${this.operation} failed for cache "${this.cache}": ${ErrorMessage.causeMessage(this.cause)}`;
28
+ }
29
+ }
24
30
 
25
31
  /** Effect wrapper around one Cloudflare `Cache` instance. */
26
32
  export interface CacheClient {
@@ -42,7 +48,7 @@ export interface CacheClient {
42
48
  options?: CacheQueryOptions,
43
49
  ) => Effect.Effect<boolean, CacheOperationError>;
44
50
  /** Access to the native Cloudflare `Cache` instance. */
45
- readonly unsafeRaw: Effect.Effect<CloudflareCache>;
51
+ readonly rawUnsafe: Effect.Effect<CloudflareCache>;
46
52
  }
47
53
 
48
54
  /** Effect wrapper around Cloudflare's global `caches` object. */
@@ -52,7 +58,7 @@ export interface CacheStorageClient {
52
58
  /** Opens a named cache. */
53
59
  readonly open: (name: string) => Effect.Effect<CacheClient, CacheOperationError>;
54
60
  /** Access to the native Cloudflare `CacheStorage` instance. */
55
- readonly unsafeRaw: Effect.Effect<CloudflareCacheStorage>;
61
+ readonly rawUnsafe: Effect.Effect<CloudflareCacheStorage>;
56
62
  }
57
63
 
58
64
  /** Service for Cloudflare's global Cache API. */
@@ -73,17 +79,34 @@ const tryCachePromise = <A>(
73
79
  catch: (cause) => cacheError(cache, operation, cause),
74
80
  });
75
81
 
82
+ const spanOptions = (cache: string, operation: CacheOperation) => ({
83
+ attributes: { cache, operation },
84
+ });
85
+
76
86
  /** Wraps a native Cloudflare `Cache` instance. */
77
87
  export const makeCacheClient = (cache: CloudflareCache, name = "default"): CacheClient => ({
78
88
  name,
79
- match: (request, options) =>
89
+ match: Effect.fn(
90
+ "Cache.match",
91
+ spanOptions(name, "match"),
92
+ )((request: CacheRequest, options?: CacheQueryOptions) =>
80
93
  tryCachePromise(name, "match", () => cache.match(request, options)).pipe(
81
94
  Effect.map(Option.fromUndefinedOr),
82
95
  ),
83
- put: (request, response) => tryCachePromise(name, "put", () => cache.put(request, response)),
84
- delete: (request, options) =>
96
+ ),
97
+ put: Effect.fn(
98
+ "Cache.put",
99
+ spanOptions(name, "put"),
100
+ )((request: CacheRequest, response: Response) =>
101
+ tryCachePromise(name, "put", () => cache.put(request, response)),
102
+ ),
103
+ delete: Effect.fn(
104
+ "Cache.delete",
105
+ spanOptions(name, "delete"),
106
+ )((request: CacheRequest, options?: CacheQueryOptions) =>
85
107
  tryCachePromise(name, "delete", () => cache.delete(request, options)),
86
- unsafeRaw: Effect.succeed(cache),
108
+ ),
109
+ rawUnsafe: Effect.succeed(cache),
87
110
  });
88
111
 
89
112
  /** Wraps a native Cloudflare `CacheStorage` instance. */
@@ -92,8 +115,9 @@ export const makeClient = (storage: CloudflareCacheStorage): CacheStorageClient
92
115
  open: (name) =>
93
116
  tryCachePromise(name, "open", () => storage.open(name)).pipe(
94
117
  Effect.map((cache) => makeCacheClient(cache, name)),
118
+ Effect.withSpan("Cache.open", spanOptions(name, "open")),
95
119
  ),
96
- unsafeRaw: Effect.succeed(storage),
120
+ rawUnsafe: Effect.succeed(storage),
97
121
  });
98
122
 
99
123
  /** Provides Cache API services from an explicit native `CacheStorage` instance. */
@@ -1,7 +1,22 @@
1
+ /**
2
+ * OTLP telemetry layers for Cloudflare Workers and Durable Objects.
3
+ *
4
+ * Every layer exposes {@link OtlpExporter.Flusher}, which drains buffered
5
+ * telemetry on demand. Cloudflare isolates can freeze before the periodic
6
+ * export interval fires, so flush explicitly before returning a response, or
7
+ * hand the flush to `ctx.waitUntil`:
8
+ *
9
+ * ```ts
10
+ * const flusher = yield* OtlpExporter.Flusher;
11
+ *
12
+ * yield* flusher.flush;
13
+ * ```
14
+ */
1
15
  import { ConfigProvider, Effect, Layer, Option } from "effect";
2
16
  import type * as Tracer from "effect/Tracer";
3
17
  import { FetchHttpClient, type Headers } from "effect/unstable/http";
4
18
  import {
19
+ OtlpExporter,
5
20
  OtlpLogger,
6
21
  OtlpMetrics,
7
22
  OtlpSerialization,
@@ -138,7 +153,7 @@ const mergeSignalLayers = (layers: ReadonlyArray<SignalLayer>): SignalLayer => {
138
153
  const makeLayer = (
139
154
  options: LayerOptions = {},
140
155
  runtimeAttributes: Record<string, unknown> = {},
141
- ): Layer.Layer<never, never, never> => {
156
+ ): Layer.Layer<OtlpExporter.Flusher> => {
142
157
  const signals = selectedSignals(options.signals);
143
158
  const resource = makeResource(options, runtimeAttributes);
144
159
  const layers: Array<SignalLayer> = [];
@@ -174,7 +189,7 @@ const makeLayer = (
174
189
  }
175
190
 
176
191
  if (layers.length === 0) {
177
- return Layer.empty;
192
+ return OtlpExporter.layerFlusher;
178
193
  }
179
194
 
180
195
  return mergeSignalLayers(layers).pipe(
@@ -191,16 +206,22 @@ const makeLayer = (
191
206
  * layers. In Cloudflare Workers and Durable Objects, the current `env` object is
192
207
  * installed as the primary `ConfigProvider`; outside Cloudflare, the ambient
193
208
  * Effect `ConfigProvider` remains the fallback.
209
+ *
210
+ * The layer provides {@link OtlpExporter.Flusher} for draining buffered
211
+ * telemetry before the isolate freezes.
194
212
  */
195
- export const layer = (options: LayerOptions = {}) => makeLayer(options);
213
+ export const layer = (options: LayerOptions = {}): Layer.Layer<OtlpExporter.Flusher> =>
214
+ makeLayer(options);
196
215
 
197
216
  /** Base OTLP telemetry layer forced to JSON serialization. */
198
- export const layerJson = (options: Omit<LayerOptions, "serialization"> = {}) =>
199
- layer({ ...options, serialization: "json" });
217
+ export const layerJson = (
218
+ options: Omit<LayerOptions, "serialization"> = {},
219
+ ): Layer.Layer<OtlpExporter.Flusher> => layer({ ...options, serialization: "json" });
200
220
 
201
221
  /** Base OTLP telemetry layer forced to protobuf serialization. */
202
- export const layerProtobuf = (options: Omit<LayerOptions, "serialization"> = {}) =>
203
- layer({ ...options, serialization: "protobuf" });
222
+ export const layerProtobuf = (
223
+ options: Omit<LayerOptions, "serialization"> = {},
224
+ ): Layer.Layer<OtlpExporter.Flusher> => layer({ ...options, serialization: "protobuf" });
204
225
 
205
226
  /**
206
227
  * OTLP layer with Cloudflare Worker resource attributes.
@@ -208,7 +229,7 @@ export const layerProtobuf = (options: Omit<LayerOptions, "serialization"> = {})
208
229
  * Provide this layer at runtime scope for long-lived metrics aggregation, or at
209
230
  * handler scope when traces/logs should flush as the Cloudflare event finishes.
210
231
  */
211
- export const workerLayer = (options: WorkerLayerOptions = {}) =>
232
+ export const layerWorker = (options: WorkerLayerOptions = {}): Layer.Layer<OtlpExporter.Flusher> =>
212
233
  makeLayer(options, {
213
234
  "cloudflare.resource_type": "worker",
214
235
  "cloudflare.worker.name": options.workerName,
@@ -221,7 +242,9 @@ export const workerLayer = (options: WorkerLayerOptions = {}) =>
221
242
  * Durable Object id. Prefer leaving `includeObjectId` disabled unless the
222
243
  * backend can tolerate high-cardinality resource attributes.
223
244
  */
224
- export const durableObjectLayer = (options: DurableObjectLayerOptions = {}) =>
245
+ export const layerDurableObject = (
246
+ options: DurableObjectLayerOptions = {},
247
+ ): Layer.Layer<OtlpExporter.Flusher, never, DurableObjectState> =>
225
248
  Layer.unwrap(
226
249
  Effect.map(DurableObjectState, (state) =>
227
250
  makeLayer(options, {
@@ -1,7 +1,8 @@
1
- import { Context, Data, Effect, Schema, type Layer } from "effect";
1
+ import { Context, Data, Effect, Schema as S, type Layer } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
4
  import type { WorkerEnvironment } from "./Environment";
5
+ import * as ErrorMessage from "./internal/ErrorMessage";
5
6
 
6
7
  const expectedContainerNamespace = "Container namespace binding with getByName()";
7
8
 
@@ -38,15 +39,15 @@ export interface ContainerStartAndWaitForPortsOptions {
38
39
  }
39
40
 
40
41
  /** Serializable state reported by a Cloudflare Container instance. */
41
- export const ContainerState = Schema.Union([
42
- Schema.Struct({
43
- lastChange: Schema.Finite,
44
- status: Schema.Literals(["running", "stopping", "stopped", "healthy"]),
42
+ export const ContainerState = S.Union([
43
+ S.Struct({
44
+ lastChange: S.Finite,
45
+ status: S.Literals(["running", "stopping", "stopped", "healthy"]),
45
46
  }),
46
- Schema.Struct({
47
- lastChange: Schema.Finite,
48
- status: Schema.Literal("stopped_with_code"),
49
- exitCode: Schema.optionalKey(Schema.Int),
47
+ S.Struct({
48
+ lastChange: S.Finite,
49
+ status: S.Literal("stopped_with_code"),
50
+ exitCode: S.optionalKey(S.Int),
50
51
  }),
51
52
  ]);
52
53
  export type ContainerState = typeof ContainerState.Type;
@@ -86,11 +87,15 @@ export class ContainerOperationError extends Data.TaggedError("ContainerOperatio
86
87
  readonly instance: string;
87
88
  readonly operation: string;
88
89
  readonly cause: unknown;
89
- }> {}
90
+ }> {
91
+ override get message(): string {
92
+ return `Container ${this.operation} failed for binding "${this.binding}" instance "${this.instance}": ${ErrorMessage.causeMessage(this.cause)}`;
93
+ }
94
+ }
90
95
 
91
96
  /** Effect-wrapped client for one named Container instance. */
92
97
  export interface ContainerInstanceClient {
93
- readonly unsafeRaw: Effect.Effect<ContainerStub, ContainerOperationError>;
98
+ readonly rawUnsafe: Effect.Effect<ContainerStub, ContainerOperationError>;
94
99
  readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
95
100
  readonly fetch: (
96
101
  input: RequestInfo | URL,
@@ -118,7 +123,7 @@ export interface ContainerNamespaceClient {
118
123
  name: string,
119
124
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
120
125
  ) => ContainerInstanceClient;
121
- readonly unsafeRaw: Effect.Effect<ContainerNamespaceResource>;
126
+ readonly rawUnsafe: Effect.Effect<ContainerNamespaceResource>;
122
127
  }
123
128
 
124
129
  export type ContainerNamespaceStaticClient<R> = {
@@ -144,9 +149,9 @@ export type ContainerNamespaceStaticClient<R> = {
144
149
  ) => Effect.Effect<void, ContainerOperationError, R>;
145
150
  readonly stop: (signal?: ContainerSignal) => Effect.Effect<void, ContainerOperationError, R>;
146
151
  readonly destroy: Effect.Effect<void, ContainerOperationError, R>;
147
- readonly unsafeRaw: Effect.Effect<ContainerStub, ContainerOperationError, R>;
152
+ readonly rawUnsafe: Effect.Effect<ContainerStub, ContainerOperationError, R>;
148
153
  };
149
- readonly unsafeRaw: () => Effect.Effect<ContainerNamespaceResource, never, R>;
154
+ readonly rawUnsafe: Effect.Effect<ContainerNamespaceResource, never, R>;
150
155
  };
151
156
 
152
157
  export type LayerOptions = {
@@ -155,7 +160,7 @@ export type LayerOptions = {
155
160
 
156
161
  export interface TagClass<Self, Id extends string>
157
162
  extends
158
- Context.ServiceClass<Self, Id, ContainerNamespaceClient>,
163
+ Context.ServiceClass<Self, `effect-cf/Container/${Id}`, ContainerNamespaceClient>,
159
164
  ContainerNamespaceStaticClient<Self> {
160
165
  readonly id: Id;
161
166
  readonly layer: (
@@ -188,32 +193,62 @@ const makeInstanceClient = (
188
193
  definition: ContainerNamespaceDefinition,
189
194
  instance: string,
190
195
  stub: ContainerStub,
191
- ): ContainerInstanceClient => ({
192
- unsafeRaw: Effect.succeed(stub),
193
- state: tryContainerPromise(definition, instance, "state", () => stub.getState()).pipe(
194
- Effect.flatMap(Schema.decodeUnknownEffect(ContainerState)),
195
- Effect.mapError((cause) =>
196
- cause instanceof ContainerOperationError
197
- ? cause
198
- : new ContainerOperationError({
199
- binding: definition.binding,
200
- instance,
201
- operation: "state",
202
- cause,
203
- }),
196
+ ): ContainerInstanceClient => {
197
+ const spanOptions = (operation: string) => ({
198
+ attributes: { binding: definition.binding, instance, operation },
199
+ });
200
+
201
+ return {
202
+ rawUnsafe: Effect.succeed(stub),
203
+ state: tryContainerPromise(definition, instance, "state", () => stub.getState()).pipe(
204
+ Effect.flatMap(S.decodeUnknownEffect(ContainerState)),
205
+ Effect.mapError((cause) =>
206
+ cause instanceof ContainerOperationError
207
+ ? cause
208
+ : new ContainerOperationError({
209
+ binding: definition.binding,
210
+ instance,
211
+ operation: "state",
212
+ cause,
213
+ }),
214
+ ),
215
+ Effect.withSpan("ContainerNamespace.state", spanOptions("state")),
204
216
  ),
205
- ),
206
- fetch: (input, init) =>
207
- tryContainerPromise(definition, instance, "fetch", () => stub.fetch(input, init)),
208
- start: (options, waitOptions) =>
209
- tryContainerPromise(definition, instance, "start", () => stub.start(options, waitOptions)),
210
- startAndWaitForPorts: (options) =>
211
- tryContainerPromise(definition, instance, "startAndWaitForPorts", () =>
212
- stub.startAndWaitForPorts(options),
217
+ fetch: Effect.fn(
218
+ "ContainerNamespace.fetch",
219
+ spanOptions("fetch"),
220
+ )(function* (input: RequestInfo | URL, init?: RequestInit) {
221
+ return yield* tryContainerPromise(definition, instance, "fetch", () =>
222
+ stub.fetch(input, init),
223
+ );
224
+ }),
225
+ start: Effect.fn(
226
+ "ContainerNamespace.start",
227
+ spanOptions("start"),
228
+ )(function* (options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions) {
229
+ return yield* tryContainerPromise(definition, instance, "start", () =>
230
+ stub.start(options, waitOptions),
231
+ );
232
+ }),
233
+ startAndWaitForPorts: Effect.fn(
234
+ "ContainerNamespace.startAndWaitForPorts",
235
+ spanOptions("startAndWaitForPorts"),
236
+ )(function* (options?: ContainerStartAndWaitForPortsOptions) {
237
+ return yield* tryContainerPromise(definition, instance, "startAndWaitForPorts", () =>
238
+ stub.startAndWaitForPorts(options),
239
+ );
240
+ }),
241
+ stop: Effect.fn(
242
+ "ContainerNamespace.stop",
243
+ spanOptions("stop"),
244
+ )(function* (signal?: ContainerSignal) {
245
+ return yield* tryContainerPromise(definition, instance, "stop", () => stub.stop(signal));
246
+ }),
247
+ destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()).pipe(
248
+ Effect.withSpan("ContainerNamespace.destroy", spanOptions("destroy")),
213
249
  ),
214
- stop: (signal) => tryContainerPromise(definition, instance, "stop", () => stub.stop(signal)),
215
- destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()),
216
- });
250
+ };
251
+ };
217
252
 
218
253
  export const isContainerNamespaceResource = (value: unknown): value is ContainerNamespaceResource =>
219
254
  typeof value === "object" &&
@@ -245,7 +280,7 @@ export const makeClient =
245
280
  const instance = getByName(name, options);
246
281
 
247
282
  return {
248
- unsafeRaw: Effect.flatMap(instance, (client) => client.unsafeRaw),
283
+ rawUnsafe: Effect.flatMap(instance, (client) => client.rawUnsafe),
249
284
  state: Effect.flatMap(instance, (client) => client.state),
250
285
  fetch: (input, init) => Effect.flatMap(instance, (client) => client.fetch(input, init)),
251
286
  start: (startOptions, waitOptions) =>
@@ -261,7 +296,7 @@ export const makeClient =
261
296
  definition,
262
297
  getByName,
263
298
  byName,
264
- unsafeRaw: Effect.succeed(namespace),
299
+ rawUnsafe: Effect.succeed(namespace),
265
300
  };
266
301
  };
267
302
 
@@ -288,18 +323,19 @@ export interface ContainerNamespaceService<Id extends string> {
288
323
  export const Tag =
289
324
  <Self>() =>
290
325
  <Id extends string>(id: Id) => {
291
- const tag = Context.Service<Self, ContainerNamespaceClient>()(id);
326
+ const tag = Context.Service<Self, ContainerNamespaceClient>()(
327
+ `effect-cf/Container/${id}` as const,
328
+ );
292
329
  const makeLayer = (definition: LayerOptions) => layer(tag, definition);
293
330
 
294
- const getByName = (
331
+ const getByName = Effect.fnUntraced(function* (
295
332
  name: string,
296
333
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
297
- ) =>
298
- Effect.gen(function* () {
299
- const namespace = yield* tag;
334
+ ) {
335
+ const namespace = yield* tag;
300
336
 
301
- return yield* namespace.getByName(name, options);
302
- });
337
+ return yield* namespace.getByName(name, options);
338
+ });
303
339
 
304
340
  const byName = (
305
341
  name: string,
@@ -319,21 +355,17 @@ export const Tag =
319
355
  stop: (signal?: ContainerSignal) =>
320
356
  Effect.flatMap(getByName(name, options), (instance) => instance.stop(signal)),
321
357
  destroy: Effect.flatMap(getByName(name, options), (instance) => instance.destroy),
322
- unsafeRaw: Effect.flatMap(getByName(name, options), (instance) => instance.unsafeRaw),
358
+ rawUnsafe: Effect.flatMap(getByName(name, options), (instance) => instance.rawUnsafe),
323
359
  });
324
360
 
325
- const unsafeRaw = Effect.fn(function* () {
326
- const namespace = yield* tag;
327
-
328
- return yield* namespace.unsafeRaw;
329
- });
361
+ const rawUnsafe = Effect.flatMap(tag, (namespace) => namespace.rawUnsafe);
330
362
 
331
363
  return Object.assign(tag, {
332
364
  id,
333
365
  layer: makeLayer,
334
366
  getByName,
335
367
  byName,
336
- unsafeRaw,
368
+ rawUnsafe,
337
369
  }) as TagClass<Self, Id>;
338
370
  };
339
371
 
package/src/D1.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import { D1Client } from "@effect/sql-d1";
2
- import { Effect, Layer } from "effect";
2
+ import { Effect, Layer, type Config } from "effect";
3
+ import type { SqlClient } from "effect/unstable/sql";
3
4
 
4
5
  import * as Binding from "./Binding";
6
+ import type { WorkerEnvironment } from "./Environment";
5
7
 
6
- const TypeId = "effect-cf/D1" as const;
8
+ const TypeId = "~effect-cf/D1" as const;
9
+
10
+ export type TypeId = typeof TypeId;
7
11
  const expectedD1Database = "D1 database binding with prepare(), batch(), and exec()";
8
12
 
9
13
  /** Typed D1 binding definition. */
@@ -66,7 +70,13 @@ export const Service =
66
70
  expected: expectedD1Database,
67
71
  });
68
72
 
69
- const sqlLayer = (options?: D1SqlLayerOptions) =>
73
+ const sqlLayer = (
74
+ options?: D1SqlLayerOptions,
75
+ ): Layer.Layer<
76
+ D1Client.D1Client | SqlClient.SqlClient,
77
+ Config.ConfigError | Binding.BindingNotFoundError | Binding.BindingValidationError,
78
+ WorkerEnvironment
79
+ > =>
70
80
  Layer.unwrap(
71
81
  Effect.gen(function* () {
72
82
  const db = yield* tag;