effect-cf 0.6.0 → 0.8.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.
@@ -1,4 +1,4 @@
1
- import { Data, Effect, type Scope } from "effect";
1
+ import { Context, Data, Effect, type Scope } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
4
  import * as CloudflareRpc from "./Rpc";
@@ -6,8 +6,6 @@ import type * as DurableObjectDefinition from "./DurableObjectDefinition";
6
6
  import * as RpcDefinition from "./RpcDefinition";
7
7
  import * as RpcInvocation from "./internal/RpcInvocation";
8
8
 
9
- const TypeId = "effect-cf/DurableObjectNamespace" as const;
10
-
11
9
  interface DurableObjectFetcher {
12
10
  fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
13
11
  }
@@ -66,14 +64,6 @@ export interface DurableObjectNamespaceDefinition {
66
64
  readonly binding: string;
67
65
  }
68
66
 
69
- type ApiFromDefinition<Definition> = Definition extends DurableObjectDefinition.Definition.Any
70
- ? DurableObjectDefinition.ServerApi<Definition>
71
- : never;
72
-
73
- type ApiOrDefinition<Api extends object, Definition> = [Api] extends [never]
74
- ? ApiFromDefinition<Definition>
75
- : Api;
76
-
77
67
  /**
78
68
  * Namespace binding metadata with optional RPC schema.
79
69
  */
@@ -109,6 +99,10 @@ type StubMethodSuccess<Api, Method extends keyof Api> = RpcInvocation.AsyncMetho
109
99
  Api,
110
100
  Method
111
101
  >;
102
+ type StubMethodCloudflareReturn<
103
+ Api,
104
+ Method extends keyof Api,
105
+ > = RpcInvocation.AsyncMethodCloudflareReturn<Api, Method>;
112
106
 
113
107
  type StubCall<R, Api extends object> = <Method extends StubMethodKey<Api>>(
114
108
  stub: DurableObjectStubClient<Api>,
@@ -128,6 +122,7 @@ type DurableObjectDirectClient<R, Api extends object> = {
128
122
  };
129
123
 
130
124
  type DefinitionNamespaceDirectMethods<
125
+ R,
131
126
  Api extends object,
132
127
  Definition extends DurableObjectDefinition.Definition.Any,
133
128
  > = {
@@ -136,7 +131,8 @@ type DefinitionNamespaceDirectMethods<
136
131
  ...args: RpcDefinition.Method.Args<Definition["methods"][Method]>
137
132
  ) => Effect.Effect<
138
133
  RpcDefinition.Method.Success<Definition["methods"][Method]>,
139
- DurableObjectRpcError
134
+ DurableObjectRpcError,
135
+ R
140
136
  >;
141
137
  };
142
138
 
@@ -177,7 +173,7 @@ type DirectMethods<
177
173
  Api extends object,
178
174
  Definition,
179
175
  > = Definition extends DurableObjectDefinition.Definition.Any
180
- ? DefinitionNamespaceDirectMethods<Api, Definition> &
176
+ ? DefinitionNamespaceDirectMethods<R, Api, Definition> &
181
177
  DefinitionNamespaceDirectClientMethods<R, Definition>
182
178
  : {};
183
179
 
@@ -185,280 +181,231 @@ export type DurableObjectNamespaceEffectClient<
185
181
  Api extends object,
186
182
  Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
