effect-cf 0.39.1 → 0.40.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 (166) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +9 -1
  3. package/dist/AiGateway.d.mts +86 -0
  4. package/dist/AiGateway.mjs +178 -0
  5. package/dist/AnalyticsEngine.d.mts +170 -0
  6. package/dist/AnalyticsEngine.mjs +275 -0
  7. package/dist/{Artifacts-B3r3RuTB.d.mts → Artifacts.d.mts} +8 -5
  8. package/dist/{Artifacts-CBxWGeZB.mjs → Artifacts.mjs} +9 -5
  9. package/dist/Binding.d.mts +45 -0
  10. package/dist/{Binding-C82jRhdr.mjs → Binding.mjs} +8 -4
  11. package/dist/BrowserRendering.d.mts +85 -0
  12. package/dist/BrowserRendering.mjs +81 -0
  13. package/dist/Cache.d.mts +40 -0
  14. package/dist/Cache.mjs +51 -0
  15. package/dist/CloudflareOtlp.d.mts +95 -0
  16. package/dist/CloudflareOtlp.mjs +125 -0
  17. package/dist/CloudflareTracer.d.mts +20 -0
  18. package/dist/CloudflareTracer.mjs +69 -0
  19. package/dist/ComputerArtifacts.d.mts +2 -2
  20. package/dist/ComputerArtifacts.mjs +3 -2
  21. package/dist/ComputerWorkspace.d.mts +36 -31
  22. package/dist/ComputerWorkspace.mjs +8 -3
  23. package/dist/ComputerWorkspaceHost.d.mts +1 -1
  24. package/dist/ComputerWorkspaceHost.mjs +1 -1
  25. package/dist/{ContainerNamespace-DbLaeFhN.d.mts → ContainerNamespace.d.mts} +16 -12
  26. package/dist/ContainerNamespace.mjs +165 -0
  27. package/dist/D1.d.mts +33 -0
  28. package/dist/D1.mjs +32 -0
  29. package/dist/DurableObject.d.mts +103 -0
  30. package/dist/DurableObject.mjs +77 -0
  31. package/dist/DurableObjectAlarm.d.mts +187 -0
  32. package/dist/DurableObjectAlarm.mjs +344 -0
  33. package/dist/DurableObjectDefinition.d.mts +87 -0
  34. package/dist/DurableObjectDefinition.mjs +125 -0
  35. package/dist/DurableObjectNamespace.d.mts +154 -0
  36. package/dist/DurableObjectNamespace.mjs +144 -0
  37. package/dist/DurableObjectRpcWebSocket.d.mts +112 -0
  38. package/dist/DurableObjectRpcWebSocket.mjs +547 -0
  39. package/dist/DurableObjectSqlite.d.mts +11 -0
  40. package/dist/DurableObjectSqlite.mjs +16 -0
  41. package/dist/DurableObjectState.d.mts +65 -0
  42. package/dist/DurableObjectState.mjs +47 -0
  43. package/dist/DurableObjectStorage.d.mts +112 -0
  44. package/dist/{DurableObjectState-DLL0YgBd.mjs → DurableObjectStorage.mjs} +19 -157
  45. package/dist/DurableObjectWebSocket.d.mts +83 -0
  46. package/dist/DurableObjectWebSocket.mjs +120 -0
  47. package/dist/Email.d.mts +106 -0
  48. package/dist/Email.mjs +247 -0
  49. package/dist/Environment.d.mts +59 -0
  50. package/dist/{ErrorMessage-Byx2OgNd.mjs → Environment.mjs} +7 -18
  51. package/dist/{Hyperdrive-HrwFlZ6x.d.mts → Hyperdrive.d.mts} +8 -5
  52. package/dist/Hyperdrive.mjs +32 -0
  53. package/dist/HyperdrivePg.d.mts +4 -3
  54. package/dist/HyperdrivePg.mjs +3 -1
  55. package/dist/Images.d.mts +191 -0
  56. package/dist/Images.mjs +112 -0
  57. package/dist/Kv.d.mts +84 -0
  58. package/dist/Kv.mjs +132 -0
  59. package/dist/Mcp.d.mts +5 -3
  60. package/dist/Mcp.mjs +13 -4
  61. package/dist/Queue.d.mts +68 -0
  62. package/dist/Queue.mjs +54 -0
  63. package/dist/QueueBinding.d.mts +42 -0
  64. package/dist/QueueBinding.mjs +69 -0
  65. package/dist/QueueDefinition.d.mts +48 -0
  66. package/dist/QueueDefinition.mjs +63 -0
  67. package/dist/R2.d.mts +89 -0
  68. package/dist/R2.mjs +117 -0
  69. package/dist/Rpc.d.mts +29 -0
  70. package/dist/Rpc.mjs +28 -0
  71. package/dist/RpcDefinition.d.mts +116 -0
  72. package/dist/RpcDefinition.mjs +184 -0
  73. package/dist/RpcTracing.d.mts +52 -0
  74. package/dist/RpcTracing.mjs +77 -0
  75. package/dist/Sandbox.d.mts +10 -5
  76. package/dist/Sandbox.mjs +9 -3
  77. package/dist/ServiceBinding.d.mts +112 -0
  78. package/dist/ServiceBinding.mjs +145 -0
  79. package/dist/Vectorize.d.mts +68 -0
  80. package/dist/Vectorize.mjs +76 -0
  81. package/dist/Vitest.d.mts +6 -4
  82. package/dist/Vitest.mjs +8 -4
  83. package/dist/WebTransport.d.mts +66 -0
  84. package/dist/WebTransport.mjs +63 -0
  85. package/dist/Worker.d.mts +139 -0
  86. package/dist/Worker.mjs +141 -0
  87. package/dist/WorkerDefinition.d.mts +100 -0
  88. package/dist/WorkerDefinition.mjs +103 -0
  89. package/dist/WorkersAi.d.mts +84 -0
  90. package/dist/WorkersAi.mjs +84 -0
  91. package/dist/Workflow.d.mts +91 -0
  92. package/dist/Workflow.mjs +139 -0
  93. package/dist/{WorkflowBinding-DtVykUCz.d.mts → WorkflowBinding.d.mts} +14 -10
  94. package/dist/WorkflowBinding.mjs +100 -0
  95. package/dist/WorkflowDefinition.d.mts +51 -0
  96. package/dist/WorkflowDefinition.mjs +69 -0
  97. package/dist/{rolldown-runtime-D7D4PA-g.mjs → _virtual/_rolldown/runtime.mjs} +1 -1
  98. package/dist/index.d.mts +40 -1800
  99. package/dist/index.mjs +40 -3123
  100. package/dist/{ComputerWorkspaceHostRegistry-CY5Fv4Ng.mjs → internal/ComputerWorkspaceHostRegistry.mjs} +1 -1
  101. package/dist/internal/Entrypoint.mjs +37 -0
  102. package/dist/internal/ErrorMessage.mjs +17 -0
  103. package/dist/internal/NativeCallback.mjs +33 -0
  104. package/dist/internal/RpcInvocation.d.mts +13 -0
  105. package/dist/internal/RpcInvocation.mjs +34 -0
  106. package/dist/internal/Runtime.mjs +31 -0
  107. package/dist/internal/Telemetry.mjs +20 -0
  108. package/dist/{WorkerContext-Yobnfw-K.mjs → internal/WorkerContext.mjs} +4 -31
  109. package/package.json +1 -1
  110. package/src/AiGateway.ts +5 -1
  111. package/src/AnalyticsEngine.ts +14 -19
  112. package/src/Artifacts.ts +6 -1
  113. package/src/Binding.ts +5 -1
  114. package/src/BrowserRendering.ts +5 -1
  115. package/src/Cache.ts +5 -1
  116. package/src/CloudflareOtlp.ts +12 -10
  117. package/src/CloudflareTracer.ts +7 -1
  118. package/src/ComputerArtifacts.ts +2 -1
  119. package/src/ComputerWorkspace.ts +7 -1
  120. package/src/ContainerNamespace.ts +6 -1
  121. package/src/D1.ts +4 -1
  122. package/src/DurableObject.ts +122 -29
  123. package/src/DurableObjectAlarm.ts +185 -52
  124. package/src/DurableObjectDefinition.ts +22 -22
  125. package/src/DurableObjectNamespace.ts +4 -1
  126. package/src/DurableObjectRpcWebSocket.ts +7 -1
  127. package/src/DurableObjectSqlite.ts +2 -1
  128. package/src/DurableObjectState.ts +4 -1
  129. package/src/DurableObjectStorage.ts +11 -13
  130. package/src/DurableObjectWebSocket.ts +5 -1
  131. package/src/Email.ts +5 -1
  132. package/src/Environment.ts +6 -1
  133. package/src/Hyperdrive.ts +4 -1
  134. package/src/HyperdrivePg.ts +3 -1
  135. package/src/Images.ts +7 -1
  136. package/src/Kv.ts +7 -1
  137. package/src/Mcp.ts +13 -13
  138. package/src/Queue.ts +3 -1
  139. package/src/QueueBinding.ts +5 -1
  140. package/src/QueueDefinition.ts +4 -1
  141. package/src/R2.ts +7 -1
  142. package/src/Rpc.ts +4 -1
  143. package/src/RpcDefinition.ts +4 -1
  144. package/src/RpcTracing.ts +9 -1
  145. package/src/Sandbox.ts +7 -1
  146. package/src/ServiceBinding.ts +6 -1
  147. package/src/Vectorize.ts +7 -1
  148. package/src/Vitest.ts +5 -1
  149. package/src/WebTransport.ts +6 -1
  150. package/src/Worker.ts +10 -16
  151. package/src/WorkerDefinition.ts +3 -1
  152. package/src/WorkersAi.ts +7 -1
  153. package/src/Workflow.ts +8 -11
  154. package/src/WorkflowBinding.ts +6 -1
  155. package/src/WorkflowDefinition.ts +4 -1
  156. package/src/internal/Entrypoint.ts +3 -1
  157. package/src/internal/ErrorMessage.ts +1 -1
  158. package/src/internal/NativeCallback.ts +3 -1
  159. package/src/internal/RpcInvocation.ts +5 -1
  160. package/src/internal/Runtime.ts +5 -1
  161. package/src/internal/Telemetry.ts +4 -3
  162. package/src/internal/WorkerContext.ts +3 -1
  163. package/dist/Binding-DKcLrQCM.d.mts +0 -94
  164. package/dist/DurableObjectState-DJ0K-zyd.d.mts +0 -244
  165. package/dist/Worker-XsGJsCJX.mjs +0 -885
  166. package/dist/Worker-gV4mDqIs.d.mts +0 -641
