effect-cf 0.20.1 → 0.23.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 +52 -0
  2. package/README.md +27 -3
  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 +1113 -922
  7. package/dist/index.mjs +980 -604
  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 +251 -80
  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 +63 -39
  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 +39 -17
  52. package/src/Entry.ts +0 -259
  53. package/src/internal/Clock.ts +0 -37
@@ -1,13 +1,17 @@
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
 
8
- /** Signal accepted by Cloudflare Container instances. */
9
+ /** Named signal accepted by Cloudflare Container instances. */
9
10
  export type ContainerSignal = "SIGKILL" | "SIGINT" | "SIGTERM";
10
11
 
12
+ /** Named or numeric signal accepted by Container `stop()`. */
13
+ export type ContainerStopSignal = ContainerSignal | number;
14
+
11
15
  /** Per-instance Container startup configuration. */
12
16
  export interface ContainerStartOptions {
13
17
  readonly envVars?: Record<string, string>;
@@ -38,15 +42,15 @@ export interface ContainerStartAndWaitForPortsOptions {
38
42
  }
39
43
 
40
44
  /** 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"]),
45
+ export const ContainerState = S.Union([
46
+ S.Struct({
47
+ lastChange: S.Finite,
48
+ status: S.Literals(["running", "stopping", "stopped", "healthy"]),
45
49
  }),
46
- Schema.Struct({
47
- lastChange: Schema.Finite,
48
- status: Schema.Literal("stopped_with_code"),
49
- exitCode: Schema.optionalKey(Schema.Int),
50
+ S.Struct({
51
+ lastChange: S.Finite,
52
+ status: S.Literal("stopped_with_code"),
53
+ exitCode: S.optionalKey(S.Int),
50
54
  }),
51
55
  ]);
52
56
  export type ContainerState = typeof ContainerState.Type;
@@ -62,8 +66,15 @@ export interface ContainerStub {
62
66
  getState(): Promise<ContainerState>;
63
67
  start(options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions): Promise<void>;
64
68
  startAndWaitForPorts(options?: ContainerStartAndWaitForPortsOptions): Promise<void>;
65
- stop(signal?: ContainerSignal): Promise<void>;
69
+ waitForPort(options: ContainerWaitOptions): Promise<number>;
70
+ stop(signal?: ContainerStopSignal): Promise<void>;
66
71
  destroy(): Promise<void>;
72
+ setAllowedHosts(hosts: Array<string>): Promise<void>;
73
+ setDeniedHosts(hosts: Array<string>): Promise<void>;
74
+ allowHost(hostname: string): Promise<void>;
75
+ denyHost(hostname: string): Promise<void>;
76
+ removeAllowedHost(hostname: string): Promise<void>;
77
+ removeDeniedHost(hostname: string): Promise<void>;
67
78
  }
68
79
 
69
80
  /** Native Container namespace binding shape. */
@@ -74,6 +85,11 @@ export interface ContainerNamespaceResource {
74
85
  ): ContainerStub;
75
86
  }
76
87
 
88
+ /** Exact native stub type produced by a Container namespace binding. */
89
+ export type ContainerStubOf<Namespace extends ContainerNamespaceResource> = ReturnType<
90
+ Namespace["getByName"]
91
+ >;
92
+
77
93
  /** Container namespace binding metadata. */
78
94
  export interface ContainerNamespaceDefinition {
79
95
  /** Binding name as configured in `wrangler.jsonc`. */
@@ -86,11 +102,15 @@ export class ContainerOperationError extends Data.TaggedError("ContainerOperatio
86
102
  readonly instance: string;
87
103
  readonly operation: string;
88
104
  readonly cause: unknown;
89
- }> {}
105
+ }> {
106
+ override get message(): string {
107
+ return `Container ${this.operation} failed for binding "${this.binding}" instance "${this.instance}": ${ErrorMessage.causeMessage(this.cause)}`;
108
+ }
109
+ }
90
110
 
91
111
  /** Effect-wrapped client for one named Container instance. */
