effect-cf 0.5.0 → 0.6.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.
@@ -19,6 +19,19 @@ export interface QueueBindingDefinition<Message extends RpcDefinition.ServiceFre
19
19
  readonly message: Message;
20
20
  }
21
21
 
22
+ export interface QueueBindingClient<Message extends RpcDefinition.ServiceFreeSchema> {
23
+ readonly send: (
24
+ message: S.Schema.Type<Message>,
25
+ options?: QueueSendOptions,
26
+ ) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
27
+ readonly sendBatch: (
28
+ messages: Iterable<MessageSendRequest<S.Schema.Type<Message>>>,
29
+ options?: QueueSendBatchOptions,
30
+ ) => Effect.Effect<void, QueueOperationError | S.SchemaError>;
31
+ readonly metrics: () => Effect.Effect<QueueMetrics, QueueOperationError>;
32
+ readonly unsafeRaw: Effect.Effect<globalThis.Queue<S.Codec.Encoded<Message>>>;
33
+ }
34
+
22
35
  declare const QueueServiceTypeId: unique symbol;
23
36
 
24
37
  /** Nominal service marker for Queue services created with {@link make}. */
@@ -81,19 +94,45 @@ export const Service =
81
94
  type Body = S.Schema.Type<Message>;
82
95
  type EncodedBody = S.Codec.Encoded<Message>;
83
96
 
97
+ const encodeMessage = S.encodeEffect(definition.message);
98
+
99
+ const makeClient = (queue: globalThis.Queue<EncodedBody>): QueueBindingClient<Message> => ({
100
+ send: Effect.fnUntraced(function* (message: Body, options?: QueueSendOptions) {
101
+ const encoded = yield* encodeMessage(message);
102
+
103
+ yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
104
+ }),
105
+ sendBatch: Effect.fnUntraced(function* (
106
+ messages: Iterable<MessageSendRequest<Body>>,
107
+ options?: QueueSendBatchOptions,
108
+ ) {
109
+ const encodedMessages: Array<MessageSendRequest<EncodedBody>> = [];
110
+
111
+ for (const message of messages) {
112
+ encodedMessages.push({
113
+ ...message,
114
+ body: yield* encodeMessage(message.body),
115
+ });
116
+ }
117
+
118
+ yield* tryQueuePromise(definition.binding, "sendBatch", () =>
119
+ queue.sendBatch(encodedMessages, options),
120
+ );
121
+ }),
122
+ metrics: () => tryQueuePromise(definition.binding, "metrics", () => queue.metrics()),
123
+ unsafeRaw: Effect.succeed(queue),
124
+ });
125
+
84
126
  const tag = Binding.Service<Self>()(
85
127
  id,
86
128
  definition.binding,
87
129
  (value): value is globalThis.Queue<EncodedBody> => isQueue<EncodedBody>(value),
130
+ makeClient,
88
131
  );
89
132
 
90
- const encodeMessage = S.encodeEffect(definition.message);
91
-
92
133
  const send = Effect.fnUntraced(function* (message: Body, options?: QueueSendOptions) {
93
134
  const queue = yield* tag;
94
- const encoded = yield* encodeMessage(message);
95
-
96
- return yield* tryQueuePromise(definition.binding, "send", () => queue.send(encoded, options));
135
+ yield* queue.send(message, options);
97
136
  });
98
137
 
99
138
  const sendBatch = Effect.fnUntraced(function* (
@@ -101,24 +140,17 @@ export const Service =
101
140
  options?: QueueSendBatchOptions,
102
141
  ) {
103
142
  const queue = yield* tag;
104
- const encodedMessages: Array<MessageSendRequest<EncodedBody>> = [];
105
-
106
- for (const message of messages) {
107
- encodedMessages.push({
108
- ...message,
109
- body: yield* encodeMessage(message.body),
110
- });
111
- }
112
-
113
- return yield* tryQueuePromise(definition.binding, "sendBatch", () =>
114
- queue.sendBatch(encodedMessages, options),
115
- );
143
+ yield* queue.sendBatch(messages, options);
116
144
  });
117
145
 