@@ -1,9 +1,17 @@
1
1
  import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
2
- import { Effect, Layer, type ManagedRuntime, type Scope, Tracer } from "effect";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Tracer from "effect/Tracer";
5
+ import type { ManagedRuntime, Scope } from "effect";
3
6
 
4
7
  import { NativeRequest } from "./Worker";
5
8
  import { WorkerEnvironment, type WorkerEnv } from "./Environment";
6
9
  import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
10
+ import {
11
+ DurableObjectAlarm,
12
+ type AlarmRegistration,
13
+ type AlarmService,
14
+ } from "./DurableObjectAlarm";
7
15
  import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
8
16
  import * as RpcDefinition from "./RpcDefinition";
9
17
  import * as Entrypoint from "./internal/Entrypoint";
@@ -13,7 +21,11 @@ import type { ReceiverOptions, RpcInvocationInfo } from "./RpcTracing";
13
21
 
14
22
  const reservedMethodNames: ReadonlySet<string> = RpcDefinition.reservedMethodNames;
15
23
 
16
- type RuntimeContext<ROut> = DurableObjectState | WorkerEnvironment | ROut;
24
+ export type RuntimeContext<ROut> =
25
+ | DurableObjectState
26
+ | DurableObjectAlarm
27
+ | WorkerEnvironment
28
+ | ROut;
17
29
 