187
183
  > = DirectMethods<never, Api, Definition> & {
184
+ /** Creates a globally unique Durable Object id. */
188
185
  readonly newUniqueId: (
189
186
  options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
190
187
  ) => Effect.Effect<globalThis.DurableObjectId>;
188
+ /** Deterministically maps a name to a Durable Object id. */
191
189
  readonly idFromName: (name: string) => Effect.Effect<globalThis.DurableObjectId>;
190
+ /** Rehydrates a Durable Object id from its string form. */
192
191
  readonly idFromString: (id: string) => Effect.Effect<globalThis.DurableObjectId>;
192
+ /** Returns a stub for an existing Durable Object id. */
193
193
  readonly get: (
194
194
  id: globalThis.DurableObjectId,
195
195
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
196
196
  ) => Effect.Effect<DurableObjectStubClient<Api>>;
197
+ /** Returns a stub by deterministic name. */
197
198
  readonly getByName: (
198
199
  name: string,
199
200
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
200
201
  ) => Effect.Effect<DurableObjectStubClient<Api>>;
202
+ /** Selects a namespace pinned to a specific jurisdiction. */
201
203
  readonly jurisdiction: (
202
204
  jurisdiction: globalThis.DurableObjectJurisdiction,
203
205
  ) => Effect.Effect<DurableObjectNamespaceClient<Api>>;
206
+ /**
207
+ * Forwards an HTTP request to a Durable Object stub.
208
+ *
209
+ * Use this for fetch-based Durable Object APIs, including WebSocket upgrade
210
+ * forwarding where the native response must be preserved.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * import { Effect } from "effect";
215
+ *
216
+ * const program = Effect.gen(function* () {
217
+ * const rooms = yield* ChatRooms;
218
+ * const room = yield* rooms.getByName("general");
219
+ *
220
+ * return yield* rooms.fetch(room, new Request("https://worker.example/room"));
221
+ * });
222
+ * ```
223
+ */
204
224
  readonly fetch: (
205
225
  stub: DurableObjectStubClient<Api>,
206
226
  input: RequestInfo | URL,
207
227
  init?: RequestInit,
208
228
  ) => Effect.Effect<globalThis.Response, DurableObjectFetchError>;
229
+ /**
230
+ * Invokes a Durable Object RPC method and returns Cloudflare's raw RPC result.
231
+ *
232
+ * This preserves Cloudflare RPC behavior such as promise-like pipelining and
233
+ * transferable / disposable result objects. It does not resolve the returned
234
+ * promise-like value and it does not decode definition-backed success schemas.
235
+ *
236
+ * Most application code should use {@link call} instead.
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * import { Effect } from "effect";
241
+ *
242
+ * const program = Effect.gen(function* () {
243
+ * const counters = yield* Counters;
244
+ * const counter = yield* counters.getByName("main");
245
+ *
246
+ * const result = yield* counters.rpc(counter, "get");
247
+ * const value = yield* Effect.promise(() => result);
248
+ *
249
+ * return value;
250
+ * });
251
+ * ```
252
+ */
209
253
  readonly rpc: <Method extends StubMethodKey<Api>>(
210
254
  stub: DurableObjectStubClient<Api>,
211
255
  method: Method,
212
256
  ...args: StubMethodArgs<Api, Method>
213
- ) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError>;
257
+ ) => Effect.Effect<StubMethodCloudflareReturn<Api, Method>, DurableObjectRpcError>;
258
+ /**
259
+ * Invokes a Durable Object RPC method, resolves Cloudflare's RPC result, and
260
+ * decodes the success value when the namespace was created from a definition.
261
+ *
262
+ * This is the normal choice when application code wants the final typed value.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * import { Effect } from "effect";
267
+ *
268
+ * const program = Effect.gen(function* () {
269
+ * const counters = yield* Counters;
270
+ * const counter = yield* counters.getByName("main");
271
+ *
272
+ * return yield* counters.call(counter, "increment", 1);
273
+ * });
274
+ * ```
275
+ */
214
276
  readonly call: <Method extends StubMethodKey<Api>>(
215
277
  stub: DurableObjectStubClient<Api>,
216
278
  method: Method,
217
279
  ...args: StubMethodArgs<Api, Method>
218
280
  ) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError>;
281
+ /**
282
+ * Invokes a Durable Object RPC method in the current `Scope`, resolves
283
+ * Cloudflare's RPC result, decodes definition-backed success values, and
284
+ * disposes the resolved result when the scope closes if it implements
285
+ * `Symbol.dispose`.
286
+ *
287
+ * Use this for RPC methods that return Cloudflare RPC resources or other
288
+ * disposable objects whose lifetime should be tied to an Effect scope.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * import { Effect } from "effect";
293
+ *
294
+ * const program = Effect.scoped(
295
+ * Effect.gen(function* () {
296
+ * const rooms = yield* ChatRooms;
297
+ * const room = yield* rooms.getByName("general");
298
+ * const handle = yield* rooms.scopedCall(room, "openStream");
299
+ *
300
+ * return yield* handle.read();
301
+ * }),
302
+ * );
303
+ * ```
304
+ */
219
305
  readonly scopedCall: <Method extends StubMethodKey<Api>>(
220
306
  stub: DurableObjectStubClient<Api>,
221
307
  method: Method,
222
308
  ...args: StubMethodArgs<Api, Method>
223
309
  ) => Effect.Effect<Awaited<StubMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