118
146
  const metrics = Effect.fnUntraced(function* () {
119
147
  const queue = yield* tag;
148
+ return yield* queue.metrics();
149
+ });
120
150
 
121
- return yield* tryQueuePromise(definition.binding, "metrics", () => queue.metrics());
151
+ const unsafeRaw = Effect.fnUntraced(function* () {
152
+ const queue = yield* tag;
153
+ return yield* queue.unsafeRaw;
122
154
  });
123
155
 
124
156
  return Object.assign(tag, {
@@ -127,5 +159,6 @@ export const Service =
127
159
  send,
128
160
  sendBatch,
129
161
  metrics,
162
+ unsafeRaw,
130
163
  });
131
164
  };
@@ -1,4 +1,4 @@
1
- import { Data, Effect } from "effect";
1
+ import { Data, Effect, type Scope } from "effect";
2
2
 
3
3
  import * as Binding from "./Binding";
4
4
  import * as CloudflareRpc from "./Rpc";
@@ -75,25 +75,47 @@ type ServiceMethodSuccess<Api, Method extends keyof Api> = RpcInvocation.AsyncMe
75
75
  Method
76
76
  >;
77
77
 
78
- type ServiceCall<Self, Api> = <Method extends ServiceMethodKey<Api>>(
78
+ type ServiceCall<R, Api> = <Method extends ServiceMethodKey<Api>>(
79
79
  method: Method,
80
80
  ...args: ServiceMethodArgs<Api, Method>
81
- ) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError, Self>;
81
+ ) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError, R>;
82
82
 