18
30
  type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
19
31
 
@@ -80,11 +92,12 @@ export type RpcHandlers<ROut, Api> = {
80
92
  : never;
81
93
  };
82
94
 
83
- export interface DurableObjectOptions<
95
+ interface DurableObjectOptionsBase<
84
96
  RRuntime,
85
97
  REvent = never,
86
98
  EventLayerError = never,
87
- Rpc extends DurableObjectRpc<RRuntime | REvent> = Record<never, never>,
99
+ Rpc extends DurableObjectRpc<RRuntime | REvent | RAlarm> = Record<never, never>,
100
+ RAlarm = never,
88
101
  > {
89
102
  /**
90
103
  * Layer provided around each Cloudflare event handled by this Durable Object.
@@ -96,7 +109,7 @@ export interface DurableObjectOptions<
96
109
  readonly eventLayer?: Layer.Layer<
97
110
  REvent,
98
111
  EventLayerError,
99
- DurableObjectState | WorkerEnvironment | RRuntime
112
+ RuntimeContext<NoInfer<RRuntime | RAlarm>>
100
113
  >;
101
114
  /**
102
115
  * Effect run when Cloudflare loads this Durable Object instance into memory.
@@ -106,7 +119,7 @@ export interface DurableObjectOptions<
106
119
  * the same Durable Object id again after eviction or restart; use Durable
107
120
  * Object storage if work must happen only once per id.
108
121
  */
109
- readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime>>;
122
+ readonly initialize?: Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime | RAlarm>>>;
110
123
  /**
111
124
  * Optional RPC methods exposed as Durable Object instance methods.
112
125
  *
@@ -118,9 +131,20 @@ export interface DurableObjectOptions<
118
131
  readonly rpc?: Rpc;
119
132
  /** Accept live native trace metadata. The client must also enable rpcTracing. */
120
133
  readonly rpcTracing?: ReceiverOptions;
121
- readonly fetch?: Effect.Effect<Response, unknown, FetchContext<RRuntime | REvent>>;
134
+ readonly fetch?: Effect.Effect<
135
+ Response,
136
+ unknown,
137
+ FetchContext<NoInfer<RRuntime | REvent | RAlarm>>
138
+ >;
122
139
 
123
- readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<RRuntime | REvent>>;
140
+ /** Register handlers to provide their typed scheduler to application layers and all DO events. */
141
+ readonly alarms?:
142
+ | Effect.Effect<unknown, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>
143
+ | AlarmRegistration<
144
+ RAlarm,
145
+ HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>,
146
+ unknown
147
+ >;
124
148
  /**
125
149
  * Optional raw alarm handler.
126
150
  *
@@ -131,23 +155,58 @@ export interface DurableObjectOptions<
131
155
  */
132
156
  readonly alarm?: (
133
157
  alarmInfo?: globalThis.AlarmInvocationInfo,
134
- ) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
158
+ ) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
135
159
  readonly webSocketMessage?: (
136
160
  socket: DurableWebSocket,
137
161
  message: string | ArrayBuffer,
138
- ) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
162
+ ) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
139
163
  readonly webSocketClose?: (
140
164
  socket: DurableWebSocket,
141
165
  code: number,
142
166
  reason: string,
143
167
  wasClean: boolean,
144
- ) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
168
+ ) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
145
169
  readonly webSocketError?: (
146
170
  socket: DurableWebSocket,
147
171
  cause: unknown,
148
- ) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent>>;
172
+ ) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
149
173
  }
