effect-cf 0.5.0 → 0.7.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/src/Worker.ts CHANGED
@@ -1,10 +1,14 @@
1
1
  import { WorkerEntrypoint as CloudflareWorkerEntrypoint } from "cloudflare:workers";
2
2
  import { Cause, ConfigProvider, Context, Effect, Layer, ManagedRuntime, type Scope } from "effect";
3
+ import type { Schema as S } from "effect";
3
4
  import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
4
5
 
6
+ import type * as Binding from "./Binding";
5
7
  import { WorkerConfig, WorkerEnvironment, type WorkerEnv } from "./Environment";
6
8
  import { fromMessage, fromMessageBatch, type QueueHandler } from "./Queue";
9
+ import type * as Rpc from "./Rpc";
7
10
  import type * as RpcDefinition from "./RpcDefinition";
11
+ import type * as ServiceBinding from "./ServiceBinding";
8
12
  import * as WorkerDefinition from "./WorkerDefinition";
9
13
  import * as Entrypoint from "./internal/Entrypoint";
10
14
  import { fromExecutionContext, type RunWaitUntilEffect } from "./internal/WorkerContext";
@@ -282,38 +286,128 @@ export const makeFetchHandler = <ROut, LayerError, Env extends WorkerEnv = Worke
282
286
  };
283
287
  };
284
288
 
285
- export const Tag = WorkerDefinition.Tag;
289
+ export type ServiceFreeSchema = S.Codec<any, any, never, never>;
286
290
 
287
- export const method = WorkerDefinition.method;
291
+ export interface Method<
292
+ Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
293
+ Success extends ServiceFreeSchema = ServiceFreeSchema,
294
+ > {
295
+ readonly args: Args;
296
+ readonly success: Success;
297
+ }
298
+
299
+ export namespace Method {
300
+ export type Any = Method<ReadonlyArray<ServiceFreeSchema>, ServiceFreeSchema>;
301
+
302
+ type ArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = Args extends readonly []
303
+ ? []
304
+ : Args extends readonly [
305
+ infer Head extends ServiceFreeSchema,
306
+ ...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
307
+ ]
308
+ ? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
309
+ : Array<S.Schema.Type<Args[number]>>;
288
310
 
289
- export const implement = WorkerDefinition.implement;
311
+ export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
312
+
313
+ export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
314
+ }
290
315
 
291
- export type Definition<
292
- Id extends string = string,
293
- Methods extends RpcDefinition.Methods = RpcDefinition.Methods,
294
- > = WorkerDefinition.Definition<Id, Methods>;
316
+ export type Methods = Record<string, Method.Any>;
317
+
318
+ export type NoReservedMethods<MethodsShape extends Methods> =
319
+ Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
320
+
321
+ export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
322
+ readonly id: Id;
323
+ readonly methods: MethodsShape;
324
+ }
295
325
 
296
326
  export namespace Definition {
297
- export type Any = WorkerDefinition.Definition.Any;
327
+ export type Any = Definition<string, Methods>;
298
328
  }
299
329
 
300
- export type ServerApi<Self extends WorkerDefinition.Definition.Any> =
301
- WorkerDefinition.ServerApi<Self>;
330
+ export type ServerApi<Self extends Definition.Any> = {
331
+ readonly [Key in keyof Self["methods"]]: (
332
+ ...args: Method.Args<Self["methods"][Key]>
333
+ ) => Promise<Method.Success<Self["methods"][Key]>>;
334
+ };
335
+
336
+ export type Api<Self extends Definition.Any> = Rpc.Provider<ServerApi<Self>, ReservedMethodName>;
302
337
 
303
- export type Api<Self extends WorkerDefinition.Definition.Any> = WorkerDefinition.Api<Self>;
338
+ export type Handlers<ROut, Self extends Definition.Any> = {
339
+ readonly [Key in keyof Self["methods"]]: (
340
+ ...args: Method.Args<Self["methods"][Key]>
341
+ ) => WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
342
+ };
304
343
 
