effect-cf 0.39.1 → 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 +10 -0
- package/README.md +9 -1
- package/dist/index.d.mts +221 -182
- package/dist/index.mjs +472 -434
- package/package.json +1 -1
- package/src/DurableObject.ts +118 -28
- package/src/DurableObjectAlarm.ts +175 -40
- package/src/DurableObjectDefinition.ts +19 -21
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
|
|
|
@@ -80,11 +89,12 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
80
89
|
: never;
|
|
81
90
|
};
|
|
82
91
|
|
|
83
|
-
|
|
92
|
+
interface DurableObjectOptionsBase<
|
|
84
93
|
RRuntime,
|
|
85
94
|
REvent = never,
|
|
86
95
|
EventLayerError = never,
|
|
87
|
-
Rpc extends DurableObjectRpc<RRuntime | REvent> = Record<never, never>,
|
|
96
|
+
Rpc extends DurableObjectRpc<RRuntime | REvent | RAlarm> = Record<never, never>,
|
|
97
|
+
RAlarm = never,
|
|
88
98
|
> {
|
|
89
99
|
/**
|
|
90
100
|
* Layer provided around each Cloudflare event handled by this Durable Object.
|
|
@@ -96,7 +106,7 @@ export interface DurableObjectOptions<
|
|
|
96
106
|
readonly eventLayer?: Layer.Layer<
|
|
97
107
|
REvent,
|
|
98
108
|
EventLayerError,
|
|
99
|
-
|
|
109
|
+
RuntimeContext<NoInfer<RRuntime | RAlarm>>
|
|
100
110
|
>;
|
|
101
111
|
/**
|
|
102
112
|
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
@@ -106,7 +116,7 @@ export interface DurableObjectOptions<
|
|
|
106
116
|
* the same Durable Object id again after eviction or restart; use Durable
|
|
107
117
|
* Object storage if work must happen only once per id.
|
|
108
118
|
*/
|
|
109
|
-
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<RRuntime
|
|
119
|
+
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime | RAlarm>>>;
|
|
110
120
|
/**
|
|
111
121
|
* Optional RPC methods exposed as Durable Object instance methods.
|
|
112
122
|
*
|
|
@@ -118,9 +128,20 @@ export interface DurableObjectOptions<
|
|
|
118
128
|
readonly rpc?: Rpc;
|
|
119
129
|
/** Accept live native trace metadata. The client must also enable rpcTracing. */
|
|
120
130
|
readonly rpcTracing?: ReceiverOptions;
|
|
121
|
-
readonly fetch?: Effect.Effect<
|
|
131
|
+
readonly fetch?: Effect.Effect<
|
|
132
|
+
Response,
|
|
133
|
+
unknown,
|
|
134
|
+
FetchContext<NoInfer<RRuntime | REvent | RAlarm>>
|
|
135
|
+
>;
|
|
122
136
|
|
|
123
|
-
|
|
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
|
+
>;
|
|
124
145
|
/**
|
|
125
146
|
* Optional raw alarm handler.
|
|
126
147
|
*
|
|
@@ -131,23 +152,58 @@ export interface DurableObjectOptions<
|
|
|
131
152
|
*/
|
|
132
153
|
readonly alarm?: (
|
|
133
154
|
alarmInfo?: globalThis.AlarmInvocationInfo,
|
|
134
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
155
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
135
156
|
readonly webSocketMessage?: (
|
|
136
157
|
socket: DurableWebSocket,
|
|
137
158
|
message: string | ArrayBuffer,
|
|
138
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
159
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
139
160
|
readonly webSocketClose?: (
|
|
140
161
|
socket: DurableWebSocket,
|
|
141
162
|
code: number,
|
|
142
163
|
reason: string,
|
|
143
164
|
wasClean: boolean,
|
|
144
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
165
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
145
166
|
readonly webSocketError?: (
|
|
146
167
|
socket: DurableWebSocket,
|
|
147
168
|
cause: unknown,
|
|
148
|
-
) => Effect.Effect<void, unknown, HandlerContext<RRuntime | REvent
|
|
169
|
+
) => Effect.Effect<void, unknown, HandlerContext<NoInfer<RRuntime> | REvent | NoInfer<RAlarm>>>;
|
|
149
170
|
}
|
|
150
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
|
+
|
|
151
207
|
/**
|
|
152
208
|
* Cloudflare `DurableObject` constructor produced by {@link make}.
|
|
153
209
|
*/
|
|
@@ -168,27 +224,62 @@ export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
|
|
|
168
224
|
* that layer are not guaranteed to run. Put resources that require timely
|
|
169
225
|
* release in `eventLayer`, whose scope closes after each handled event.
|
|
170
226
|
*/
|
|
171
|
-
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<
|
|
172
245
|
ROut,
|
|
173
246
|
LayerError,
|
|
174
247
|
REvent = never,
|
|
175
248
|
EventLayerError = never,
|
|
176
|
-
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,
|
|
177
254
|
>(
|
|
178
|
-
layer: Layer.Layer<ROut, LayerError,
|
|
179
|
-
options:
|
|
180
|
-
): 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
|
+
|
|
181
264
|
class EffectDurableObject extends CloudflareDurableObject<WorkerEnv> {
|
|
182
|
-
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut>, LayerError>;
|
|
265
|
+
readonly runtime: ManagedRuntime.ManagedRuntime<RuntimeContext<ROut | RAlarm>, LayerError>;
|
|
183
266
|
|
|
184
267
|
constructor(state: globalThis.DurableObjectState, env: WorkerEnv) {
|
|
185
268
|
super(state, env);
|
|
186
269
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
env,
|
|
190
|
-
Layer.succeed(DurableObjectState, fromDurableObjectState(state)),
|
|
270
|
+
const baseServices = DurableObjectAlarm.layer.pipe(
|
|
271
|
+
Layer.provideMerge(Layer.succeed(DurableObjectState, fromDurableObjectState(state))),
|
|
191
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);
|
|
192
283
|
|
|
193
284
|
const initialize = options.initialize;
|
|
194
285
|
|
|
@@ -198,7 +289,7 @@ export const make = <
|
|
|
198
289
|
}
|
|
199
290
|
|
|
200
291
|
[RunSymbol]<A, E>(
|
|
201
|
-
effect: Effect.Effect<A, E, HandlerContext<ROut | REvent>>,
|
|
292
|
+
effect: Effect.Effect<A, E, HandlerContext<ROut | REvent | RAlarm>>,
|
|
202
293
|
runOptions: RunOptions = {},
|
|
203
294
|
): Promise<A> {
|
|
204
295
|
const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
|
|
@@ -206,11 +297,11 @@ export const make = <
|
|
|
206
297
|
const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
|
|
207
298
|
|
|
208
299
|
if (eventLayer === undefined) {
|
|
209
|
-
// SAFETY:
|
|
300
|
+
// SAFETY: initialize cannot require event services; without an event layer, all requirements are already provided.
|
|
210
301
|
return Runtime.runEventPromise(
|
|
211
302
|
this.runtime,
|
|
212
303
|
// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
|
|
213
|
-
effect as Effect.Effect<A, E, HandlerContext<ROut>>,
|
|
304
|
+
effect as Effect.Effect<A, E, HandlerContext<ROut | RAlarm>>,
|
|
214
305
|
undefined,
|
|
215
306
|
parentSpan,
|
|
216
307
|
);
|
|
@@ -219,7 +310,7 @@ export const make = <
|
|
|
219
310
|
return Runtime.runEventPromise<
|
|
220
311
|
A,
|
|
221
312
|
E,
|
|
222
|
-
RuntimeContext<ROut>,
|
|
313
|
+
RuntimeContext<ROut | RAlarm>,
|
|
223
314
|
REvent,
|
|
224
315
|
EventLayerError,
|
|
225
316
|
LayerError
|
|
@@ -239,7 +330,6 @@ export const make = <
|
|
|
239
330
|
}
|
|
240
331
|
|
|
241
332
|
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void {
|
|
242
|
-
const logicalAlarms = options.alarms?.pipe(Effect.asVoid);
|
|
243
333
|
const rawAlarm = options.alarm?.(alarmInfo);
|
|
244
334
|
const alarmEffect =
|
|
245
335
|
logicalAlarms !== undefined && rawAlarm !== undefined
|
|
@@ -297,10 +387,10 @@ export const make = <
|
|
|
297
387
|
options.rpcTracing,
|
|
298
388
|
);
|
|
299
389
|
|
|
300
|
-
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent>>(
|
|
390
|
+
return Entrypoint.assumeEntrypointClass<DurableObjectClass<Rpc, ROut | REvent | RAlarm>>(
|
|
301
391
|
EffectDurableObject,
|
|
302
392
|
);
|
|
303
|
-
}
|
|
393
|
+
}
|
|
304
394
|
|
|
305
395
|
export type {
|
|
306
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,
|