effect-cf 0.27.0 → 0.28.1

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 (67) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +6 -0
  3. package/dist/{Artifacts-DcoXcRP5.d.mts → Artifacts-DnHQA-Ax.d.mts} +42 -25
  4. package/dist/{Artifacts-BhWrdyqX.mjs → Artifacts-rq3hUKQE.mjs} +46 -30
  5. package/dist/{Binding-COCtoJXa.d.mts → Binding-xJJJPmiD.d.mts} +6 -5
  6. package/dist/ComputerArtifacts.d.mts +1 -1
  7. package/dist/ComputerArtifacts.mjs +9 -9
  8. package/dist/ComputerWorkspace.d.mts +5 -5
  9. package/dist/ComputerWorkspace.mjs +107 -75
  10. package/dist/ComputerWorkspaceHost.d.mts +1 -1
  11. package/dist/ComputerWorkspaceHost.mjs +18 -16
  12. package/dist/{DurableObjectState-Dux8KfjG.mjs → DurableObjectState-BjkK2kwH.mjs} +3 -3
  13. package/dist/{DurableObjectState-biHrHwrh.d.mts → DurableObjectState-OupNMMUl.d.mts} +89 -64
  14. package/dist/{ErrorMessage-_lg53p3W.mjs → ErrorMessage-BM0K-Nik.mjs} +1 -4
  15. package/dist/{Hyperdrive-6p59yyVk.d.mts → Hyperdrive-z-9fFz15.d.mts} +2 -2
  16. package/dist/HyperdrivePg.d.mts +2 -2
  17. package/dist/Vitest.d.mts +6 -5
  18. package/dist/Vitest.mjs +2 -2
  19. package/dist/{WorkflowBinding-zlRPCXVf.d.mts → WorkflowBinding-DUZtKCVM.d.mts} +5 -10
  20. package/dist/index.d.mts +88 -62
  21. package/dist/index.mjs +291 -181
  22. package/package.json +1 -1
  23. package/src/AiGateway.ts +62 -7
  24. package/src/AnalyticsEngine.ts +17 -13
  25. package/src/Artifacts.ts +111 -47
  26. package/src/Binding.ts +34 -23
  27. package/src/BrowserRendering.ts +72 -33
  28. package/src/CloudflareOtlp.ts +21 -11
  29. package/src/ComputerArtifacts.ts +17 -9
  30. package/src/ComputerWorkspace.ts +202 -98
  31. package/src/ComputerWorkspaceHost.ts +55 -27
  32. package/src/ContainerNamespace.ts +8 -9
  33. package/src/D1.ts +8 -14
  34. package/src/DurableObject.ts +33 -13
  35. package/src/DurableObjectAlarm.ts +17 -5
  36. package/src/DurableObjectDefinition.ts +89 -72
  37. package/src/DurableObjectNamespace.ts +26 -12
  38. package/src/DurableObjectRpcWebSocket.ts +9 -10
  39. package/src/DurableObjectState.ts +1 -0
  40. package/src/DurableObjectStorage.ts +5 -2
  41. package/src/DurableObjectWebSocket.ts +4 -1
  42. package/src/Email.ts +26 -25
  43. package/src/Hyperdrive.ts +4 -10
  44. package/src/Images.ts +12 -14
  45. package/src/Kv.ts +15 -17
  46. package/src/Queue.ts +2 -1
  47. package/src/QueueBinding.ts +7 -12
  48. package/src/QueueDefinition.ts +7 -2
  49. package/src/R2.ts +33 -24
  50. package/src/Rpc.ts +15 -21
  51. package/src/RpcDefinition.ts +33 -38
  52. package/src/ServiceBinding.ts +26 -8
  53. package/src/Vectorize.ts +45 -18
  54. package/src/Vitest.ts +9 -3
  55. package/src/WebTransport.ts +11 -5
  56. package/src/Worker.ts +97 -24
  57. package/src/WorkerDefinition.ts +143 -63
  58. package/src/WorkersAi.ts +50 -28
  59. package/src/Workflow.ts +66 -13
  60. package/src/WorkflowBinding.ts +18 -25
  61. package/src/WorkflowDefinition.ts +4 -2
  62. package/src/internal/Entrypoint.ts +29 -13
  63. package/src/internal/ErrorMessage.ts +7 -6
  64. package/src/internal/RpcInvocation.ts +34 -11
  65. package/src/internal/Runtime.ts +33 -22
  66. package/src/internal/Telemetry.ts +17 -10
  67. package/src/internal/WorkerContext.ts +3 -3