305
- export type Handlers<
306
- ROut,
307
- Self extends WorkerDefinition.Definition.Any,
308
- > = WorkerDefinition.Handlers<ROut, Self>;
344
+ export interface Options<ROut, Self extends Definition.Any> extends Omit<
345
+ WorkerOptions<ROut, Handlers<ROut, Self>>,
346
+ "rpc"
347
+ > {
348
+ readonly rpc: Handlers<ROut, Self>;
349
+ }
309
350
 
310
- export type Options<ROut, Self extends WorkerDefinition.Definition.Any> = WorkerDefinition.Options<
351
+ export type LayerOptions = WorkerDefinition.LayerOptions;
352
+
353
+ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
354
+ Self,
355
+ Id,
356
+ ServiceBinding.ServiceBindingEffectClient<
357
+ Api<Definition<Id, MethodsShape>>,
358
+ Definition<Id, MethodsShape>
359
+ >
360
+ > &
361
+ ServiceBinding.ServiceBindingStaticClient<
362
+ Self,
363
+ Api<Definition<Id, MethodsShape>>,
364
+ Definition<Id, MethodsShape>
365
+ > & {
366
+ readonly id: Id;
367
+ readonly methods: MethodsShape;
368
+ readonly make: <ROut, LayerError>(
369
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
370
+ options: Options<ROut, Definition<Id, MethodsShape>>,
371
+ ) => WorkerClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
372
+ readonly layer: (
373
+ options: LayerOptions,
374
+ ) => Layer.Layer<
375
+ Self,
376
+ Binding.BindingNotFoundError | Binding.BindingValidationError,
377
+ WorkerEnvironment
378
+ >;
379
+ };
380
+
381
+ export type TagFactory = <Self>() => <Id extends string, const MethodsShape extends Methods>(
382
+ id: Id,
383
+ methods: MethodsShape & NoReservedMethods<MethodsShape>,
384
+ ) => TagClass<Self, Id, MethodsShape>;
385
+
386
+ export const Tag = WorkerDefinition.Tag as unknown as TagFactory;
387
+
388
+ export const method = WorkerDefinition.method as {
389
+ <Success extends ServiceFreeSchema>(definition: {
390
+ readonly success: Success;
391
+ }): Method<readonly [], Success>;
392
+ <
393
+ const Args extends ReadonlyArray<ServiceFreeSchema>,
394
+ Success extends ServiceFreeSchema,
395
+ >(definition: {
396
+ readonly args: Args;
397
+ readonly success: Success;
398
+ }): Method<Args, Success>;
399
+ };
400
+
401
+ export const implement = WorkerDefinition.implement as unknown as <
311
402
  ROut,
312
- Self
313
- >;
403
+ const Self extends Definition.Any,
404
+ >(
405
+ _definition: Self,
406
+ handlers: Handlers<ROut, Self>,
407
+ ) => Handlers<ROut, Self>;
314
408
 
315
409
  export type HandlerEffect<
316
410
  ROut,
317
- Self extends WorkerDefinition.Definition.Any,
411
+ Self extends Definition.Any,
318
412
  Key extends keyof Self["methods"],
319
- > = WorkerDefinition.HandlerEffect<ROut, Self, Key>;
413
+ > = WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
@@ -1,28 +1,77 @@
1
- import { Effect, type Layer } from "effect";
1
+ import { Context, Effect, type Layer } from "effect";
2
+ import type { Schema as S } from "effect";
2
3
 
4
+ import * as Binding from "./Binding";
3
5
  import type { WorkerEnvironment } from "./Environment";
4
6
  import * as WorkerEntrypoint from "./Worker";
5
7
  import type { WorkerRpcHandler } from "./Worker";
8
+ import type * as Rpc from "./Rpc";
6
9
  import * as RpcDefinition from "./RpcDefinition";
7
10
  import * as ServiceBinding from "./ServiceBinding";