92
- export interface ContainerInstanceClient {
93
- readonly unsafeRaw: Effect.Effect<ContainerStub, ContainerOperationError>;
112
+ export interface ContainerInstanceClient<Stub extends ContainerStub = ContainerStub> {
113
+ readonly rawUnsafe: Effect.Effect<Stub, ContainerOperationError>;
94
114
  readonly state: Effect.Effect<ContainerState, ContainerOperationError>;
95
115
  readonly fetch: (
96
116
  input: RequestInfo | URL,
@@ -103,29 +123,51 @@ export interface ContainerInstanceClient {
103
123
  readonly startAndWaitForPorts: (
104
124
  options?: ContainerStartAndWaitForPortsOptions,
105
125
  ) => Effect.Effect<void, ContainerOperationError>;
106
- readonly stop: (signal?: ContainerSignal) => Effect.Effect<void, ContainerOperationError>;
126
+ readonly waitForPort: (
127
+ options: ContainerWaitOptions,
128
+ ) => Effect.Effect<number, ContainerOperationError>;
129
+ readonly stop: (signal?: ContainerStopSignal) => Effect.Effect<void, ContainerOperationError>;
107
130
  readonly destroy: Effect.Effect<void, ContainerOperationError>;
131
+ readonly setAllowedHosts: (
132
+ hosts: ReadonlyArray<string>,
133
+ ) => Effect.Effect<void, ContainerOperationError>;
134
+ readonly setDeniedHosts: (
135
+ hosts: ReadonlyArray<string>,
136
+ ) => Effect.Effect<void, ContainerOperationError>;
137
+ readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
138
+ readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
139
+ readonly removeAllowedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
140
+ readonly removeDeniedHost: (hostname: string) => Effect.Effect<void, ContainerOperationError>;
108
141
  }
109
142
 
110
143
  /** Effect client for a Container namespace binding. */
111
- export interface ContainerNamespaceClient {
144
+ export interface ContainerNamespaceClient<
145
+ Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
146
+ > {
112
147
  readonly definition: ContainerNamespaceDefinition;
113
148
  readonly getByName: (
114
149
  name: string,
115
150
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
116
- ) => Effect.Effect<ContainerInstanceClient, ContainerOperationError>;
151
+ ) => Effect.Effect<ContainerInstanceClient<ContainerStubOf<Namespace>>, ContainerOperationError>;
117
152
  readonly byName: (
118
153
  name: string,
119
154
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
120
- ) => ContainerInstanceClient;
121
- readonly unsafeRaw: Effect.Effect<ContainerNamespaceResource>;
155
+ ) => ContainerInstanceClient<ContainerStubOf<Namespace>>;
156
+ readonly rawUnsafe: Effect.Effect<Namespace>;
122
157
  }
123
158
 
124
- export type ContainerNamespaceStaticClient<R> = {
159
+ export type ContainerNamespaceStaticClient<
160
+ R,
161
+ Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
162
+ > = {
125
163
  readonly getByName: (
126
164
  name: string,
127
165
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
128
- ) => Effect.Effect<ContainerInstanceClient, ContainerOperationError, R>;
166
+ ) => Effect.Effect<
167
+ ContainerInstanceClient<ContainerStubOf<Namespace>>,
168
+ ContainerOperationError,
169
+ R
170
+ >;
129
171
  readonly byName: (
130
172
  name: string,
131
173
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
@@ -142,21 +184,44 @@ export type ContainerNamespaceStaticClient<R> = {
142
184
  readonly startAndWaitForPorts: (
143
185
  options?: ContainerStartAndWaitForPortsOptions,
144
186
  ) => Effect.Effect<void, ContainerOperationError, R>;
145
- readonly stop: (signal?: ContainerSignal) => Effect.Effect<void, ContainerOperationError, R>;
187
+ readonly waitForPort: (
188
+ options: ContainerWaitOptions,
189
+ ) => Effect.Effect<number, ContainerOperationError, R>;
190
+ readonly stop: (
191
+ signal?: ContainerStopSignal,
192
+ ) => Effect.Effect<void, ContainerOperationError, R>;
146
193
  readonly destroy: Effect.Effect<void, ContainerOperationError, R>;
147
- readonly unsafeRaw: Effect.Effect<ContainerStub, ContainerOperationError, R>;
194
+ readonly setAllowedHosts: (
195
+ hosts: ReadonlyArray<string>,
196
+ ) => Effect.Effect<void, ContainerOperationError, R>;
197
+ readonly setDeniedHosts: (
198
+ hosts: ReadonlyArray<string>,
199
+ ) => Effect.Effect<void, ContainerOperationError, R>;
200
+ readonly allowHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
201
+ readonly denyHost: (hostname: string) => Effect.Effect<void, ContainerOperationError, R>;
202
+ readonly removeAllowedHost: (
203
+ hostname: string,
204
+ ) => Effect.Effect<void, ContainerOperationError, R>;
205
+ readonly removeDeniedHost: (
206
+ hostname: string,
207
+ ) => Effect.Effect<void, ContainerOperationError, R>;
208
+ readonly rawUnsafe: Effect.Effect<ContainerStubOf<Namespace>, ContainerOperationError, R>;
148
209
  };
149
- readonly unsafeRaw: () => Effect.Effect<ContainerNamespaceResource, never, R>;
210
+ readonly rawUnsafe: Effect.Effect<Namespace, never, R>;
150
211
  };
151
212
 
152
213
  export type LayerOptions = {
153
214
  readonly binding: string;
154
215
  };
155
216
 
156
- export interface TagClass<Self, Id extends string>
217
+ export interface TagClass<
218
+ Self,
219
+ Id extends string,
220
+ Namespace extends ContainerNamespaceResource = ContainerNamespaceResource,
221
+ >
157
222
  extends
158
- Context.ServiceClass<Self, Id, ContainerNamespaceClient>,
159
- ContainerNamespaceStaticClient<Self> {
223
+ Context.ServiceClass<Self, `effect-cf/Container/${Id}`, ContainerNamespaceClient<Namespace>>,
224
+ ContainerNamespaceStaticClient<Self, Namespace> {
160
225
  readonly id: Id;
161
226
  readonly layer: (
162
227
  options: LayerOptions,
@@ -184,36 +249,100 @@ const tryContainerPromise = <A>(
184
249
  }),
185
250
  });
186
251
 
187
- const makeInstanceClient = (
252
+ const makeInstanceClient = <Stub extends ContainerStub>(
188
253
  definition: ContainerNamespaceDefinition,
189
254
  instance: string,
190
- 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
- }),
255
+ stub: Stub,
256
+ ): ContainerInstanceClient<Stub> => {
257
+ const spanOptions = (operation: string) => ({
258
+ attributes: { binding: definition.binding, instance, operation },
259
+ });
260
+ const hostListOperation = (operation: "setAllowedHosts" | "setDeniedHosts") =>
261
+ Effect.fn(
262
+ `ContainerNamespace.${operation}`,
263
+ spanOptions(operation),
264
+ )(function* (hosts: ReadonlyArray<string>) {
265
+ return yield* tryContainerPromise(definition, instance, operation, () =>
266
+ stub[operation]([...hosts]),
267
+ );
268
+ });
269
+ const hostnameOperation = (
270
+ operation: "allowHost" | "denyHost" | "removeAllowedHost" | "removeDeniedHost",
271
+ ) =>
272
+ Effect.fn(
273
+ `ContainerNamespace.${operation}`,
274
+ spanOptions(operation),
275
+ )(function* (hostname: string) {
276
+ return yield* tryContainerPromise(definition, instance, operation, () =>
277
+ stub[operation](hostname),
278
+ );
279
+ });
280
+
281
+ return {
282
+ rawUnsafe: Effect.succeed(stub),
283
+ state: tryContainerPromise(definition, instance, "state", () => stub.getState()).pipe(
284
+ Effect.flatMap(S.decodeUnknownEffect(ContainerState)),
285
+ Effect.mapError((cause) =>
286
+ cause instanceof ContainerOperationError
287
+ ? cause
288
+ : new ContainerOperationError({
289
+ binding: definition.binding,
290
+ instance,
291
+ operation: "state",
292
+ cause,
293
+ }),
294
+ ),
295
+ Effect.withSpan("ContainerNamespace.state", spanOptions("state")),
204
296
  ),
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),
297
+ fetch: Effect.fn(
298
+ "ContainerNamespace.fetch",
299
+ spanOptions("fetch"),
300
+ )(function* (input: RequestInfo | URL, init?: RequestInit) {
301
+ return yield* tryContainerPromise(definition, instance, "fetch", () =>
302
+ stub.fetch(input, init),
303
+ );
304
+ }),
305
+ start: Effect.fn(
306
+ "ContainerNamespace.start",
307
+ spanOptions("start"),
308
+ )(function* (options?: ContainerStartOptions, waitOptions?: ContainerWaitOptions) {
309
+ return yield* tryContainerPromise(definition, instance, "start", () =>
310
+ stub.start(options, waitOptions),
311
+ );
312
+ }),
313
+ startAndWaitForPorts: Effect.fn(
314
+ "ContainerNamespace.startAndWaitForPorts",
315
+ spanOptions("startAndWaitForPorts"),
316
+ )(function* (options?: ContainerStartAndWaitForPortsOptions) {
317
+ return yield* tryContainerPromise(definition, instance, "startAndWaitForPorts", () =>
318
+ stub.startAndWaitForPorts(options),
319
+ );
320
+ }),
321
+ waitForPort: Effect.fn(
322
+ "ContainerNamespace.waitForPort",
323
+ spanOptions("waitForPort"),
324
+ )(function* (options: ContainerWaitOptions) {
325
+ return yield* tryContainerPromise(definition, instance, "waitForPort", () =>
326
+ stub.waitForPort(options),
327
+ );
328
+ }),
329
+ stop: Effect.fn(
330
+ "ContainerNamespace.stop",
331
+ spanOptions("stop"),
332
+ )(function* (signal?: ContainerStopSignal) {
333
+ return yield* tryContainerPromise(definition, instance, "stop", () => stub.stop(signal));
334
+ }),
335
+ destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()).pipe(
336
+ Effect.withSpan("ContainerNamespace.destroy", spanOptions("destroy")),
213
337
  ),
214
- stop: (signal) => tryContainerPromise(definition, instance, "stop", () => stub.stop(signal)),
215
- destroy: tryContainerPromise(definition, instance, "destroy", () => stub.destroy()),
216
- });
338
+ setAllowedHosts: hostListOperation("setAllowedHosts"),
339
+ setDeniedHosts: hostListOperation("setDeniedHosts"),
340
+ allowHost: hostnameOperation("allowHost"),
341
+ denyHost: hostnameOperation("denyHost"),
342
+ removeAllowedHost: hostnameOperation("removeAllowedHost"),
343
+ removeDeniedHost: hostnameOperation("removeDeniedHost"),
344
+ };
345
+ };
217
346
 
