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