8
11
 
12
+ export type ServiceFreeSchema = S.Codec<any, any, never, never>;
13
+
14
+ export interface Method<
15
+ Args extends ReadonlyArray<ServiceFreeSchema> = ReadonlyArray<ServiceFreeSchema>,
16
+ Success extends ServiceFreeSchema = ServiceFreeSchema,
17
+ > {
18
+ readonly args: Args;
19
+ readonly success: Success;
20
+ }
21
+
22
+ export namespace Method {
23
+ export type Any = Method<ReadonlyArray<ServiceFreeSchema>, ServiceFreeSchema>;
24
+
25
+ type ArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> = Args extends readonly []
26
+ ? []
27
+ : Args extends readonly [
28
+ infer Head extends ServiceFreeSchema,
29
+ ...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
30
+ ]
31
+ ? [S.Schema.Type<Head>, ...ArgsFromSchemas<Tail>]
32
+ : Array<S.Schema.Type<Args[number]>>;
33
+
34
+ type EncodedArgsFromSchemas<Args extends ReadonlyArray<ServiceFreeSchema>> =
35
+ Args extends readonly []
36
+ ? []
37
+ : Args extends readonly [
38
+ infer Head extends ServiceFreeSchema,
39
+ ...infer Tail extends ReadonlyArray<ServiceFreeSchema>,
40
+ ]
41
+ ? [S.Codec.Encoded<Head>, ...EncodedArgsFromSchemas<Tail>]
42
+ : Array<S.Codec.Encoded<Args[number]>>;
43
+
44
+ export type Args<Self extends Any> = ArgsFromSchemas<Self["args"]>;
45
+
46
+ export type EncodedArgs<Self extends Any> = EncodedArgsFromSchemas<Self["args"]>;
47
+
48
+ export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
49
+
50
+ export type EncodedSuccess<Self extends Any> = S.Codec.Encoded<Self["success"]>;
51
+ }
52
+
53
+ export type Methods = Record<string, Method.Any>;
54
+
9
55
  /**
10
56
  * RPC contract for a Worker service.
11
57
  *
12
58
  * Create with {@link make} and reuse to type both worker implementations and
13
59
  * service bindings in other workers.
14
60
  */
15
- export type Definition<
16
- Id extends string = string,
17
- Methods extends RpcDefinition.Methods = RpcDefinition.Methods,
18
- > = RpcDefinition.Definition<Id, Methods>;
61
+ export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
62
+ readonly id: Id;
63
+ readonly methods: MethodsShape;
64
+ }
19
65
 
20
66
  export namespace Definition {
21
- export type Any = RpcDefinition.Definition.Any;
67
+ export type Any = Definition<string, Methods>;
22
68
  }
23
69
 
24
70
  export type ReservedMethodName = WorkerEntrypoint.ReservedMethodName;
25
71
 