package/src/Worker.ts CHANGED
@@ -118,10 +118,6 @@ type WorkerFetchContext<ROut> =
118
118
  type WorkerRpcContext<ROut> = WorkerBaseContext<ROut> | Scope.Scope;
119
119
 
120
120
  type RuntimeContext<ROut> = WorkerBaseContext<ROut>;
121
- type RunOptions = {
122
- readonly eventLayer?: boolean;
123
- };
124
-
125
121
  const RunSymbol = Symbol.for("effect-cf/Worker/run");
126
122
  const FetchSymbol = Symbol.for("effect-cf/Worker/fetch");
127
123
 
@@ -151,7 +147,7 @@ export type WorkerRpcHandler<ROut, A = unknown> = Effect.Effect<A, unknown, Work
151
147
  */
152
148
  export type WorkerRpc<ROut> = Record<string, (...args: Array<any>) => WorkerRpcHandler<ROut>>;
153
149
 
154
- export type WorkerRpcShape<Rpc extends WorkerRpc<ROut>, ROut> = {
150
+ export type WorkerRpcContract<Rpc extends WorkerRpc<ROut>, ROut> = {
155
151
  readonly [Key in keyof Rpc]: Rpc[Key] extends (
156
152
  ...args: infer Args
157
153
  ) => Effect.Effect<infer A, unknown, WorkerRpcContext<ROut>>
@@ -167,7 +163,7 @@ export type RpcHandlers<ROut, Api> = {
167
163
  ? never
168
164
  : [Api[Key]] extends [never]
169
165
  ? never
170
- : Api[Key] extends (...args: Array<any>) => Promise<unknown>
166
+ : Api[Key] extends (...args: Array<any>) => Promise<any>
171
167
  ? Key
172
168
  : never
173
169
  : never]: Api[Key] extends (...args: infer Args) => Promise<infer A>
@@ -189,7 +185,8 @@ export interface WorkerOptions<
189
185
  *
190
186
  * The layer is built inside the event's Effect scope and finalized when the
191
187
  * event effect completes. Use this for event-scoped resources such as OTLP
192
- * trace/log exporters that should flush at event completion.
188
+ * trace/log exporters. Worker fetch and RPC handlers schedule a best-effort
189
+ * flush capped at two seconds; queue handlers do not flush automatically.
193
190
  */
194
191
  readonly eventLayer?: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
195
192
  /** Main request handler. */
@@ -204,11 +201,21 @@ export interface WorkerOptions<
204
201
  * Optional RPC methods exposed as class instance methods.
205
202
  *
206
203
  * After every invocation, the configured OTLP flusher is scheduled through
207
- * `WorkerContext.waitUntil`, including when the handler fails.
204
+ * `WorkerContext.waitUntil`, including when the handler fails. The scheduled
205
+ * flush silently settles within two seconds even if the exporter does not.
208
206
  */
209
207
  readonly rpc?: Rpc;
210
208
  }
211
209
 
210
+ type WorkerOptionsWithEventLayer<
211
+ RRuntime,
212
+ REvent,
213
+ EventLayerError,
214
+ Rpc extends WorkerRpc<RRuntime | REvent> = Record<never, never>,
215
+ > = Omit<WorkerOptions<RRuntime, REvent, EventLayerError, Rpc>, "eventLayer"> & {
216
+ readonly eventLayer: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
217
+ };
218
+
212
219
  export type FetchWorkerOptions<RRuntime, REvent = never, EventLayerError = never> = Omit<
213
220
  WorkerOptions<RRuntime, REvent, EventLayerError, Record<never, never>>,
214
221
  "rpc"
@@ -216,6 +223,13 @@ export type FetchWorkerOptions<RRuntime, REvent = never, EventLayerError = never
216
223
  readonly rpc?: never;
217
224
  };