218
347
  export const isContainerNamespaceResource = (value: unknown): value is ContainerNamespaceResource =>
219
348
  typeof value === "object" &&
@@ -222,13 +351,22 @@ export const isContainerNamespaceResource = (value: unknown): value is Container
222
351
 
223
352
  export const makeClient =
224
353
  (definition: ContainerNamespaceDefinition) =>
225
- (namespace: ContainerNamespaceResource): ContainerNamespaceClient => {
354
+ <Namespace extends ContainerNamespaceResource>(
355
+ namespace: Namespace,
356
+ ): ContainerNamespaceClient<Namespace> => {
226
357
  const getByName = (
227
358
  name: string,
228
359
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
229
360
  ) =>
230
361
  Effect.try({
231
- try: () => makeInstanceClient(definition, name, namespace.getByName(name, options)),
362
+ try: () =>
363
+ makeInstanceClient(
364
+ definition,
365
+ name,
366
+ // getByName has a single call signature, so its return type is
367
+ // exactly the namespace's stub type.
368
+ namespace.getByName(name, options) as ContainerStubOf<Namespace>,
369
+ ),
232
370
  catch: (cause) =>
233
371
  new ContainerOperationError({
234
372
  binding: definition.binding,
@@ -241,19 +379,31 @@ export const makeClient =
241
379
  const byName = (
242
380
  name: string,
243
381
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
244
- ): ContainerInstanceClient => {
382
+ ): ContainerInstanceClient<ContainerStubOf<Namespace>> => {
245
383
  const instance = getByName(name, options);
246
384
 
247
385
  return {
248
- unsafeRaw: Effect.flatMap(instance, (client) => client.unsafeRaw),
386
+ rawUnsafe: Effect.flatMap(instance, (client) => client.rawUnsafe),
249
387
  state: Effect.flatMap(instance, (client) => client.state),
250
388
  fetch: (input, init) => Effect.flatMap(instance, (client) => client.fetch(input, init)),
251
389
  start: (startOptions, waitOptions) =>
252
390
  Effect.flatMap(instance, (client) => client.start(startOptions, waitOptions)),
253
391
  startAndWaitForPorts: (startOptions) =>
254
392
  Effect.flatMap(instance, (client) => client.startAndWaitForPorts(startOptions)),
393
+ waitForPort: (waitOptions) =>
394
+ Effect.flatMap(instance, (client) => client.waitForPort(waitOptions)),
255
395
  stop: (signal) => Effect.flatMap(instance, (client) => client.stop(signal)),
256
396
  destroy: Effect.flatMap(instance, (client) => client.destroy),
397
+ setAllowedHosts: (hosts) =>
398
+ Effect.flatMap(instance, (client) => client.setAllowedHosts(hosts)),
399
+ setDeniedHosts: (hosts) =>
400
+ Effect.flatMap(instance, (client) => client.setDeniedHosts(hosts)),
401
+ allowHost: (hostname) => Effect.flatMap(instance, (client) => client.allowHost(hostname)),
402
+ denyHost: (hostname) => Effect.flatMap(instance, (client) => client.denyHost(hostname)),
403
+ removeAllowedHost: (hostname) =>
404
+ Effect.flatMap(instance, (client) => client.removeAllowedHost(hostname)),
405
+ removeDeniedHost: (hostname) =>
406
+ Effect.flatMap(instance, (client) => client.removeDeniedHost(hostname)),
257
407
  };
258
408
  };
259
409
 
@@ -261,17 +411,25 @@ export const makeClient =
261
411
  definition,
262
412
  getByName,
263
413
  byName,
264
- unsafeRaw: Effect.succeed(namespace),
414
+ rawUnsafe: Effect.succeed(namespace),
265
415
  };
266
416
  };
267
417
 
268
- export const layer = <Self>(
269
- tag: Context.Service<Self, ContainerNamespaceClient>,
418
+ export const layer = <Self, Namespace extends ContainerNamespaceResource>(
419
+ tag: Context.Service<Self, ContainerNamespaceClient<Namespace>>,
270
420
  definition: ContainerNamespaceDefinition,
271
421
  ) =>
272
- Binding.layer(tag, definition.binding, isContainerNamespaceResource, makeClient(definition), {
273
- expected: expectedContainerNamespace,
274
- });
422
+ Binding.layer(
423
+ tag,
424
+ definition.binding,
425
+ // Runtime validation stays structural; the exact namespace type is
426
+ // asserted by the consumer's binding declaration, like env typing.
427
+ isContainerNamespaceResource as (value: unknown) => value is Namespace,
428
+ makeClient(definition),
429
+ {
430
+ expected: expectedContainerNamespace,
431
+ },
432
+ );
275
433
 
276
434
  export const make = <Id extends string>(id: Id) => Tag<ContainerNamespaceService<Id>>()<Id>(id);
277
435
 
@@ -286,20 +444,21 @@ export interface ContainerNamespaceService<Id extends string> {
286
444
 
287
445
  /** Creates a typed Effect service for a Cloudflare Container namespace. */
288
446
  export const Tag =
289
- <Self>() =>
447
+ <Self, Namespace extends ContainerNamespaceResource = ContainerNamespaceResource>() =>
290
448
  <Id extends string>(id: Id) => {
291
- const tag = Context.Service<Self, ContainerNamespaceClient>()(id);
449
+ const tag = Context.Service<Self, ContainerNamespaceClient<Namespace>>()(
450
+ `effect-cf/Container/${id}` as const,
451
+ );
292
452
  const makeLayer = (definition: LayerOptions) => layer(tag, definition);
293
453
 
294
- const getByName = (
454
+ const getByName = Effect.fnUntraced(function* (
295
455
  name: string,
296
456
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
297
- ) =>
298
- Effect.gen(function* () {
299
- const namespace = yield* tag;
457
+ ) {
458
+ const namespace = yield* tag;
300
459
 
301
- return yield* namespace.getByName(name, options);
302
- });
460
+ return yield* namespace.getByName(name, options);
461
+ });
303
462
 
304
463
  const byName = (
305
464
  name: string,
@@ -316,25 +475,37 @@ export const Tag =
316
475
  Effect.flatMap(getByName(name, options), (instance) =>
317
476
  instance.startAndWaitForPorts(startOptions),
318
477
  ),
319
- stop: (signal?: ContainerSignal) =>
478
+ waitForPort: (waitOptions: ContainerWaitOptions) =>
479
+ Effect.flatMap(getByName(name, options), (instance) => instance.waitForPort(waitOptions)),
480
+ stop: (signal?: ContainerStopSignal) =>
320
481
  Effect.flatMap(getByName(name, options), (instance) => instance.stop(signal)),
321
482
  destroy: Effect.flatMap(getByName(name, options), (instance) => instance.destroy),
322
- unsafeRaw: Effect.flatMap(getByName(name, options), (instance) => instance.unsafeRaw),
483
+ setAllowedHosts: (hosts: ReadonlyArray<string>) =>
484
+ Effect.flatMap(getByName(name, options), (instance) => instance.setAllowedHosts(hosts)),
485
+ setDeniedHosts: (hosts: ReadonlyArray<string>) =>
486
+ Effect.flatMap(getByName(name, options), (instance) => instance.setDeniedHosts(hosts)),
487
+ allowHost: (hostname: string) =>
488
+ Effect.flatMap(getByName(name, options), (instance) => instance.allowHost(hostname)),
489
+ denyHost: (hostname: string) =>
490
+ Effect.flatMap(getByName(name, options), (instance) => instance.denyHost(hostname)),
491
+ removeAllowedHost: (hostname: string) =>
492
+ Effect.flatMap(getByName(name, options), (instance) =>
493
+ instance.removeAllowedHost(hostname),
494
+ ),
495
+ removeDeniedHost: (hostname: string) =>
496
+ Effect.flatMap(getByName(name, options), (instance) => instance.removeDeniedHost(hostname)),
497
+ rawUnsafe: Effect.flatMap(getByName(name, options), (instance) => instance.rawUnsafe),
323
498
  });
324
499
 
325
- const unsafeRaw = Effect.fn(function* () {
326
- const namespace = yield* tag;
327
-
328
- return yield* namespace.unsafeRaw;
329
- });
500
+ const rawUnsafe = Effect.flatMap(tag, (namespace) => namespace.rawUnsafe);
330
501
 
331
502
  return Object.assign(tag, {
332
503
  id,
333
504
  layer: makeLayer,
334
505
  getByName,
335
506
  byName,
336
- unsafeRaw,
337
- }) as TagClass<Self, Id>;
507
+ rawUnsafe,
508
+ }) as TagClass<Self, Id, Namespace>;
338
509
  };
339
510
 
340
511
  export const ContainerNamespace = Tag;
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;