72
+ export type NoReservedMethods<MethodsShape extends Methods> =
73
+ Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
74
+
26
75
  const reservedMethodNames = new Set<string>([
27
76
  "constructor",
28
77
  "dup",
@@ -43,38 +92,27 @@ const reservedMethodNames = new Set<string>([
43
92
  /**
44
93
  * Promise-based client API derived from a {@link Definition}.
45
94
  */
46
- export type ServerApi<Self extends Definition.Any> = RpcDefinition.Definition.ServerApi<Self>;
47
-
48
- export type Api<Self extends Definition.Any> = RpcDefinition.Definition.Api<
49
- Self,
50
- ReservedMethodName
51
- >;
52
-
53
- declare const BindingServiceTypeId: unique symbol;
95
+ export type ServerApi<Self extends Definition.Any> = {
96
+ readonly [Key in keyof Self["methods"]]: (
97
+ ...args: Method.Args<Self["methods"][Key]>
98
+ ) => Promise<Method.Success<Self["methods"][Key]>>;
99
+ };
54
100
 
55
- /**
56
- * Nominal service marker for a worker binding created with {@link make}.
57
- */
58
- export interface BindingService<Id extends string, Self extends Definition.Any> {
59
- readonly [BindingServiceTypeId]: {
60
- readonly id: Id;
61
- readonly definition: Self;
62
- };
63
- }
101
+ export type Api<Self extends Definition.Any> = Rpc.Provider<ServerApi<Self>, ReservedMethodName>;
64
102
 
65
103
  /**
66
104
  * Effect handlers for each RPC method in a worker definition.
67
105
  */
68
106
  export type Handlers<ROut, Self extends Definition.Any> = {
69
107
  readonly [Key in keyof Self["methods"]]: (
70
- ...args: RpcDefinition.Method.Args<Self["methods"][Key]>
71
- ) => WorkerRpcHandler<ROut, RpcDefinition.Method.Success<Self["methods"][Key]>>;
108
+ ...args: Method.Args<Self["methods"][Key]>
109
+ ) => WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
72
110
  };
73
111
 
74
112
  type BoundaryHandlers<ROut, Self extends Definition.Any> = {
75
113
  readonly [Key in keyof Self["methods"]]: (
76
114
  ...args: Array<unknown>
77
- ) => WorkerRpcHandler<ROut, RpcDefinition.Method.EncodedSuccess<Self["methods"][Key]>>;
115
+ ) => WorkerRpcHandler<ROut, Method.EncodedSuccess<Self["methods"][Key]>>;
78
116
  };
79
117
 
80
118
  /**
@@ -87,10 +125,57 @@ export interface Options<ROut, Self extends Definition.Any> extends Omit<
87
125
  readonly rpc: Handlers<ROut, Self>;
88
126
  }
89
127
 
128
+ export type LayerOptions = {
129
+ readonly binding: string;
130
+ };
131
+
132
+ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
133
+ Self,
134
+ Id,
135
+ ServiceBinding.ServiceBindingEffectClient<
136
+ Api<Definition<Id, MethodsShape>>,
137
+ Definition<Id, MethodsShape>
138
+ >
139
+ > &
140
+ ServiceBinding.ServiceBindingStaticClient<
141
+ Self,
142
+ Api<Definition<Id, MethodsShape>>,
143
+ Definition<Id, MethodsShape>
144
+ > & {
145
+ readonly id: Id;
146
+ readonly methods: MethodsShape;
147
+ readonly make: <ROut, LayerError>(
148
+ layer: Layer.Layer<
149
+ ROut,
150
+ LayerError,
151
+ WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
152
+ >,
153
+ options: Options<ROut, Definition<Id, MethodsShape>>,
154
+ ) => WorkerEntrypoint.WorkerClass<Handlers<ROut, Definition<Id, MethodsShape>>, ROut>;
155
+ readonly layer: (
156
+ options: LayerOptions,
157
+ ) => Layer.Layer<
158
+ Self,
159
+ Binding.BindingNotFoundError | Binding.BindingValidationError,
160
+ WorkerEnvironment
161
+ >;
162
+ };
163
+
90
164
  /**
91
165
  * Defines a single RPC method schema in a worker definition.
92
166
  */
93
- export const method = RpcDefinition.method;
167
+ export const method = RpcDefinition.method as {
168
+ <Success extends ServiceFreeSchema>(definition: {
169
+ readonly success: Success;
170
+ }): Method<readonly [], Success>;
171
+ <
172
+ const Args extends ReadonlyArray<ServiceFreeSchema>,
173
+ Success extends ServiceFreeSchema,
174
+ >(definition: {
175
+ readonly args: Args;
176
+ readonly success: Success;
177
+ }): Method<Args, Success>;
178
+ };
94
179
 
95
180
  /**
96
181
  * Creates a typed worker RPC definition plus helpers for implementation and bindings.
@@ -105,9 +190,9 @@ export const method = RpcDefinition.method;
105
190
  * });
106
191
  * ```
107
192
  */
108
- const makeDefinition = <Id extends string, const MethodsShape extends RpcDefinition.Methods>(
193
+ const makeDefinition = <Id extends string, const MethodsShape extends Methods>(
109
194
  id: Id,
110
- methods: MethodsShape & RpcDefinition.NoReservedMethods<MethodsShape, ReservedMethodName>,
195
+ methods: MethodsShape & NoReservedMethods<MethodsShape>,
111
196
  ) => {
112
197
  type SelfDefinition = Definition<Id, MethodsShape>;
113
198
  RpcDefinition.assertNoReservedMethods("Worker", methods, reservedMethodNames);
@@ -126,49 +211,88 @@ const makeDefinition = <Id extends string, const MethodsShape extends RpcDefinit
126
211
  ...options,
127
212
  rpc: wrapHandlers(definition, options.rpc),
128
213
  }),
129
- Binding:
130
- <Self>() =>
131
- <BindingId extends string>(
132
- bindingId: BindingId,
133
- binding: Omit<ServiceBinding.ServiceBindingDefinition<SelfDefinition>, "definition">,
134
- ) =>
135
- ServiceBinding.Service<Self, Api<SelfDefinition>>()<BindingId, SelfDefinition>(bindingId, {
136
- ...binding,
137
- definition,
138
- }),
139
- binding: <BindingId extends string>(
140
- bindingId: BindingId,
141
- binding: Omit<ServiceBinding.ServiceBindingDefinition<SelfDefinition>, "definition">,
142
- ) =>
143
- ServiceBinding.Service<BindingService<BindingId, SelfDefinition>, Api<SelfDefinition>>()<
144
- BindingId,
145
- SelfDefinition
146
- >(bindingId, {
147
- ...binding,
148
- definition,
149
- }),
150
214
  });