218
225
 
226
+ type FetchWorkerOptionsWithEventLayer<RRuntime, REvent, EventLayerError> = Omit<
227
+ FetchWorkerOptions<RRuntime, REvent, EventLayerError>,
228
+ "eventLayer"
229
+ > & {
230
+ readonly eventLayer: Layer.Layer<REvent, EventLayerError, WorkerBaseContext<RRuntime>>;
231
+ };
232
+
219
233
  /**
220
234
  * Cloudflare `WorkerEntrypoint` constructor produced by {@link make}.
221
235
  */
@@ -225,7 +239,8 @@ export type WorkerClass<Rpc extends WorkerRpc<ROut>, ROut> = new (
225
239
  ) => CloudflareWorkerEntrypoint<WorkerEnv> & {
226
240
  fetch(request: Request): Promise<Response>;
227
241
  queue(batch: globalThis.MessageBatch): Promise<void>;
228
- } & WorkerRpcShape<Rpc, ROut>;
242
+ [FetchSymbol](request: Request, requestContext: globalThis.ExecutionContext): Promise<Response>;
243
+ } & WorkerRpcContract<Rpc, ROut>;
229
244
 
230
245
  /**
231
246
  * `ExportedHandler`-compatible fetch object produced by {@link makeFetchHandler}.
@@ -287,7 +302,8 @@ const isWorkerOptions = <ROut, REvent, EventLayerError, Rpc extends WorkerRpc<RO
287
302
  *
288
303
  * The flush is handed to `ctx.waitUntil` so it never delays the response
289
304
  * (including streaming bodies still being consumed). It is a no-op when the
290
- * context does not contain an `OtlpExporter.Flusher`.
305
+ * context does not contain an `OtlpExporter.Flusher`. The handed-off effect is
306
+ * best-effort and silently settles within two seconds.
291
307
  */
292
308
  const scheduleTelemetryFlush: Effect.Effect<void, never, WorkerContext> =