150
174
 
175
+ // Providing a scheduler through an ordinary layer does not install its dispatcher.
176
+ type CheckAlarmRegistration<RProvided, RAlarm> = [
177
+ Exclude<Extract<NoInfer<RProvided>, AlarmService>, NoInfer<RAlarm>>,
178
+ ] extends [never]
179
+ ? unknown
180
+ : never;
181
+
182
+ /** A declared event or alarm service must have its corresponding provider. */
183
+ export type DurableObjectOptions<
184
+ RRuntime,
185
+ REvent = never,
186
+ EventLayerError = never,
187
+ Rpc extends DurableObjectRpc<RRuntime | REvent | RAlarm> = Record<never, never>,
188
+ RAlarm = never,
189
+ > = DurableObjectOptionsBase<RRuntime, REvent, EventLayerError, Rpc, RAlarm> &
190
+ CheckAlarmRegistration<RRuntime | REvent, RAlarm> &
191
+ ([Exclude<REvent, RuntimeContext<RRuntime | RAlarm> | Scope.Scope>] extends [never]
192
+ ? unknown
193
+ : {
194
+ readonly eventLayer: Layer.Layer<
195
+ REvent,
196
+ EventLayerError,
197
+ RuntimeContext<NoInfer<RRuntime | RAlarm>>
198
+ >;
199
+ }) &
200
+ ([RAlarm] extends [never]
201
+ ? unknown
202
+ : {
203
+ readonly alarms: AlarmRegistration<
204
+ RAlarm,
205
+ HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>,
206
+ unknown
207
+ >;
208
+ });
209
+
151
210
  /**
152
211
  * Cloudflare `DurableObject` constructor produced by {@link make}.
153
212
  */
@@ -168,27 +227,62 @@ export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
168
227
  * that layer are not guaranteed to run. Put resources that require timely
169
228
  * release in `eventLayer`, whose scope closes after each handled event.
170
229
  */
171
- export const make = <
230
+ export function make<ROut, LayerError>(
231
+ layer: Layer.Layer<ROut, LayerError, RuntimeContext<never>> & CheckAlarmRegistration<ROut, never>,
232
+ ): DurableObjectClass<Record<never, never>, ROut>;
233
+ export function make<
234
+ ROut,
235
+ LayerError,
236
+ REvent = never,
237
+ EventLayerError = never,
238
+ const Rpc extends DurableObjectRpc<NoInfer<ROut> | REvent | NoInfer<RAlarm>> = Record<
239
+ never,
240
+ never
241
+ >,
242
+ RAlarm = never,
243
+ >(
244
+ layer: Layer.Layer<ROut, LayerError, RuntimeContext<NoInfer<RAlarm>>>,
245
+ options: DurableObjectOptions<ROut, REvent, EventLayerError, Rpc, RAlarm>,
246
+ ): DurableObjectClass<Rpc, ROut | REvent | RAlarm>;
247
+ export function make<
172
248
  ROut,
173
249
  LayerError,
174
250
  REvent = never,
175
251
  EventLayerError = never,