310
+ /**
311
+ * Exposes the underlying native Durable Object namespace binding.
312
+ *
313
+ * Prefer the typed helpers above unless Cloudflare exposes a platform feature
314
+ * that is not wrapped by effect-cf yet.
315
+ */
224
316
  readonly unsafeRaw: Effect.Effect<DurableObjectNamespaceClient<Api>>;
225
317
  };
226
318
 
227
- /**
228
- * Creates a typed Effect service for a Durable Object namespace binding.
229
- *
230
- * Returned value includes namespace helpers (`get`, `getByName`, `newUniqueId`),
231
- * raw `fetch` / `call` helpers, and direct RPC helpers when `definition` is provided.
232
- *
233
- * @example
234
- * ```ts
235
- * const ChatRoom = DurableObjectDefinition.make("ChatRoom", {
236
- * postMessage: DurableObjectDefinition.method({
237
- * args: [Schema.String],
238
- * success: Schema.Void,
239
- * }),
240
- * });
241
- *
242
- * const ChatRooms = ChatRoom.namespace("CHAT_ROOMS", { binding: "CHAT_ROOMS" });
243
- *
244
- * const program = Effect.gen(function* () {
245
- * const room = ChatRooms.byName("general");
246
- * yield* room.postMessage("hello");
247
- * });
248
- * ```
249
- */
250
- export const Service =
251
- <Self, Api extends object = never>() =>
252
- <
253
- Id extends string,
254
- const Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
255
- >(
256
- id: Id,
257
- definition: DurableObjectNamespaceBindingDefinition<Definition>,
258
- ) => {
259
- type NamespaceApi = ApiOrDefinition<Api, Definition>;
260
- type NamespaceClient = DurableObjectNamespaceClient<NamespaceApi>;
261
- type StubClient = DurableObjectStubClient<NamespaceApi>;
262
-
263
- const hasFunction = (value: object, key: string): boolean =>
264
- typeof Reflect.get(value, key) === "function";
265
-
266
- const isDurableObjectNamespaceClient = (value: unknown): value is NamespaceClient =>
267
- typeof value === "object" &&
268
- value !== null &&
269
- hasFunction(value, "getByName") &&
270
- hasFunction(value, "get") &&
271
- hasFunction(value, "idFromName") &&
272
- hasFunction(value, "idFromString") &&
273
- hasFunction(value, "newUniqueId") &&
274
- hasFunction(value, "jurisdiction");
319
+ export type DurableObjectNamespaceStaticClient<
320
+ R,
321
+ Api extends object,
322
+ Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
323
+ > = DirectMethods<R, Api, Definition> & {
324
+ readonly newUniqueId: (
325
+ options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
326
+ ) => Effect.Effect<globalThis.DurableObjectId, never, R>;
327
+ readonly idFromName: (name: string) => Effect.Effect<globalThis.DurableObjectId, never, R>;
328
+ readonly idFromString: (id: string) => Effect.Effect<globalThis.DurableObjectId, never, R>;
329
+ readonly get: (
330
+ id: globalThis.DurableObjectId,
331
+ options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
332
+ ) => Effect.Effect<DurableObjectStubClient<Api>, never, R>;
333
+ readonly getByName: (
334
+ name: string,
335
+ options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
336
+ ) => Effect.Effect<DurableObjectStubClient<Api>, never, R>;
337
+ readonly jurisdiction: (
338
+ jurisdiction: globalThis.DurableObjectJurisdiction,
339
+ ) => Effect.Effect<DurableObjectNamespaceClient<Api>, never, R>;
340
+ readonly fetch: (
341
+ stub: DurableObjectStubClient<Api>,
342
+ input: RequestInfo | URL,
343
+ init?: RequestInit,
344
+ ) => Effect.Effect<globalThis.Response, DurableObjectFetchError, R>;
345
+ readonly rpc: <Method extends StubMethodKey<Api>>(
346
+ stub: DurableObjectStubClient<Api>,
347
+ method: Method,
348
+ ...args: StubMethodArgs<Api, Method>
349
+ ) => Effect.Effect<StubMethodCloudflareReturn<Api, Method>, DurableObjectRpcError, R>;
350
+ readonly call: <Method extends StubMethodKey<Api>>(
351
+ stub: DurableObjectStubClient<Api>,
352
+ method: Method,
353
+ ...args: StubMethodArgs<Api, Method>
354
+ ) => Effect.Effect<StubMethodSuccess<Api, Method>, DurableObjectRpcError, R>;
355
+ readonly scopedCall: <Method extends StubMethodKey<Api>>(
356
+ stub: DurableObjectStubClient<Api>,
357
+ method: Method,
358
+ ...args: StubMethodArgs<Api, Method>
359
+ ) => Effect.Effect<Awaited<StubMethodSuccess<Api, Method>>, unknown, Scope.Scope | R>;
360
+ readonly unsafeRaw: () => Effect.Effect<DurableObjectNamespaceClient<Api>, never, R>;
361
+ };
275
362
 
276
- const makeClient = (
277
- namespace: NamespaceClient,
278
- ): DurableObjectNamespaceEffectClient<NamespaceApi, Definition> => {
279
- const newUniqueId = (options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions) =>
280
- Effect.sync(() => namespace.newUniqueId(options));
281
-
282
- const idFromName = (name: string) => Effect.sync(() => namespace.idFromName(name));
283
-
284
- const idFromString = (id: string) => Effect.sync(() => namespace.idFromString(id));
285
-
286
- const get = (
287
- id: globalThis.DurableObjectId,
288
- options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
289
- ) => Effect.sync(() => namespace.get(id, options));
290
-
291
- const getByName = (
292
- name: string,
293
- options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
294
- ) => Effect.sync(() => namespace.getByName(name, options));
295
-
296
- const jurisdiction = (value: globalThis.DurableObjectJurisdiction) =>
297
- Effect.sync(() => namespace.jurisdiction(value));
298
-
299
- const fetch = (stub: StubClient, input: RequestInfo | URL, init?: RequestInit) =>
300
- Effect.tryPromise({
301
- try: () => stub.fetch(input, init),
302
- catch: (cause) => new DurableObjectFetchError({ binding: definition.binding, cause }),
303
- });
304
-
305
- const rpc = <Method extends StubMethodKey<NamespaceApi>>(
306
- stub: StubClient,
307
- method: Method,
308
- ...args: StubMethodArgs<NamespaceApi, Method>
309
- ) =>
310
- Effect.gen(function* () {
311
- const methodName = String(method);
312
- const encodedArgs =
313
- definition.definition === undefined
314
- ? args
315
- : yield* RpcDefinition.encodeArgs(
316
- definition.definition,
317
- methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
318
- args as never,
319
- ).pipe(
320
- Effect.mapError(
321
- (cause) =>
322
- new DurableObjectRpcError({
323
- binding: definition.binding,
324
- method: methodName,
325
- cause,
326
- }),
327
- ),
328
- );
329
-
330
- return yield* RpcInvocation.invokeRpcMethod(
331
- stub,
332
- method,
333
- encodedArgs as StubMethodArgs<NamespaceApi, Method>,
334
- (cause) =>
335
- new DurableObjectRpcError({
336
- binding: definition.binding,
337
- method: methodName,
338
- cause,
339
- }),
340
- );
341
- });
342
-
343
- const call = <Method extends StubMethodKey<NamespaceApi>>(
344
- stub: StubClient,
345
- method: Method,
346
- ...args: StubMethodArgs<NamespaceApi, Method>
347
- ) =>
348
- Effect.gen(function* () {
349
- const methodName = String(method);
350
- const value = yield* CloudflareRpc.resolve(yield* rpc(stub, method, ...args)).pipe(
351
- Effect.mapError(
352
- (cause) =>
353
- new DurableObjectRpcError({
354
- binding: definition.binding,
355
- method: methodName,
356
- cause,
357
- }),
358
- ),
359
- );
360
-
361
- if (definition.definition === undefined) {
362
- return value as StubMethodSuccess<NamespaceApi, Method>;
363
- }
364
-
365
- const decoded = yield* RpcDefinition.decodeSuccess(
366
- definition.definition,
367
- methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
368
- value,
369
- ).pipe(
370
- Effect.mapError(
371
- (cause) =>
372
- new DurableObjectRpcError({
373
- binding: definition.binding,
374
- method: methodName,
375
- cause,
376
- }),
377
- ),
378
- );
379
-
380
- return decoded as StubMethodSuccess<NamespaceApi, Method>;
381
- });
382
-
383
- const scopedCall = <Method extends StubMethodKey<NamespaceApi>>(
384
- stub: StubClient,
385
- method: Method,
386
- ...args: StubMethodArgs<NamespaceApi, Method>
387
- ) =>
388
- Effect.gen(function* () {
389
- const result = yield* rpc(stub, method, ...args);
390
- return yield* CloudflareRpc.scoped(result);
391
- });
392
-
393
- const directMethods = makeDirectMethods<never, NamespaceApi, Definition>(
394
- definition.definition,
395
- {
396
- call,
397
- fetch,
398
- get,
399
- getByName,
400
- },
401
- );
402
-
403
- return Object.assign(directMethods, {
404
- newUniqueId,
405
- idFromName,
406
- idFromString,
407
- get,
408
- getByName,
409
- jurisdiction,
410
- fetch,
411
- rpc,
412
- call,
413
- scopedCall,
414
- unsafeRaw: Effect.succeed(namespace),
415
- }) as DurableObjectNamespaceEffectClient<NamespaceApi, Definition>;
416
- };
363
+ const hasFunction = (value: object, key: string): boolean =>
364
+ typeof Reflect.get(value, key) === "function";
365
+
366
+ export const isDurableObjectNamespaceClient = <Api extends object>(
367
+ value: unknown,
368
+ ): value is DurableObjectNamespaceClient<Api> =>
369
+ typeof value === "object" &&
370
+ value !== null &&
371
+ hasFunction(value, "getByName") &&
372
+ hasFunction(value, "get") &&
373
+ hasFunction(value, "idFromName") &&
374
+ hasFunction(value, "idFromString") &&
375
+ hasFunction(value, "newUniqueId") &&
376
+ hasFunction(value, "jurisdiction");
377
+
378
+ export const makeClient = <
379
+ Api extends object,
380
+ const Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
381
+ >(
382
+ definition: DurableObjectNamespaceBindingDefinition<Definition>,
383
+ ): ((
384
+ namespace: DurableObjectNamespaceClient<Api>,
385
+ ) => DurableObjectNamespaceEffectClient<Api, Definition>) => {
386
+ type NamespaceClient = DurableObjectNamespaceClient<Api>;
387
+ type StubClient = DurableObjectStubClient<Api>;
417
388
 
418
- const tag = Binding.Service<Self>()(
419
- id,
420
- definition.binding,
421
- (value): value is NamespaceClient => isDurableObjectNamespaceClient(value),
422
- makeClient,
423
- );
424
-
425
- const newUniqueId = Effect.fnUntraced(function* (
426
- options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions,
427
- ) {
428
- const namespace = yield* tag;
429
- return yield* namespace.newUniqueId(options);
430
- });
389
+ return (namespace: NamespaceClient) => {
390
+ const newUniqueId = (options?: globalThis.DurableObjectNamespaceNewUniqueIdOptions) =>
391
+ Effect.sync(() => namespace.newUniqueId(options));
431
392
 
432
- const idFromName = Effect.fnUntraced(function* (name: string) {
433
- const namespace = yield* tag;
434
- return yield* namespace.idFromName(name);
435
- });
393
+ const idFromName = (name: string) => Effect.sync(() => namespace.idFromName(name));
436
394
 
437
- const idFromString = Effect.fnUntraced(function* (id: string) {
438
- const namespace = yield* tag;
439
- return yield* namespace.idFromString(id);
440
- });
395
+ const idFromString = (id: string) => Effect.sync(() => namespace.idFromString(id));
441
396
 
442
- const get = Effect.fnUntraced(function* (
397
+ const get = (
443
398
  id: globalThis.DurableObjectId,
444
399
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
445
- ) {
446
- const namespace = yield* tag;
447
- return yield* namespace.get(id, options);
448
- });
400
+ ) => Effect.sync(() => namespace.get(id, options));
449
401
 
450
- const getByName = Effect.fnUntraced(function* (
402
+ const getByName = (
451
403
  name: string,
452
404
  options?: globalThis.DurableObjectNamespaceGetDurableObjectOptions,
453
- ) {
454
- const namespace = yield* tag;
455
- return yield* namespace.getByName(name, options);
456
- });
405
+ ) => Effect.sync(() => namespace.getByName(name, options));
457
406
 
458
- const jurisdiction = Effect.fnUntraced(function* (value: globalThis.DurableObjectJurisdiction) {
459
- const namespace = yield* tag;
460
- return yield* namespace.jurisdiction(value);
461
- });
407
+ const jurisdiction = (value: globalThis.DurableObjectJurisdiction) =>
408
+ Effect.sync(() => namespace.jurisdiction(value));
462
409
 
463
410
  const fetch = (stub: StubClient, input: RequestInfo | URL, init?: RequestInit) =>
464
411
  Effect.tryPromise({
@@ -466,10 +413,10 @@ export const Service =
466
413
  catch: (cause) => new DurableObjectFetchError({ binding: definition.binding, cause }),
467
414
  });
468
415
 
469
- const rpc = <Method extends StubMethodKey<NamespaceApi>>(
416
+ const rpc = <Method extends StubMethodKey<Api>>(
470
417
  stub: StubClient,
471
418
  method: Method,
472
- ...args: StubMethodArgs<NamespaceApi, Method>
419
+ ...args: StubMethodArgs<Api, Method>
473
420
  ) =>
474
421
  Effect.gen(function* () {
475
422
  const methodName = String(method);
@@ -494,7 +441,7 @@ export const Service =
494
441
  return yield* RpcInvocation.invokeRpcMethod(
495
442
  stub,
496
443
  method,
497
- encodedArgs as StubMethodArgs<NamespaceApi, Method>,
444
+ encodedArgs as StubMethodArgs<Api, Method>,
498
445
  (cause) =>
499
446
  new DurableObjectRpcError({
500
447
  binding: definition.binding,
@@ -504,14 +451,20 @@ export const Service =
504
451
  );
505
452
  });
506
453
 
507
- const call = <Method extends StubMethodKey<NamespaceApi>>(
508
- stub: StubClient,
509
- method: Method,
510
- ...args: StubMethodArgs<NamespaceApi, Method>
454
+ const decodeSuccess = <Method extends StubMethodKey<Api>>(
455
+ methodName: string,
456
+ value: Awaited<StubMethodCloudflareReturn<Api, Method>>,
511
457
  ) =>
512
458
  Effect.gen(function* () {
513
- const methodName = String(method);
514
- const value = yield* CloudflareRpc.resolve(yield* rpc(stub, method, ...args)).pipe(
459
+ if (definition.definition === undefined) {
460
+ return value as StubMethodSuccess<Api, Method>;
461
+ }
462
+
463
+ const decoded = yield* RpcDefinition.decodeSuccess(
464
+ definition.definition,
465
+ methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
466
+ value,
467
+ ).pipe(
515
468
  Effect.mapError(
516
469
  (cause) =>
517
470
  new DurableObjectRpcError({
@@ -522,15 +475,17 @@ export const Service =
522
475
  ),
523
476
  );
524
477
 
525
- if (definition.definition === undefined) {
526
- return value as StubMethodSuccess<NamespaceApi, Method>;
527
- }
478
+ return decoded as StubMethodSuccess<Api, Method>;
479
+ });
528
480
 
529
- const decoded = yield* RpcDefinition.decodeSuccess(
530
- definition.definition,
531
- methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
532
- value,
533
- ).pipe(
481
+ const call = <Method extends StubMethodKey<Api>>(
482
+ stub: StubClient,
483
+ method: Method,
484
+ ...args: StubMethodArgs<Api, Method>
485
+ ) =>
486
+ Effect.gen(function* () {
487
+ const methodName = String(method);
488
+ const value = yield* CloudflareRpc.resolve(yield* rpc(stub, method, ...args)).pipe(
534
489
  Effect.mapError(
535
490
  (cause) =>
536
491
  new DurableObjectRpcError({
@@ -541,34 +496,29 @@ export const Service =
541
496
  ),
542
497
  );
543
498
 
544
- return decoded as StubMethodSuccess<NamespaceApi, Method>;
499
+ return yield* decodeSuccess<Method>(methodName, value);
545
500
  });
546
501
 
547
- const scopedCall = <Method extends StubMethodKey<NamespaceApi>>(
502
+ const scopedCall = <Method extends StubMethodKey<Api>>(
548
503
  stub: StubClient,
549
504
  method: Method,
550
- ...args: StubMethodArgs<NamespaceApi, Method>
505
+ ...args: StubMethodArgs<Api, Method>
551
506
  ) =>
552
507
  Effect.gen(function* () {
508
+ const methodName = String(method);
553
509
  const result = yield* rpc(stub, method, ...args);
554
- return yield* CloudflareRpc.scoped(result);
510
+ const value = yield* CloudflareRpc.scoped(result);
511
+ return yield* decodeSuccess<Method>(methodName, value);
555
512
  });
556
513
 
557
- const unsafeRaw = Effect.fnUntraced(function* () {
558
- const namespace = yield* tag;
559
- return yield* namespace.unsafeRaw;
560
- });
561
-
562
- const directMethods = makeDirectMethods<Self, NamespaceApi, Definition>(definition.definition, {
514
+ const directMethods = makeDirectMethods<never, Api, Definition>(definition.definition, {
563
515
  call,
564
516
  fetch,
565
517
  get,
566
518
  getByName,
567
519
  });
568
520
 
569
- return Object.assign(tag, directMethods, {
570
- [TypeId]: TypeId,
571
- definition,
521
+ return Object.assign(directMethods, {
572
522
  newUniqueId,
573
523
  idFromName,
574
524
  idFromString,
@@ -579,26 +529,28 @@ export const Service =
579
529
  rpc,
580
530
  call,
581
531
  scopedCall,
582
- unsafeRaw,
583
- }) as typeof tag &
584
- DirectMethods<Self, NamespaceApi, Definition> & {
585
- readonly [TypeId]: typeof TypeId;
586
- readonly definition: DurableObjectNamespaceBindingDefinition<Definition>;
587
- readonly newUniqueId: typeof newUniqueId;
588
- readonly idFromName: typeof idFromName;
589
- readonly idFromString: typeof idFromString;
590
- readonly get: typeof get;
591
- readonly getByName: typeof getByName;
592
- readonly jurisdiction: typeof jurisdiction;
593
- readonly fetch: typeof fetch;
594
- readonly rpc: typeof rpc;
595
- readonly call: typeof call;
596
- readonly scopedCall: typeof scopedCall;
597
- readonly unsafeRaw: typeof unsafeRaw;
598
- };
532
+ unsafeRaw: Effect.succeed(namespace),
533
+ }) as DurableObjectNamespaceEffectClient<Api, Definition>;
599
534
  };
535
+ };
600
536
 
601
- const makeDirectMethods = <
537
+ export const layer = <
538
+ Self,
539
+ Api extends object,
540
+ const Definition extends DurableObjectDefinition.Definition.Any | undefined = undefined,
541
+ >(
542
+ tag: Context.Service<Self, DurableObjectNamespaceEffectClient<Api, Definition>>,
543
+ definition: DurableObjectNamespaceBindingDefinition<Definition>,
544
+ ) =>
545
+ Binding.layer(
546
+ tag,
547
+ definition.binding,
548
+ (value): value is DurableObjectNamespaceClient<Api> =>
549
+ isDurableObjectNamespaceClient<Api>(value),
550
+ makeClient<Api, Definition>(definition),
551
+ );
552
+
553
+ export const makeDirectMethods = <
602
554
  R,
603
555
  Api extends object,
604
556
  Definition extends DurableObjectDefinition.Definition.Any | undefined,
@@ -655,7 +607,7 @@ const makeDirectMethods = <
655
607
  stub: DurableObjectStubClient<Api>,
656
608
  method: string,
657
609
  ...args: Array<unknown>
658
- ) => Effect.Effect<unknown, DurableObjectRpcError>
610
+ ) => Effect.Effect<unknown, DurableObjectRpcError, R>
659
611
  )(stub, methodName, ...args);
660
612
  });
661
613
  }