293
309
  Telemetry.scheduleTelemetryFlush((flush) =>
@@ -316,7 +332,17 @@ export function make<ROut, LayerError>(
316
332
  export function make<
317
333
  ROut,
318
334
  LayerError,
319
- REvent = never,
335
+ REvent,
336
+ EventLayerError = never,
337
+ const Rpc extends WorkerRpc<ROut | REvent> = Record<never, never>,
338
+ >(
339
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
340
+ options: WorkerOptionsWithEventLayer<ROut, REvent, EventLayerError, Rpc>,
341
+ ): WorkerClass<Rpc, ROut | REvent>;
342
+ export function make<
343
+ ROut,
344
+ LayerError,
345
+ REvent extends never = never,
320
346
  EventLayerError = never,
321
347
  const Rpc extends WorkerRpc<ROut | REvent> = Record<never, never>,
322
348
  >(
@@ -333,9 +359,9 @@ export function make<
333
359
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
334
360
  optionsOrFetch: WorkerOptions<ROut, REvent, EventLayerError, Rpc> | WorkerHandler<ROut>,
335
361
  ): WorkerClass<Rpc, ROut | REvent> {
336
- const options = isWorkerOptions(optionsOrFetch)
362
+ const options: WorkerOptions<ROut, REvent, EventLayerError, Rpc> = isWorkerOptions(optionsOrFetch)
337
363
  ? optionsOrFetch
338
- : ({ fetch: optionsOrFetch } as WorkerOptions<ROut, REvent, EventLayerError, Rpc>);
364
+ : { fetch: optionsOrFetch };
339
365
 
340
366
  class EffectWorker extends CloudflareWorkerEntrypoint<WorkerEnv> {
341
367
  readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
@@ -359,13 +385,25 @@ export function make<
359
385
 
360
386
  [RunSymbol]<A, E>(
361
387
  effect: Effect.Effect<A, E, RuntimeContext<ROut> | REvent | Scope.Scope>,
362
- runOptions: RunOptions = {},
363
388
  ): Promise<A> {
364
- return Runtime.runEventPromise(
365
- this.runtime,
366
- effect,
367
- runOptions.eventLayer === false ? undefined : options.eventLayer,
368
- );
389
+ const eventLayer = options.eventLayer;
390
+
391
+ if (eventLayer === undefined) {
392
+ // SAFETY: the public make overloads permit an absent event layer only when REvent is never.
393
+ return Runtime.runEventPromise(
394
+ this.runtime,
395
+ effect as Effect.Effect<A, E, RuntimeContext<ROut> | Scope.Scope>,
396
+ );
397
+ }
398
+
399
+ return Runtime.runEventPromise<
400
+ A,
401
+ E,
402
+ RuntimeContext<ROut>,
403
+ REvent,
404
+ EventLayerError,
405
+ LayerError
406
+ >(this.runtime, effect, eventLayer);
369
407
  }
370
408
 
371
409
  fetch(request: Request): Promise<Response> {
@@ -390,6 +428,7 @@ export function make<
390
428
  Layer.succeed(WorkerContext, fromExecutionContext(ctx)),
391
429
  );
392
430
 
431
+ // SAFETY: requestServices provides the HTTP/request contexts introduced by renderFetchSuccess.
393
432
  return this[RunSymbol](
394
433
  renderFetchSuccess(fetchHandler).pipe(
395
434
  Effect.tap(() => scheduleTelemetryFlush),
@@ -438,7 +477,27 @@ interface FetchCapableInstance {
438
477
  * isolate is evicted; Cloudflare provides no shutdown hook. Use `eventLayer`
439
478
  * for resources that must be finalized per event.
440
479
  */
441
- export const makeFetchHandler = <
480
+ export function makeFetchHandler<
481
+ ROut,
482
+ LayerError,
483
+ REvent,
484
+ EventLayerError = never,
485
+ Env extends WorkerEnv = WorkerEnv,
486
+ >(
487
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
488
+ options: FetchWorkerOptionsWithEventLayer<ROut, REvent, EventLayerError>,
489
+ ): FetchHandler<Env>;
490
+ export function makeFetchHandler<
491
+ ROut,
492
+ LayerError,
493
+ REvent extends never = never,
494
+ EventLayerError = never,
495
+ Env extends WorkerEnv = WorkerEnv,
496
+ >(
497
+ layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
498
+ options: FetchWorkerOptions<ROut, REvent, EventLayerError>,
499
+ ): FetchHandler<Env>;
500
+ export function makeFetchHandler<
442
501
  ROut,
443
502
  LayerError,
444
503
  REvent = never,
@@ -447,8 +506,19 @@ export const makeFetchHandler = <
447
506
  >(
448
507
  layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>,
449
508
  options: FetchWorkerOptions<ROut, REvent, EventLayerError>,
450
- ): FetchHandler<Env> => {
451
- const WorkerClass = make(layer, options);
509
+ ): FetchHandler<Env> {
510
+ const WorkerClass =
511
+ options.eventLayer === undefined
512
+ ? make(
513
+ layer,
514
+ // SAFETY: the no-event public overload is the only one that permits an absent eventLayer.
515
+ options as FetchWorkerOptions<ROut, never, EventLayerError>,
516
+ )
517
+ : make(
518
+ layer,
519
+ // SAFETY: this branch has the eventLayer required by the event-dependent public overload.
520
+ options as FetchWorkerOptionsWithEventLayer<ROut, REvent, EventLayerError>,
521
+ );
452
522
  const instances = new WeakMap<WorkerEnv, FetchCapableInstance>();
453
523
 
454
524
  return {
@@ -456,14 +526,14 @@ export const makeFetchHandler = <
456
526
  let instance = instances.get(env);
457
527
 
458
528
  if (instance === undefined) {
459
- instance = new WorkerClass(ctx, env) as unknown as FetchCapableInstance;
529
+ instance = new WorkerClass(ctx, env);
460
530
  instances.set(env, instance);
461
531
  }
462
532
 
463
533
  return instance[FetchSymbol](request, ctx);
464
534
  },
465
535
  };
466
- };
536
+ }
467
537
 
468
538
  export type {
469
539
  Api,
@@ -481,3 +551,6 @@ export type {
481
551
  } from "./WorkerDefinition";
482
552
 
483
553
  export { implement, method, Tag } from "./WorkerDefinition";
554
+
555
+ /** @deprecated Use {@link WorkerRpcContract}. */
556
+ export { type WorkerRpcContract as "WorkerRpcShape" };
@@ -48,7 +48,7 @@ export namespace Method {
48
48
 
49
49
  export type Success<Self extends Any> = S.Schema.Type<Self["success"]>;
50
50
 
51
- export type EncodedSuccess<Self extends Any> = S.Json;
51
+ export type EncodedSuccess<_Self extends Any> = S.Json;
52
52
  }
53
53
 
54
54
  export type Methods = Record<string, Method.Any>;
@@ -59,9 +59,12 @@ export type Methods = Record<string, Method.Any>;
59
59
  * Create with {@link make} and reuse to type both worker implementations and
60
60
  * service bindings in other workers.
61
61
  */
62
- export interface Definition<Id extends string = string, MethodsShape extends Methods = Methods> {
62
+ export interface Definition<
63
+ Id extends string = string,
64
+ MethodDefinitions extends Methods = Methods,
65
+ > {
63
66
  readonly id: Id;
64
- readonly methods: MethodsShape;
67
+ readonly methods: MethodDefinitions;
65
68
  }
66
69
 
67
70
  export namespace Definition {
@@ -70,8 +73,8 @@ export namespace Definition {
70
73
 
71
74
  export type ReservedMethodName = WorkerEntrypoint.ReservedMethodName;
72
75
 
73
- export type NoReservedMethods<MethodsShape extends Methods> =
74
- Extract<keyof MethodsShape, ReservedMethodName> extends never ? MethodsShape : never;
76
+ export type NoReservedMethods<MethodDefinitions extends Methods> =
77
+ Extract<keyof MethodDefinitions, ReservedMethodName> extends never ? MethodDefinitions : never;
75
78
 
76
79
  const reservedMethodNames = new Set<string>([
77
80
  "constructor",
@@ -116,51 +119,89 @@ type BoundaryHandlers<ROut, Self extends Definition.Any> = {
116
119
  ) => WorkerRpcHandler<ROut, Method.EncodedSuccess<Self["methods"][Key]>>;
117
120
  };
118
121
 
122
+ type MutableBoundaryHandlers<ROut, Self extends Definition.Any> = {
123
+ -readonly [Key in keyof Self["methods"]]: (
124
+ ...args: Array<unknown>
125
+ ) => WorkerRpcHandler<ROut, Method.EncodedSuccess<Self["methods"][Key]>>;
126
+ };
127
+
128
+ type BaseOptions<ROut, Self extends Definition.Any, REvent, EventLayerError> = Omit<
129
+ WorkerEntrypoint.WorkerOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>,
130
+ "eventLayer" | "rpc"
131
+ > & {
132
+ readonly rpc: Handlers<ROut | REvent, Self>;
133
+ };
134
+
135
+ type EventLayerOptions<ROut, REvent, EventLayerError> = [REvent] extends [never]
136
+ ? Pick<WorkerEntrypoint.WorkerOptions<ROut, never, EventLayerError>, "eventLayer">
137
+ : {
138
+ readonly eventLayer: NonNullable<
139
+ WorkerEntrypoint.WorkerOptions<ROut, REvent, EventLayerError>["eventLayer"]
140
+ >;
141
+ };
142
+
119
143
  /**
120
144
  * Worker constructor options for a specific RPC definition.
121
145
  */
122
- export interface Options<
146
+ export type Options<
123
147
  ROut,
124
148
  Self extends Definition.Any,
125
149
  REvent = never,
126
150
  EventLayerError = never,
127
- > extends Omit<
128
- WorkerEntrypoint.WorkerOptions<ROut, REvent, EventLayerError, Handlers<ROut | REvent, Self>>,
129
- "rpc"
130
- > {
131
- readonly rpc: Handlers<ROut | REvent, Self>;
132
- }
151
+ > = BaseOptions<ROut, Self, REvent, EventLayerError> &
152
+ EventLayerOptions<ROut, REvent, EventLayerError>;
133
153
 
134
154
  export type LayerOptions = {
135
155
  readonly binding: string;
136
156
  };
137
157
 
138
- export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Context.ServiceClass<
158
+ export type TagClass<
159
+ Self,
160
+ Id extends string,
161
+ MethodDefinitions extends Methods,
162
+ > = Context.ServiceClass<
139
163
  Self,
140
164
  Id,
141
165
  ServiceBinding.ServiceBindingEffectClient<
142
- Api<Definition<Id, MethodsShape>>,
143
- Definition<Id, MethodsShape>
166
+ Api<Definition<Id, MethodDefinitions>>,
167
+ Definition<Id, MethodDefinitions>
144
168
  >
145
169
  > &
146
170
  ServiceBinding.ServiceBindingStaticClient<
147
171
  Self,
148
- Api<Definition<Id, MethodsShape>>,
149
- Definition<Id, MethodsShape>
172
+ Api<Definition<Id, MethodDefinitions>>,
173
+ Definition<Id, MethodDefinitions>
150
174
  > & {
151
175
  readonly id: Id;
152
- readonly methods: MethodsShape;
153
- readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
154
- layer: Layer.Layer<
155
- ROut,
156
- LayerError,
157
- WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
158
- >,
159
- options: Options<ROut, Definition<Id, MethodsShape>, REvent, EventLayerError>,
160
- ) => WorkerEntrypoint.WorkerClass<
161
- Handlers<ROut | REvent, Definition<Id, MethodsShape>>,
162
- ROut | REvent
163
- >;
176
+ readonly methods: MethodDefinitions;
177
+ readonly make: {
178
+ <ROut, LayerError, REvent, EventLayerError = never>(
179
+ layer: Layer.Layer<
180
+ ROut,
181
+ LayerError,
182
+ WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
183
+ >,
184
+ options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError> & {
185
+ readonly eventLayer: NonNullable<
186
+ WorkerEntrypoint.WorkerOptions<ROut, REvent, EventLayerError>["eventLayer"]
187
+ >;
188
+ },
189
+ ): WorkerEntrypoint.WorkerClass<
190
+ Handlers<ROut | REvent, Definition<Id, MethodDefinitions>>,
191
+ ROut | REvent
192
+ >;
193
+ <ROut, LayerError, REvent extends never = never, EventLayerError = never>(
194
+ layer: Layer.Layer<
195
+ ROut,
196
+ LayerError,
197
+ WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
198
+ >,
199
+ options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError>,
200
+ ): WorkerEntrypoint.WorkerClass<
201
+ Handlers<ROut | REvent, Definition<Id, MethodDefinitions>>,
202
+ ROut | REvent
203
+ >;
204
+ };
164
205
  readonly layer: (
165
206
  options: LayerOptions,
166
207
  ) => Layer.Layer<
@@ -170,10 +211,19 @@ export type TagClass<Self, Id extends string, MethodsShape extends Methods> = Co
170
211
  >;
171
212
  };
172
213
 
214
+ type TagClassValue = S.Schema.Type<typeof S.Unknown>;
215
+
216
+ const assumeTagClass = <Self, Id extends string, MethodDefinitions extends Methods>(
217
+ value: TagClassValue,
218
+ ): TagClass<Self, Id, MethodDefinitions> => {
219
+ // SAFETY: callers supply a Context service tag with every definition-derived static member attached.
220
+ return value as TagClass<Self, Id, MethodDefinitions>;
221
+ };
222
+
173
223
  /**
174
224
  * Defines a single RPC method schema in a worker definition.
175
225
  */
176
- export const method = RpcDefinition.method as {
226
+ export const method: {
177
227
  <Success extends ServiceFreeSchema>(definition: {
178
228
  readonly success: Success;
179
229
  }): Method<readonly [], Success>;
@@ -184,7 +234,7 @@ export const method = RpcDefinition.method as {
184
234
  readonly args: Args;
185
235
  readonly success: Success;
186
236
  }): Method<Args, Success>;
187
- };
237
+ } = RpcDefinition.method;
188
238
 
189
239
  /**
190
240
  * Creates a typed worker RPC definition plus helpers for implementation and bindings.
@@ -199,11 +249,11 @@ export const method = RpcDefinition.method as {
199
249
  * });
200
250
  * ```
201
251
  */
202
- const makeDefinition = <Id extends string, const MethodsShape extends Methods>(
252
+ const makeDefinition = <Id extends string, const MethodDefinitions extends Methods>(
203
253
  id: Id,
204
- methods: MethodsShape & NoReservedMethods<MethodsShape>,
254
+ methods: MethodDefinitions & NoReservedMethods<MethodDefinitions>,
205
255
  ) => {
206
- type SelfDefinition = Definition<Id, MethodsShape>;
256
+ type SelfDefinition = Definition<Id, MethodDefinitions>;
207
257
  RpcDefinition.assertNoReservedMethods("Worker", methods, reservedMethodNames);
208
258
  const definition: SelfDefinition = RpcDefinition.make(id, methods);
209
259
 
@@ -215,32 +265,55 @@ const makeDefinition = <Id extends string, const MethodsShape extends Methods>(
215
265
  WorkerEntrypoint.ExecutionContext | WorkerEntrypoint.WorkerContext | WorkerEnvironment
216
266
  >,
217
267
  options: Options<ROut, SelfDefinition, REvent, EventLayerError>,
218
- ) =>
219
- WorkerEntrypoint.make(layer, {
268
+ ) => {
269
+ type WrappedRpc = BoundaryHandlers<ROut | REvent, SelfDefinition>;
270
+ type WrappedOptions = WorkerEntrypoint.WorkerOptions<
271
+ ROut,
272
+ REvent,
273
+ EventLayerError,
274
+ WrappedRpc
275
+ >;
276
+ type WrappedOptionsWithEventLayer = Omit<WrappedOptions, "eventLayer"> & {
277
+ readonly eventLayer: NonNullable<WrappedOptions["eventLayer"]>;
278
+ };
279
+ const workerOptions = {
220
280
  ...options,
221
281
  rpc: wrapHandlers(definition, options.rpc),
222
- }),
282
+ };
283
+
284
+ if (workerOptions.eventLayer === undefined) {
285
+ // SAFETY: Options permits an absent eventLayer only when REvent is never.
286
+ return WorkerEntrypoint.make(
287
+ layer,
288
+ workerOptions as WorkerEntrypoint.WorkerOptions<
289
+ ROut,
290
+ never,
291
+ EventLayerError,
292
+ BoundaryHandlers<ROut, SelfDefinition>
293
+ >,
294
+ );
295
+ }
296
+
297
+ // SAFETY: this branch has the eventLayer required by WorkerEntrypoint.make's event overload.
298
+ return WorkerEntrypoint.make(layer, workerOptions as WrappedOptionsWithEventLayer);
299
+ },
223
300
  });
224
301
  };
225
302
 
226
- export const make = <Id extends string, const MethodsShape extends Methods>(
303
+ export const make = <Id extends string, const MethodDefinitions extends Methods>(
227
304
  id: Id,
228
- methods: MethodsShape & NoReservedMethods<MethodsShape>,
229
- ) =>
230
- Tag<Definition<Id, MethodsShape>>()<Id, MethodsShape>(
231
- id,
232
- methods as MethodsShape & NoReservedMethods<MethodsShape>,
233
- );
305
+ methods: MethodDefinitions & NoReservedMethods<MethodDefinitions>,
306
+ ) => Tag<Definition<Id, MethodDefinitions>>()<Id, MethodDefinitions>(id, methods);
234
307
 
235
308
  export const Tag =
236
309
  <Self>() =>
237
- <Id extends string, const MethodsShape extends Methods>(
310
+ <Id extends string, const MethodDefinitions extends Methods>(
238
311
  id: Id,
239
- methods: MethodsShape & NoReservedMethods<MethodsShape>,
312
+ methods: MethodDefinitions & NoReservedMethods<MethodDefinitions>,
240
313
  ) => {
241
- const definition = makeDefinition<Id, MethodsShape>(id, methods);
314
+ const definition = makeDefinition<Id, MethodDefinitions>(id, methods);
242
315
 
243
- type SelfDefinition = Definition<Id, MethodsShape>;
316
+ type SelfDefinition = Definition<Id, MethodDefinitions>;
244
317
  type ClientApi = Api<SelfDefinition>;
245
318
  const tag = Context.Service<
246
319
  Self,
@@ -264,11 +337,12 @@ export const Tag =
264
337
 
265
338
  const rpc = <Method extends keyof ClientApi>(
266
339
  method: Method,
267
- ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
340
+ ...args: ClientApi[Method] extends (...args: infer Args) => any ? Args : never
268
341
  ) =>
269
342
  Effect.gen(function* () {
270
343
  const service = yield* tag;
271
344
 
345
+ // SAFETY: the public signature above restores the selected ClientApi tuple and result types.
272
346
  return yield* (service.rpc as UnsafeInvoke<ServiceBinding.ServiceBindingRpcError>)(
273
347
  method,
274
348
  ...args,
@@ -277,11 +351,12 @@ export const Tag =
277
351
 
278
352
  const call = <Method extends keyof ClientApi>(
279
353
  method: Method,
280
- ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
354
+ ...args: ClientApi[Method] extends (...args: infer Args) => any ? Args : never
281
355
  ) =>
282
356
  Effect.gen(function* () {
283
357
  const service = yield* tag;
284
358
 
359
+ // SAFETY: the public signature above restores the selected ClientApi tuple and result types.
285
360
  return yield* (service.call as UnsafeInvoke<ServiceBinding.ServiceBindingRpcError>)(
286
361
  method,
287
362
  ...args,
@@ -290,11 +365,12 @@ export const Tag =
290
365
 
291
366
  const scopedCall = <Method extends keyof ClientApi>(
292
367
  method: Method,
293
- ...args: ClientApi[Method] extends (...args: infer Args) => unknown ? Args : never
368
+ ...args: ClientApi[Method] extends (...args: infer Args) => any ? Args : never
294
369
  ) =>
295
370
  Effect.gen(function* () {
296
371
  const service = yield* tag;
297
372
 
373
+ // SAFETY: the public signature above restores the selected ClientApi tuple and result types.
298
374
  return yield* (service.scopedCall as UnsafeInvoke<ServiceBinding.ServiceBindingRpcError>)(
299
375
  method,
300
376
  ...args,
@@ -303,19 +379,22 @@ export const Tag =
303
379
 
304
380
  const directMethods = ServiceBinding.makeDirectMethods<Self, ClientApi, SelfDefinition>(
305
381
  definition,
382
+ // SAFETY: call's public generic signature is derived from this same ClientApi definition.
306
383
  call as never,
307
384
  );
308
385
 
309
- return Object.assign(tag, directMethods, {
310
- id: definition.id,
311
- methods: definition.methods,
312
- make: definition.make,
313
- layer,
314
- fetch,
315
- rpc,
316
- call,
317
- scopedCall,
318
- }) as unknown as TagClass<Self, Id, MethodsShape>;
386
+ return assumeTagClass<Self, Id, MethodDefinitions>(
387
+ Object.assign(tag, directMethods, {
388
+ id: definition.id,
389
+ methods: definition.methods,
390
+ make: definition.make,
391
+ layer,
392
+ fetch,
393
+ rpc,
394
+ call,
395
+ scopedCall,
396
+ }),
397
+ );
319
398
  };
320
399
 
321
400
  export const Worker = Tag;
@@ -324,8 +403,9 @@ const wrapHandlers = <ROut, const Self extends Definition.Any>(
324
403
  definition: Self,
325
404
  handlers: Handlers<ROut, Self>,
326
405
  ): BoundaryHandlers<ROut, Self> => {
327
- const wrapped = {} as Record<string, unknown>;
406
+ const wrapped: MutableBoundaryHandlers<ROut, Self> = Object.create(null);
328
407
 
408
+ // SAFETY: definition.methods is the owner of the method-name union used to index both mappings.
329
409
  for (const key of Object.keys(definition.methods) as Array<
330
410
  RpcDefinition.Definition.MethodNames<Self>
331
411
  >) {
@@ -340,7 +420,7 @@ const wrapHandlers = <ROut, const Self extends Definition.Any>(
340
420
  });
341
421
  }
342
422
 
343
- return wrapped as BoundaryHandlers<ROut, Self>;
423
+ return wrapped;
344
424
  };
345
425
 
346
426
  /**