176
- const Rpc extends DurableObjectRpc<ROut | REvent> = Record<never, never>,
252
+ const Rpc extends DurableObjectRpc<NoInfer<ROut> | REvent | NoInfer<RAlarm>> = Record<
253
+ never,
254
+ never
255
+ >,
256
+ RAlarm = never,
177
257
  >(
178
- layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
179
- options: DurableObjectOptions<ROut, REvent, EventLayerError, Rpc> = {},
180
- ): DurableObjectClass<Rpc, ROut | REvent> => {
258
+ layer: Layer.Layer<ROut, LayerError, RuntimeContext<NoInfer<RAlarm>>>,
259
+ options: DurableObjectOptionsBase<ROut, REvent, EventLayerError, Rpc, RAlarm> = {},
260
+ ): DurableObjectClass<Rpc, ROut | REvent | RAlarm> {
261
+ const registration =
262
+ options.alarms !== undefined && !Effect.isEffect(options.alarms) ? options.alarms : undefined;
263
+ const logicalAlarms = (
264
+ registration?.run ?? (Effect.isEffect(options.alarms) ? options.alarms : undefined)
265
+ )?.pipe(Effect.asVoid);
266
+
181
267
  class EffectDurableObject extends CloudflareDurableObject<WorkerEnv> {
182
- readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
268
+ readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut | RAlarm>, LayerError>;
183
269
 
184
270
  constructor(state: globalThis.DurableObjectState, env: WorkerEnv) {
185
271
  super(state, env);
186
272
 
187
- this.runtime = Runtime.makeEntrypointRuntime<ROut, LayerError, DurableObjectState>(
188
- layer,
189
- env,
190
- Layer.succeed(DurableObjectState, fromDurableObjectState(state)),
273
+ const baseServices = DurableObjectAlarm.layer.pipe(
274
+ Layer.provideMerge(Layer.succeed(DurableObjectState, fromDurableObjectState(state))),
191
275
  );
276
+ // SAFETY: RAlarm is inferred only from the registration's layer; without a registration it is never.
277
+ const services = (registration?.layer ?? Layer.empty).pipe(
278
+ Layer.provideMerge(baseServices),
279
+ ) as Layer.Layer<DurableObjectState | DurableObjectAlarm | RAlarm>;
280
+
281
+ this.runtime = Runtime.makeEntrypointRuntime<
282
+ ROut,
283
+ LayerError,
284
+ DurableObjectState | DurableObjectAlarm | RAlarm
285
+ >(layer, env, services);
192
286
 
193
287
  const initialize = options.initialize;
194
288
 
@@ -198,7 +292,7 @@ export const make = <
198
292
  }
199
293
 
200
294
  [RunSymbol]<A, E>(
201
- effect: Effect.Effect<A, E, HandlerContext<ROut | REvent>>,
295
+ effect: Effect.Effect<A, E, HandlerContext<ROut | REvent | RAlarm>>,
202
296
  runOptions: RunOptions = {},
203
297
  ): Promise<A> {
204
298
  const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
@@ -206,11 +300,11 @@ export const make = <
206
300
  const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
207
301
 
208
302
  if (eventLayer === undefined) {
209
- // SAFETY: event-layer bypass is used only for initialize; otherwise an absent layer means REvent is never.
303
+ // SAFETY: initialize cannot require event services; without an event layer, all requirements are already provided.
210
304
  return Runtime.runEventPromise(
211
305
  this.runtime,
212
306
  // @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
213
- effect as Effect.Effect<A, E, HandlerContext<ROut>>,
307
+ effect as Effect.Effect<A, E, HandlerContext<ROut | RAlarm>>,
214
308
  undefined,
215
309
  parentSpan,
216
310
  );
@@ -219,7 +313,7 @@ export const make = <
219
313
  return Runtime.runEventPromise<
220
314
  A,
221
315
  E,
222
- RuntimeContext<ROut>,
316
+ RuntimeContext<ROut | RAlarm>,
223
317
  REvent,
224
318
  EventLayerError,
225
319
  LayerError
@@ -239,7 +333,6 @@ export const make = <
239
333
  }
240
334
 
241
335
  alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void {
242
- const logicalAlarms = options.alarms?.pipe(Effect.asVoid);
243
336
  const rawAlarm = options.alarm?.(alarmInfo);
244
337
  const alarmEffect =
245
338
  logicalAlarms !== undefined && rawAlarm !== undefined
@@ -297,10 +390,10 @@ export const make = <
297
390
  options.rpcTracing,
298
391
  );
299
392
 
300
- return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
393
+ return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent | RAlarm>>(
301
394
  EffectDurableObject,
302
395
  );
303
- };
396
+ }
304
397
 
305
398
  export type {
306
399
  Api,
@@ -1,15 +1,13 @@
1
- import {
2
- Clock,
3
- Context,
4
- Data,
5
- DateTime,
6
- Duration,
7
- Effect,
8
- Exit,
9
- Layer,
10
- Predicate,
11
- Schema as S,
12
- } from "effect";
1
+ import * as Clock from "effect/Clock";
2
+ import * as Context from "effect/Context";
3
+ import * as Data from "effect/Data";
4
+ import * as DateTime from "effect/DateTime";
5
+ import * as Duration from "effect/Duration";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Exit from "effect/Exit";
8
+ import * as Layer from "effect/Layer";
9
+ import * as Predicate from "effect/Predicate";
10
+ import * as S from "effect/Schema";
13
11
 
14
12
  import { DurableObjectState } from "./DurableObjectState";
15
13
  import { type SqlStorageValue, StorageOperationError } from "./DurableObjectStorage";
@@ -368,6 +366,63 @@ export type DefinedAlarmHandlers<Definitions extends AlarmDefinitions, R = never
368
366
  ) => Effect.Effect<void, E, R>;
369
367
  };
370
368
 
369
+ /** A discriminated union keeps each tag paired with its decoded payload type. */
370
+ export type DefinedScheduleAlarmInput<Definitions extends AlarmDefinitions> = {
371
+ readonly [Tag in keyof Definitions & string]: Omit<ScheduleAlarmInput<Tag>, "payload"> & {
372
+ readonly payload: AlarmDefinitionPayload<Definitions[Tag]>;
373
+ };
374
+ }[keyof Definitions & string];
375
+
376
+ export interface DefinedAlarmTransaction<Definitions extends AlarmDefinitions> {
377
+ readonly scheduleAlarm: (
378
+ input: DefinedScheduleAlarmInput<Definitions>,
379
+ ) => ReturnType<AlarmScheduler["scheduleAlarm"]>;
380
+ readonly cancelAlarm: (
381
+ input: AlarmRef<keyof Definitions & string>,
382
+ ) => ReturnType<AlarmScheduler["cancelAlarm"]>;
383
+ }
384
+
385
+ const TypedAlarmSchedulerTypeId: unique symbol = Symbol.for(
386
+ "effect-cf/DurableObjectAlarm/TypedScheduler",
387
+ );
388
+
389
+ /** Identifies service requirements whose scheduler needs a registered dispatcher. */
390
+ export interface AlarmService {
391
+ readonly Service: {
392
+ readonly [TypedAlarmSchedulerTypeId]: typeof TypedAlarmSchedulerTypeId;
393
+ };
394
+ }
395
+
396
+ export interface DefinedAlarmScheduler<
397
+ Definitions extends AlarmDefinitions,
398
+ > extends DefinedAlarmTransaction<Definitions> {
399
+ readonly [TypedAlarmSchedulerTypeId]: typeof TypedAlarmSchedulerTypeId;
400
+ readonly transaction: <A, E, R>(
401
+ closure: (alarms: DefinedAlarmTransaction<Definitions>) => Effect.Effect<A, E, R>,
402
+ ) => Effect.Effect<A, E | StorageOperationError, R>;
403
+ }
404
+
405
+ /** Registers a dispatcher and the service authorized to schedule its alarms. */
406
+ export interface AlarmRegistration<Self, R = never, E = never> {
407
+ readonly layer: Layer.Layer<Self, never, DurableObjectAlarm>;
408
+ readonly run: Effect.Effect<
409
+ ProcessDueAlarmsResult,
410
+ E | DurableObjectAlarmError,
411
+ R | DurableObjectAlarm
412
+ >;
413
+ }
414
+
415
+ export interface AlarmTagClass<
416
+ Self,
417
+ Id extends string,
418
+ Definitions extends AlarmDefinitions,
419
+ > extends Context.ServiceClass<Self, Id, DefinedAlarmScheduler<Definitions>> {
420
+ readonly handlers: <R = never, E = never>(
421
+ handlers: DefinedAlarmHandlers<Definitions, R, E>,
422
+ options?: ProcessDueAlarmsOptions<R, E>,
423
+ ) => AlarmRegistration<Self, R, E>;
424
+ }
425
+
371
426
  const isAlarmDefinitionConfig = (
372
427
  definition: AlarmDefinitionEntry,
373
428
  ): definition is AlarmDefinitionConfig => Predicate.isObject(definition) && "payload" in definition;
@@ -391,55 +446,133 @@ const getAlarmDefinitionFailureAction = (
391
446
  : { mode: definition.failure };
392
447
  };
393
448
 
394
- export const define = <const Definitions extends AlarmDefinitions>(definitions: Definitions) => ({
395
- handlers: <R = never, E = never>(
396
- handlers: DefinedAlarmHandlers<Definitions, R, E>,
397
- options?: ProcessDueAlarmsOptions<R, E>,
398
- ) =>
399
- processDue(
400
- (event) =>
401
- Effect.gen(function* () {
402
- const definition = definitions[event.tag];
449
+ const makeDefinition = <const Definitions extends AlarmDefinitions>(definitions: Definitions) => {
450
+ const definitionFor = (tag: string) =>
451
+ Object.hasOwn(definitions, tag) ? definitions[tag] : undefined;
403
452
 
404
- if (definition === undefined) {
405
- return;
406
- }
453
+ const bind = (mutations: AlarmTransaction): DefinedAlarmTransaction<Definitions> => ({
454
+ scheduleAlarm: Effect.fn("DefinedAlarms.scheduleAlarm")(function* (
455
+ input: DefinedScheduleAlarmInput<Definitions>,
456
+ ) {
457
+ const definition = definitionFor(input.tag);
407
458
 
408
- const schema = getAlarmDefinitionSchema(definition);
409
- // SAFETY: schema is selected from the same tagged definition used to select its handler.
410
- const payload = yield* (
411
- S.decodeUnknownEffect(schema)(event.payload) as Effect.Effect<
412
- AlarmDefinitionPayload<Definitions[keyof Definitions & string]>,
413
- unknown
414
- >
415
- ).pipe(
416
- Effect.mapError(
417
- (cause) =>
459
+ if (definition === undefined) {
460
+ return yield* Effect.fail(
461
+ new InvalidAlarmRefError({
462
+ cause: new Error(`Unknown alarm tag "${input.tag}"`),
463
+ }),
464
+ );
465
+ }
466
+ const payload = yield* S.encodeEffect(getAlarmDefinitionSchema(definition))(
467
+ input.payload,
468
+ ).pipe(
469
+ Effect.flatMap(S.decodeUnknownEffect(S.Json)),
470
+ Effect.mapError((cause) => new InvalidAlarmPayloadError({ cause })),
471
+ );
472
+
473
+ yield* mutations.scheduleAlarm({ ...input, payload });
474
+ }),
475
+ cancelAlarm: Effect.fn("DefinedAlarms.cancelAlarm")(function* (
476
+ input: AlarmRef<keyof Definitions & string>,
477
+ ) {
478
+ if (definitionFor(input.tag) === undefined) {
479
+ return yield* Effect.fail(
480
+ new InvalidAlarmRefError({
481
+ cause: new Error(`Unknown alarm tag "${input.tag}"`),
482
+ }),
483
+ );
484
+ }
485
+
486
+ yield* mutations.cancelAlarm(input);
487
+ }),
488
+ });
489
+
490
+ return {
491
+ make: (alarms: AlarmScheduler): DefinedAlarmScheduler<Definitions> => ({
492
+ [TypedAlarmSchedulerTypeId]: TypedAlarmSchedulerTypeId,
493
+ ...bind(alarms),
494
+ transaction: (closure) => alarms.transaction((tx) => closure(bind(tx))),
495
+ }),
496
+ handlers: <R = never, E = never>(
497
+ handlers: DefinedAlarmHandlers<Definitions, R, E>,
498
+ options?: ProcessDueAlarmsOptions<R, E>,
499
+ ) =>
500
+ processDue(
501
+ (event) =>
502
+ Effect.gen(function* () {
503
+ const definition = definitionFor(event.tag);
504
+
505
+ if (definition === undefined) {
506
+ return yield* Effect.fail(
418
507
  new StoredAlarmDecodeError({
419
- cause,
508
+ cause: new Error(`Unknown alarm tag "${event.tag}"`),
420
509
  storageId: getScheduledEventId(event),
421
510
  }),
422
- ),
423
- );
424
- const handler = handlers[event.tag];
511
+ );
512
+ }
425
513
 
426
- // SAFETY: event.tag indexes the matching definition and handler, whose payload schema was decoded above.
427
- yield* handler({ ...event, payload } as never);
428
- }),
429
- {
430
- ...options,
431
- onFailure: (failure) =>
432
- Effect.gen(function* () {
433
- const action = getAlarmDefinitionFailureAction(definitions[failure.tag]);
434
- const optionAction =
435
- options?.onFailure === undefined ? undefined : yield* options.onFailure(failure);
514
+ const schema = getAlarmDefinitionSchema(definition);
515
+ // SAFETY: schema is selected from the same tagged definition used to select its handler.
516
+ const payload = yield* (
517
+ S.decodeUnknownEffect(schema)(event.payload) as Effect.Effect<
518
+ AlarmDefinitionPayload<Definitions[keyof Definitions & string]>,
519
+ unknown
520
+ >
521
+ ).pipe(
522
+ Effect.mapError(
523
+ (cause) =>
524
+ new StoredAlarmDecodeError({
525
+ cause,
526
+ storageId: getScheduledEventId(event),
527
+ }),
528
+ ),
529
+ );
530
+ const handler = handlers[event.tag];
436
531
 
437
- return action ?? optionAction;
532
+ // SAFETY: event.tag indexes the matching definition and handler, whose payload schema was decoded above.
533
+ yield* handler({ ...event, payload } as never);
438
534
  }),
439
- },
440
- ),
535
+ {
536
+ ...options,
537
+ onFailure: (failure) =>
538
+ Effect.gen(function* () {
539
+ const action = getAlarmDefinitionFailureAction(definitionFor(failure.tag));
540
+ const optionAction =
541
+ options?.onFailure === undefined ? undefined : yield* options.onFailure(failure);
542
+
543
+ return action ?? optionAction;
544
+ }),
545
+ },
546
+ ),
547
+ };
548
+ };
549
+
550
+ /** Handler-only definitions for applications using the raw scheduler. Prefer Tag for typed scheduling. */
551
+ export const define = <const Definitions extends AlarmDefinitions>(definitions: Definitions) => ({
552
+ handlers: makeDefinition(definitions).handlers,
441
553
  });
442
554
 
555
+ /** The typed scheduler becomes available when its handlers are registered on a Durable Object. */
556
+ export const Tag =
557
+ <Self>() =>
558
+ <const Id extends string, const Definitions extends AlarmDefinitions>(
559
+ id: Id,
560
+ definitions: Definitions,
561
+ ): AlarmTagClass<Self, Id, Definitions> => {
562
+ const tag = Context.Service<Self, DefinedAlarmScheduler<Definitions>>()(id);
563
+ const definition = makeDefinition(definitions);
564
+
565
+ return Object.assign(tag, {
566
+ handlers: <R = never, E = never>(
567
+ handlers: DefinedAlarmHandlers<Definitions, R, E>,
568
+ options?: ProcessDueAlarmsOptions<R, E>,
569
+ ): AlarmRegistration<Self, R, E> => ({
570
+ layer: Layer.effect(tag, Effect.map(DurableObjectAlarm, definition.make)),
571
+ run: definition.handlers(handlers, options),
572
+ }),
573
+ });
574
+ };
575
+
443
576
  export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, AlarmScheduler>()(
444
577
  "effect-cf/DurableObjectAlarm",
445
578
  ) {
@@ -723,6 +856,6 @@ export class DurableObjectAlarm extends Context.Service<DurableObjectAlarm, Alar
723
856
  scheduleAlarm: (input) => transaction((alarms) => alarms.scheduleAlarm(input)),
724
857
  transaction,
725
858
  });
726
- }).pipe(Effect.withSpan("DurableObjectAlarm.layer")),
859
+ }),
727
860
  );
