effect-cf 0.53.0 → 0.55.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 +6 -0
- package/dist/DurableObject.d.mts +1 -0
- package/dist/DurableObject.mjs +33 -11
- package/dist/DurableObjectAlarm.d.mts +76 -9
- package/dist/DurableObjectAlarm.mjs +265 -36
- package/dist/RateLimit.d.mts +50 -0
- package/dist/RateLimit.mjs +46 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/internal/Runtime.mjs +5 -3
- package/package.json +1 -1
- package/src/DurableObject.ts +93 -30
- package/src/DurableObjectAlarm.ts +647 -60
- package/src/RateLimit.ts +110 -0
- package/src/index.ts +1 -0
- package/src/internal/Entrypoint.ts +8 -4
- package/src/internal/Runtime.ts +12 -7
package/dist/index.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import { Email_exports } from "./Email.mjs";
|
|
|
35
35
|
import { Hyperdrive_exports } from "./Hyperdrive.mjs";
|
|
36
36
|
import { Images_exports } from "./Images.mjs";
|
|
37
37
|
import { Kv_exports } from "./Kv.mjs";
|
|
38
|
+
import { RateLimit_exports } from "./RateLimit.mjs";
|
|
38
39
|
import { R2_exports } from "./R2.mjs";
|
|
39
40
|
import { RpcWebSocketClient_exports } from "./RpcWebSocketClient.mjs";
|
|
40
41
|
import { Vectorize_exports } from "./Vectorize.mjs";
|
|
@@ -42,4 +43,4 @@ import { WebTransport_exports } from "./WebTransport.mjs";
|
|
|
42
43
|
import { WorkflowBinding_exports } from "./WorkflowBinding.mjs";
|
|
43
44
|
import { WorkflowDefinition_exports } from "./WorkflowDefinition.mjs";
|
|
44
45
|
import { Workflow_exports } from "./Workflow.mjs";
|
|
45
|
-
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerFiles_exports as ContainerFiles, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectContainer_exports as DurableObjectContainer, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
|
46
|
+
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerFiles_exports as ContainerFiles, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectContainer_exports as DurableObjectContainer, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, RateLimit_exports as RateLimit, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
|
@@ -31,14 +31,16 @@ const makeEntrypointRuntime = (layer, env, services) => {
|
|
|
31
31
|
const entrypointServices = Layer.mergeAll(services, ConfigProvider.layer(WorkerConfig.providerFromEnv(env)), Layer.succeed(WorkerEnvironment, env));
|
|
32
32
|
return ManagedRuntime.make(provideEntrypointServices(layer, entrypointServices));
|
|
33
33
|
};
|
|
34
|
+
/** Close event resources before an outer lifecycle (such as alarm reconciliation) completes. */
|
|
35
|
+
const scopeEvent = (effect) => Effect.scoped(withScope(effect));
|
|
34
36
|
function runEventPromise(...args) {
|
|
35
37
|
const [runtime, effect, eventLayer, parent, onFailure] = args;
|
|
36
38
|
if (eventLayer === void 0) {
|
|
37
|
-
const event =
|
|
39
|
+
const event = scopeEvent(effect);
|
|
38
40
|
return runPromise(runtime, parent === void 0 ? event : Effect.withParentSpan(event, parent), onFailure);
|
|
39
41
|
}
|
|
40
|
-
const event =
|
|
42
|
+
const event = scopeEvent(effect.pipe(Effect.provide(eventLayer, { local: true })));
|
|
41
43
|
return runPromise(runtime, parent === void 0 ? event : Effect.withParentSpan(event, parent), onFailure);
|
|
42
44
|
}
|
|
43
45
|
//#endregion
|
|
44
|
-
export { makeEntrypointRuntime, runEventPromise };
|
|
46
|
+
export { makeEntrypointRuntime, runEventPromise, scopeEvent };
|
package/package.json
CHANGED
package/src/DurableObject.ts
CHANGED
|
@@ -10,8 +10,14 @@ import { WorkerEnvironment, type WorkerEnv } from "./Environment";
|
|
|
10
10
|
import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
|
|
11
11
|
import {
|
|
12
12
|
DurableObjectAlarm,
|
|
13
|
+
DispatchAfterEvent,
|
|
14
|
+
HasDueAlarms,
|
|
15
|
+
RunAlarm,
|
|
16
|
+
dispatchRawAlarm,
|
|
13
17
|
type AlarmRegistration,
|
|
14
18
|
type AlarmService,
|
|
19
|
+
type InvalidAlarmRegistrationError,
|
|
20
|
+
type InvalidScheduleConfigurationError,
|
|
15
21
|
} from "./DurableObjectAlarm";
|
|
16
22
|
import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
|
|
17
23
|
import * as RpcDefinition from "./RpcDefinition";
|
|
@@ -229,6 +235,7 @@ export type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (
|
|
|
229
235
|
env: WorkerEnv,
|
|
230
236
|
) => CloudflareDurableObject<WorkerEnv> &
|
|
231
237
|
DurableObjectRpcApi<Rpc, ROut> & {
|
|
238
|
+
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> | void;
|
|
232
239
|
[RunSymbol]<A, E>(
|
|
233
240
|
effect: Effect.Effect<A, E, HandlerContext<ROut>>,
|
|
234
241
|
options?: RunOptions,
|
|
@@ -274,12 +281,15 @@ export function make<
|
|
|
274
281
|
): DurableObjectClass<Rpc, ROut | REvent | RAlarm> {
|
|
275
282
|
const registration =
|
|
276
283
|
options.alarms !== undefined && !Effect.isEffect(options.alarms) ? options.alarms : undefined;
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
284
|
+
const legacyAlarms = Effect.isEffect(options.alarms)
|
|
285
|
+
? options.alarms.pipe(Effect.asVoid)
|
|
286
|
+
: undefined;
|
|
280
287
|
|
|
281
288
|
class EffectDurableObject extends CloudflareDurableObject<WorkerEnv> {
|
|
282
|
-
readonly runtime: ManagedRuntime.ManagedRuntime<
|
|
289
|
+
readonly runtime: ManagedRuntime.ManagedRuntime<
|
|
290
|
+
RuntimeContext<ROut | RAlarm>,
|
|
291
|
+
LayerError | InvalidAlarmRegistrationError
|
|
292
|
+
>;
|
|
283
293
|
|
|
284
294
|
constructor(state: globalThis.DurableObjectState, env: WorkerEnv) {
|
|
285
295
|
super(state, env);
|
|
@@ -290,12 +300,16 @@ export function make<
|
|
|
290
300
|
// SAFETY: RAlarm is inferred only from the registration's layer; without a registration it is never.
|
|
291
301
|
const services = (registration?.layer ?? Layer.empty).pipe(
|
|
292
302
|
Layer.provideMerge(baseServices),
|
|
293
|
-
) as Layer.Layer<
|
|
303
|
+
) as Layer.Layer<
|
|
304
|
+
DurableObjectState | DurableObjectAlarm | RAlarm,
|
|
305
|
+
InvalidAlarmRegistrationError
|
|
306
|
+
>;
|
|
294
307
|
|
|
295
308
|
this.runtime = Runtime.makeEntrypointRuntime<
|
|
296
309
|
ROut,
|
|
297
310
|
LayerError,
|
|
298
|
-
DurableObjectState | DurableObjectAlarm | RAlarm
|
|
311
|
+
DurableObjectState | DurableObjectAlarm | RAlarm,
|
|
312
|
+
InvalidAlarmRegistrationError
|
|
299
313
|
>(layer, env, services);
|
|
300
314
|
|
|
301
315
|
const initialize = options.initialize;
|
|
@@ -322,27 +336,54 @@ export function make<
|
|
|
322
336
|
const eventLayer = runOptions.eventLayer === false ? undefined : options.eventLayer;
|
|
323
337
|
const parent = runOptions.rpc?.parent;
|
|
324
338
|
const parentSpan = parent === undefined ? undefined : Tracer.externalSpan(parent);
|
|
339
|
+
const afterResponse = runOptions.event === "rpc" || runOptions.event === "fetch";
|
|
340
|
+
let dispatchAfterEvent = false;
|
|
341
|
+
const handler = afterResponse
|
|
342
|
+
? effect.pipe(
|
|
343
|
+
Effect.onExit(() =>
|
|
344
|
+
Effect.flatMap(DurableObjectAlarm, (alarms) =>
|
|
345
|
+
Effect.map(alarms[DispatchAfterEvent], (enabled) => {
|
|
346
|
+
dispatchAfterEvent = enabled;
|
|
347
|
+
}),
|
|
348
|
+
),
|
|
349
|
+
),
|
|
350
|
+
)
|
|
351
|
+
: effect;
|
|
352
|
+
let response: Promise<A>;
|
|
325
353
|
|
|
326
354
|
if (eventLayer === undefined) {
|
|
327
355
|
// SAFETY: initialize cannot require event services; without an event layer, all requirements are already provided.
|
|
328
|
-
|
|
356
|
+
response = Runtime.runEventPromise(
|
|
329
357
|
this.runtime,
|
|
330
358
|
// @effect-diagnostics-next-line unsafeEffectTypeAssertion:off
|
|
331
|
-
|
|
359
|
+
handler as Effect.Effect<A, E, HandlerContext<ROut | RAlarm>>,
|
|
332
360
|
undefined,
|
|
333
361
|
parentSpan,
|
|
334
362
|
runOptions.onFailure,
|
|
335
363
|
);
|
|
364
|
+
} else {
|
|
365
|
+
response = Runtime.runEventPromise<
|
|
366
|
+
A,
|
|
367
|
+
E,
|
|
368
|
+
RuntimeContext<ROut | RAlarm>,
|
|
369
|
+
REvent,
|
|
370
|
+
EventLayerError,
|
|
371
|
+
LayerError | InvalidAlarmRegistrationError
|
|
372
|
+
>(this.runtime, handler, eventLayer, parentSpan, runOptions.onFailure);
|
|
336
373
|
}
|
|
337
374
|
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
375
|
+
if (!afterResponse) return response;
|
|
376
|
+
|
|
377
|
+
return response.finally(() => {
|
|
378
|
+
if (dispatchAfterEvent) {
|
|
379
|
+
this.ctx.waitUntil(
|
|
380
|
+
// The default scheduler yields to a later task, after the response chain unwinds.
|
|
381
|
+
Effect.runPromise(
|
|
382
|
+
Effect.yieldNow.pipe(Effect.andThen(Effect.promise(() => this.#runAlarm(false)))),
|
|
383
|
+
),
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
346
387
|
}
|
|
347
388
|
|
|
348
389
|
fetch(request: Request): Promise<Response> {
|
|
@@ -357,21 +398,43 @@ export function make<
|
|
|
357
398
|
});
|
|
358
399
|
}
|
|
359
400
|
|
|
360
|
-
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void>
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
401
|
+
alarm(alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> {
|
|
402
|
+
return this.#runAlarm(true, alarmInfo);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
#runAlarm(native: boolean, alarmInfo?: globalThis.AlarmInvocationInfo): Promise<void> {
|
|
406
|
+
const event = (beginDispatch: Effect.Effect<void, InvalidScheduleConfigurationError>) => {
|
|
407
|
+
const alarmEffect = Effect.gen(function* () {
|
|
408
|
+
const alarms = yield* DurableObjectAlarm;
|
|
409
|
+
|
|
410
|
+
if (!native && !(yield* alarms[HasDueAlarms])) return;
|
|
411
|
+
const rawAlarm = options.alarm?.(alarmInfo);
|
|
412
|
+
const customAlarm =
|
|
413
|
+
legacyAlarms === undefined
|
|
414
|
+
? (rawAlarm ?? Effect.void)
|
|
415
|
+
: legacyAlarms.pipe(Effect.andThen(rawAlarm ?? Effect.void));
|
|
416
|
+
|
|
417
|
+
yield* beginDispatch;
|
|
418
|
+
// Custom dispatchers, including Effect-form alarms, recover retained checkpoints.
|
|
419
|
+
yield* registration === undefined || rawAlarm !== undefined
|
|
420
|
+
? dispatchRawAlarm(customAlarm, registration)
|
|
421
|
+
: registration.run.pipe(Effect.asVoid);
|
|
422
|
+
}).pipe(Effect.onExit(() => scheduleTelemetryFlush));
|
|
423
|
+
|
|
424
|
+
return Runtime.scopeEvent(
|
|
425
|
+
options.eventLayer === undefined
|
|
426
|
+
? alarmEffect
|
|
427
|
+
: alarmEffect.pipe(Effect.provide(options.eventLayer, { local: true })),
|
|
428
|
+
);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
return this[RunSymbol](
|
|
432
|
+
Effect.flatMap(DurableObjectAlarm, (alarms) => alarms[RunAlarm](event, native)),
|
|
433
|
+
{
|
|
372
434
|
event: "alarm",
|
|
373
|
-
|
|
374
|
-
|
|
435
|
+
eventLayer: false,
|
|
436
|
+
},
|
|
437
|
+
);
|
|
375
438
|
}
|
|
376
439
|
|
|
377
440
|
webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void> | void {
|