151
215
  };
152
216
 
153
- export const make = makeDefinition;
217
+ export const make = <Id extends string, const MethodsShape extends Methods>(
218
+ id: Id,
219
+ methods: MethodsShape & NoReservedMethods<MethodsShape>,
220
+ ) =>
221
+ Tag<Definition<Id, MethodsShape>>()<Id, MethodsShape>(
222
+ id,
223
+ methods as MethodsShape & NoReservedMethods<MethodsShape>,
224
+ );
154
225
 
155
226
  export const Tag =
156
- <_Self>() =>
157
- <Id extends string, const MethodsShape extends RpcDefinition.Methods>(
227
+ <Self>() =>
228
+ <Id extends string, const MethodsShape extends Methods>(
158
229
  id: Id,
159
- methods: MethodsShape & RpcDefinition.NoReservedMethods<MethodsShape, ReservedMethodName>,
230
+ methods: MethodsShape & NoReservedMethods<MethodsShape>,
160
231
  ) => {
161
232
  const definition = makeDefinition<Id, MethodsShape>(id, methods);
233
+ type SelfDefinition = Definition<Id, MethodsShape>;
234
+ type ClientApi = Api<SelfDefinition>;
235
+ const tag = Context.Service<
236
+ Self,
237
+ ServiceBinding.ServiceBindingEffectClient<ClientApi, SelfDefinition>
238
+ >()(id);
239
+
240
+ const bindingDefinition = (binding: LayerOptions) => ({
241
+ ...binding,
242
+ definition,
243
+ });
244
+
245
+ const layer = (binding: LayerOptions) =>
246
+ ServiceBinding.layer<Self, ClientApi, SelfDefinition>(tag, bindingDefinition(binding));
247
+
248
+ const fetch = (input: RequestInfo | URL, init?: RequestInit) =>
249
+ Effect.gen(function* () {
250
+ const service = yield* tag;
251
+ return yield* service.fetch(input, init);
252
+ });
253
+
254
+ const rpc = <Method extends keyof ClientApi>(
255
+ method: Method,
256
+ ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
257
+ ) =>
258
+ Effect.gen(function* () {
259
+ const service = yield* tag;
260
+ return yield* service.rpc(method as never, ...(args as never));
261
+ });
262
+
263
+ const call = <Method extends keyof ClientApi>(
264
+ method: Method,
265
+ ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
266
+ ) =>
267
+ Effect.gen(function* () {
268
+ const service = yield* tag;
269
+ return yield* service.call(method as never, ...(args as never));
270
+ });
271
+
272
+ const scopedCall = <Method extends keyof ClientApi>(
273
+ method: Method,
274
+ ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
275
+ ) =>
276
+ Effect.gen(function* () {
277
+ const service = yield* tag;
278
+ return yield* service.scopedCall(method as never, ...(args as never));
279
+ });
162
280
 
163
- abstract class WorkerDefinitionClass {
164
- static readonly id = definition.id;
165
- static readonly methods = definition.methods;
166
- static readonly make = definition.make;
167
- static readonly Binding = definition.Binding;
168
- static readonly binding = definition.binding;
169
- }
281
+ const directMethods = ServiceBinding.makeDirectMethods<Self, ClientApi, SelfDefinition>(
282
+ definition,
283
+ call as never,
284
+ );
170
285
 
171
- return WorkerDefinitionClass as (abstract new () => object) & typeof definition;
286
+ return Object.assign(tag, directMethods, {
287
+ id: definition.id,
288
+ methods: definition.methods,
289
+ make: definition.make,
290
+ layer,
291
+ fetch,
292
+ rpc,
293
+ call,
294
+ scopedCall,
295
+ }) as unknown as TagClass<Self, Id, MethodsShape>;
172
296
  };
173
297
 
174
298
  export const Worker = Tag;
@@ -209,4 +333,4 @@ export type HandlerEffect<
209
333
  ROut,
210
334
  Self extends Definition.Any,
211
335
  Key extends keyof Self["methods"],
212
- > = WorkerRpcHandler<ROut, RpcDefinition.Method.Success<Self["methods"][Key]>>;
336
+ > = WorkerRpcHandler<ROut, Method.Success<Self["methods"][Key]>>;
package/src/Workflow.ts CHANGED
@@ -252,10 +252,6 @@ export const waitForEvent = <Payload>(
252
252
  workflowStep.waitForEvent<Payload>(name, options),
253
253
  );
254
254
 
255
- export const Tag = WorkflowDefinition.Tag;
256
-
257
- export const implement = WorkflowDefinition.implement;
258
-
259
255
  export type Definition<
260
256
  Id extends string = string,
261
257
  Payload extends WorkflowDefinition.Definition.Any["payload"] =
@@ -268,6 +264,29 @@ export namespace Definition {
268
264
  export type Any = WorkflowDefinition.Definition.Any;
269
265
  }
270
266
 
267
+ export type LayerOptions = WorkflowDefinition.LayerOptions;
268
+
269
+ export type TagClass<
270
+ Self,
271
+ Id extends string,
272
+ Payload extends WorkflowDefinition.Definition.Any["payload"],
273
+ Result extends WorkflowDefinition.Definition.Any["result"],
274
+ > = WorkflowDefinition.TagClass<Self, Id, Payload, Result>;
275
+
276
+ export const Tag: <Self>() => <
277
+ Id extends string,
278
+ Payload extends WorkflowDefinition.Definition.Any["payload"],
279
+ Result extends WorkflowDefinition.Definition.Any["result"],
280
+ >(
281
+ id: Id,
282
+ definition: {
283
+ readonly payload: Payload;
284
+ readonly result: Result;
285
+ },
286
+ ) => TagClass<Self, Id, Payload, Result> = WorkflowDefinition.Tag;
287
+
288
+ export const implement = WorkflowDefinition.implement;
289
+
271
290
  export type Handler<
272
291
  ROut,
273
292
  Self extends WorkflowDefinition.Definition.Any,