728
861
  }
@@ -1,10 +1,11 @@
1
- import { Context, Effect, type Layer, type Schema as S } from "effect";
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import type { Layer, Schema as S } from "effect";
2
4
 
3
5
  import type * as Binding from "./Binding";
4
6
  import * as DurableObjectEntrypoint from "./DurableObject";
5
7
  import type { DurableObjectHandler } from "./DurableObject";
6
8
  import * as DurableObjectNamespace from "./DurableObjectNamespace";
7
- import type { DurableObjectState } from "./DurableObjectState";
8
9
  import type * as Rpc from "./Rpc";
9
10
  import * as RpcDefinition from "./RpcDefinition";
10
11
  import { recordDecodedArgs } from "./internal/RpcInvocation";
@@ -118,22 +119,21 @@ type MutableBoundaryHandlers<ROut, Self extends Definition.Any> = {
118
119
  /**
119
120
  * Durable Object constructor options for a specific RPC definition.
120
121
  */
121
- export interface Options<
122
+ export type Options<
122
123
  ROut,
123
124
  Self extends Definition.Any,
124
125
  REvent = never,
125
126
  EventLayerError = never,
126
- > extends Omit<
127
- DurableObjectEntrypoint.DurableObjectOptions<
128
- ROut,
129
- REvent,
130
- EventLayerError,
131
- Handlers<ROut | REvent, Self>
132
- >,
133
- "rpc"
134
- > {
135
- readonly rpc: Handlers<ROut | REvent, Self>;
136
- }
127
+ RAlarm = never,
128
+ > = DurableObjectEntrypoint.DurableObjectOptions<
129
+ ROut,
130
+ REvent,
131
+ EventLayerError,
132
+ Handlers<NoInfer<ROut> | REvent | NoInfer<RAlarm>, Self>,
133
+ RAlarm
134
+ > & {
135
+ readonly rpc: Handlers<NoInfer<ROut> | REvent | NoInfer<RAlarm>, Self>;
136
+ };
137
137
 
138
138
  export type LayerOptions = {
139
139
  readonly binding: string;
@@ -159,12 +159,12 @@ export type TagClass<
159
159
  > & {
160
160
  readonly id: Id;
161
161
  readonly methods: MethodDefinitions;
162
- readonly make: <ROut, LayerError, REvent = never, EventLayerError = never>(
163
- layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
164
- options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError>,
162
+ readonly make: <ROut, LayerError, REvent = never, EventLayerError = never, RAlarm = never>(
163
+ layer: Layer.Layer<ROut, LayerError, DurableObjectEntrypoint.RuntimeContext<NoInfer<RAlarm>>>,
164
+ options: Options<ROut, Definition<Id, MethodDefinitions>, REvent, EventLayerError, RAlarm>,
165
165
  ) => DurableObjectEntrypoint.DurableObjectClass<
166
- Handlers<ROut | REvent, Definition<Id, MethodDefinitions>>,
167
- ROut | REvent
166
+ Handlers<ROut | REvent | RAlarm, Definition<Id, MethodDefinitions>>,
167
+ ROut | REvent | RAlarm
168
168
  >;
169
169
  readonly layer: (
170
170
  options: LayerOptions,
@@ -201,9 +201,9 @@ const makeDefinition = <Id extends string, const MethodDefinitions extends Metho
201
201
  const definition: SelfDefinition = RpcDefinition.make(id, methods);
202
202
 
203
203
  return Object.assign(definition, {
204
- make: <ROut, LayerError, REvent = never, EventLayerError = never>(
205
- layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>,
206
- options: Options<ROut, SelfDefinition, REvent, EventLayerError>,
204
+ make: <ROut, LayerError, REvent = never, EventLayerError = never, RAlarm = never>(
205
+ layer: Layer.Layer<ROut, LayerError, DurableObjectEntrypoint.RuntimeContext<NoInfer<RAlarm>>>,
206
+ options: Options<ROut, SelfDefinition, REvent, EventLayerError, RAlarm>,
207
207
  ) =>
208
208
  DurableObjectEntrypoint.make(layer, {
209
209
  ...options,
@@ -1,4 +1,7 @@
1
- import { Data, Effect, Predicate, type Context, type Layer, type Scope } from "effect";
1
+ import * as Data from "effect/Data";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Predicate from "effect/Predicate";
4
+ import type { Context, Layer, Scope } from "effect";
2
5
 
3
6
  import * as Binding from "./Binding";
4
7
  import type { WorkerEnvironment } from "./Environment";
@@ -1,4 +1,10 @@
1
- import { Context, Effect, Layer, Option, Predicate, Queue, Schema } from "effect";
1
+ import * as Context from "effect/Context";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Option from "effect/Option";
5
+ import * as Predicate from "effect/Predicate";
6
+ import * as Queue from "effect/Queue";
7
+ import * as Schema from "effect/Schema";
2
8
  import * as RpcMessage from "effect/unstable/rpc/RpcMessage";
3
9
  import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
4
10
  import * as RpcServer from "effect/unstable/rpc/RpcServer";
@@ -1,5 +1,6 @@
1
1
  import { SqliteClient } from "@effect/sql-sqlite-do";
2
- import { Effect, Layer } from "effect";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
3
4
  import type { SqlClient } from "effect/unstable/sql";
4
5
 
5
6
  import { DurableObjectState } from "./DurableObjectState";
@@ -1,4 +1,7 @@
1
- import { Cause, Context, Effect, Exit } from "effect";
1
+ import * as Cause from "effect/Cause";
2
+ import * as Context from "effect/Context";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Exit from "effect/Exit";
2
5
 
3
6
  import { fromDurableObjectStorage, type DurableObjectStorage } from "./DurableObjectStorage";
4
7
  import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";