83
- type DefinitionDirectMethods<Self, Definition extends WorkerDefinition.Definition.Any> = {
83
+ type DefinitionDirectMethods<R, Definition extends WorkerDefinition.Definition.Any> = {
84
84
  readonly [Method in RpcDefinition.Definition.MethodNames<Definition>]: (
85
85
  ...args: RpcDefinition.Method.Args<Definition["methods"][Method]>
86
86
  ) => Effect.Effect<
87
87
  RpcDefinition.Method.Success<Definition["methods"][Method]>,
88
88
  ServiceBindingRpcError,
89
- Self
89
+ R
90
90
  >;
91
91
  };
92
92
 
93
- type DirectMethods<Self, Definition> = Definition extends WorkerDefinition.Definition.Any
94
- ? DefinitionDirectMethods<Self, Definition>
93
+ type DirectMethods<R, Definition> = Definition extends WorkerDefinition.Definition.Any
94
+ ? DefinitionDirectMethods<R, Definition>
95
95
  : {};
96
96
 
97
+ export type ServiceBindingEffectClient<
98
+ Api extends object,
99
+ Definition extends WorkerDefinition.Definition.Any | undefined = undefined,
100
+ > = DirectMethods<never, Definition> & {
101
+ readonly fetch: (
102
+ input: RequestInfo | URL,
103
+ init?: RequestInit,
104
+ ) => Effect.Effect<globalThis.Response, ServiceBindingFetchError>;
105
+ readonly rpc: <Method extends ServiceMethodKey<Api>>(
106
+ method: Method,
107
+ ...args: ServiceMethodArgs<Api, Method>
108
+ ) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError>;
109
+ readonly call: <Method extends ServiceMethodKey<Api>>(
110
+ method: Method,
111
+ ...args: ServiceMethodArgs<Api, Method>
112
+ ) => Effect.Effect<ServiceMethodSuccess<Api, Method>, ServiceBindingRpcError>;
113
+ readonly scopedCall: <Method extends ServiceMethodKey<Api>>(
114
+ method: Method,
115
+ ...args: ServiceMethodArgs<Api, Method>
116
+ ) => Effect.Effect<Awaited<ServiceMethodSuccess<Api, Method>>, unknown, Scope.Scope>;
117
+ };
118
+
97
119
  /**
98
120
  * Creates a typed Effect service for a Worker service binding.
99
121
  *
@@ -131,21 +153,125 @@ export const Service =
131
153
  ) => {
132
154
  type ServiceApi = ApiOrDefinition<Api, Definition>;
133
155
 
156
+ const makeClient = (
157
+ service: ServiceBindingClient<ServiceApi>,
158
+ ): ServiceBindingEffectClient<ServiceApi, Definition> => {
159
+ const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
160
+ Effect.tryPromise({
161
+ try: () => service.fetch(input, init),
162
+ catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
163
+ });
164
+
165
+ const rpc = <Method extends ServiceMethodKey<ServiceApi>>(
166
+ method: Method,
167
+ ...args: ServiceMethodArgs<ServiceApi, Method>
168
+ ) =>
169
+ Effect.gen(function* () {
170
+ const methodName = String(method);
171
+ const encodedArgs =
172
+ definition.definition === undefined
173
+ ? args
174
+ : yield* RpcDefinition.encodeArgs(
175
+ definition.definition,
176
+ methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
177
+ args as never,
178
+ ).pipe(
179
+ Effect.mapError(
180
+ (cause) =>
181
+ new ServiceBindingRpcError({
182
+ binding: definition.binding,
183
+ method: methodName,
184
+ cause,
185
+ }),
186
+ ),
187
+ );
188
+
189
+ return yield* RpcInvocation.invokeRpcMethod(
190
+ service,
191
+ method,
192
+ encodedArgs as ServiceMethodArgs<ServiceApi, Method>,
193
+ (cause) =>
194
+ new ServiceBindingRpcError({
195
+ binding: definition.binding,
196
+ method: methodName,
197
+ cause,
198
+ }),
199
+ );
200
+ });
201
+
202
+ const call = <Method extends ServiceMethodKey<ServiceApi>>(
203
+ method: Method,
204
+ ...args: ServiceMethodArgs<ServiceApi, Method>
205
+ ) =>
206
+ Effect.gen(function* () {
207
+ const methodName = String(method);
208
+ const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
209
+ Effect.mapError(
210
+ (cause) =>
211
+ new ServiceBindingRpcError({
212
+ binding: definition.binding,
213
+ method: methodName,
214
+ cause,
215
+ }),
216
+ ),
217
+ );
218
+
219
+ if (definition.definition === undefined) {
220
+ return value as ServiceMethodSuccess<ServiceApi, Method>;
221
+ }
222
+
223
+ const decoded = yield* RpcDefinition.decodeSuccess(
224
+ definition.definition,
225
+ methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
226
+ value,
227
+ ).pipe(
228
+ Effect.mapError(
229
+ (cause) =>
230
+ new ServiceBindingRpcError({
231
+ binding: definition.binding,
232
+ method: methodName,
233
+ cause,
234
+ }),
235
+ ),
236
+ );
237
+
238
+ return decoded as ServiceMethodSuccess<ServiceApi, Method>;
239
+ });
240
+
241
+ const scopedCall = <Method extends ServiceMethodKey<ServiceApi>>(
242
+ method: Method,
243
+ ...args: ServiceMethodArgs<ServiceApi, Method>
244
+ ) =>
245
+ Effect.gen(function* () {
246
+ const result = yield* rpc(method, ...args);
247
+ return yield* CloudflareRpc.scoped(result);
248
+ });
249
+
250
+ const directMethods = makeDirectMethods<never, ServiceApi, Definition>(
251
+ definition.definition,
252
+ call,
253
+ );
254
+
255
+ return Object.assign(directMethods, {
256
+ fetch,
257
+ rpc,
258
+ call,
259
+ scopedCall,
260
+ }) as ServiceBindingEffectClient<ServiceApi, Definition>;
261
+ };
262
+
134
263
  const tag = Binding.Service<Self>()(
135
264
  id,
136
265
  definition.binding,
137
266
  (value): value is ServiceBindingClient<ServiceApi> =>
138
267
  typeof value === "object" && value !== null && "fetch" in value,
268
+ makeClient,
139
269
  );
140
270
 
141
271
  const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
142
272
  Effect.gen(function* () {
143
273
  const service = yield* tag;
144
-
145
- return yield* Effect.tryPromise({
146
- try: () => service.fetch(input, init),
147
- catch: (cause) => new ServiceBindingFetchError({ binding: definition.binding, cause }),
148
- });
274
+ return yield* service.fetch(input, init);
149
275
  });
150
276
 
151
277
  const rpc = <Method extends ServiceMethodKey<ServiceApi>>(
@@ -154,36 +280,7 @@ export const Service =
154
280
  ) =>
155
281
  Effect.gen(function* () {
156
282
  const service = yield* tag;
157
- const methodName = String(method);
158
- const encodedArgs =
159
- definition.definition === undefined
160
- ? args
161
- : yield* RpcDefinition.encodeArgs(
162
- definition.definition,
163
- methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
164
- args as never,
165
- ).pipe(
166
- Effect.mapError(
167
- (cause) =>
168
- new ServiceBindingRpcError({
169
- binding: definition.binding,
170
- method: methodName,
171
- cause,
172
- }),
173
- ),
174
- );
175
-
176
- return yield* RpcInvocation.invokeRpcMethod(
177
- service,
178
- method,
179
- encodedArgs as ServiceMethodArgs<ServiceApi, Method>,
180
- (cause) =>
181
- new ServiceBindingRpcError({
182
- binding: definition.binding,
183
- method: methodName,
184
- cause,
185
- }),
186
- );
283
+ return yield* service.rpc(method, ...args);
187
284
  });
188
285
 
189
286
  const call = <Method extends ServiceMethodKey<ServiceApi>>(
@@ -191,38 +288,8 @@ export const Service =
191
288
  ...args: ServiceMethodArgs<ServiceApi, Method>
192
289
  ) =>
193
290
  Effect.gen(function* () {
194
- const methodName = String(method);
195
- const value = yield* CloudflareRpc.resolve(yield* rpc(method, ...args)).pipe(
196
- Effect.mapError(
197
- (cause) =>
198
- new ServiceBindingRpcError({
199
- binding: definition.binding,
200
- method: methodName,
201
- cause,
202
- }),
203
- ),
204
- );
205
-
206
- if (definition.definition === undefined) {
207
- return value as ServiceMethodSuccess<ServiceApi, Method>;
208
- }
209
-
210
- const decoded = yield* RpcDefinition.decodeSuccess(
211
- definition.definition,
212
- methodName as RpcDefinition.Definition.MethodNames<NonNullable<Definition>>,
213
- value,
214
- ).pipe(
215
- Effect.mapError(
216
- (cause) =>
217
- new ServiceBindingRpcError({
218
- binding: definition.binding,
219
- method: methodName,
220
- cause,
221
- }),
222
- ),
223
- );
224
-
225
- return decoded as ServiceMethodSuccess<ServiceApi, Method>;
291
+ const service = yield* tag;
292
+ return yield* service.call(method, ...args);
226
293
  });
227
294
 
228
295
  const scopedCall = <Method extends ServiceMethodKey<ServiceApi>>(
@@ -230,8 +297,8 @@ export const Service =
230
297
  ...args: ServiceMethodArgs<ServiceApi, Method>
231
298
  ) =>
232
299
  Effect.gen(function* () {
233
- const result = yield* rpc(method, ...args);
234
- return yield* CloudflareRpc.scoped(result);
300
+ const service = yield* tag;
301
+ return yield* service.scopedCall(method, ...args);
235
302
  });
236
303
 
237
304
  const directMethods = makeDirectMethods<Self, ServiceApi, Definition>(
@@ -257,14 +324,10 @@ export const Service =
257
324
  };
258
325
  };
259
326
 
260
- const makeDirectMethods = <
261
- Self,
262
- Api,
263
- Definition extends WorkerDefinition.Definition.Any | undefined,
264
- >(
327
+ const makeDirectMethods = <R, Api, Definition extends WorkerDefinition.Definition.Any | undefined>(
265
328
  rpcDefinition: Definition | undefined,
266
- call: ServiceCall<Self, Api>,
267
- ): DirectMethods<Self, Definition> => {
329
+ call: ServiceCall<R, Api>,
330
+ ): DirectMethods<R, Definition> => {
268
331
  const methods = {} as Record<string, unknown>;
269
332
 
270
333
  if (rpcDefinition !== undefined) {
@@ -274,5 +337,5 @@ const makeDirectMethods = <
274
337
  }
275
338
  }
276
339
 
277
- return methods as DirectMethods<Self, Definition>;
340
+ return methods as DirectMethods<R, Definition>;
278
341
  };