effect-cf 0.0.4 → 0.1.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/LICENSE +21 -0
- package/README.md +36 -46
- package/dist/index.d.mts +342 -10
- package/dist/index.mjs +332 -3
- package/package.json +1 -1
- package/src/DurableObject.ts +26 -2
- package/src/DurableObjectAlarm.ts +832 -0
- package/src/index.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dan van der Merwe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,70 +1,56 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
-
Effect-native primitives for
|
|
3
|
+
Effect-native Cloudflare primitives for Workers, Durable Objects, bindings, KV, and Durable Object storage.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`effect-cf` currently targets Effect 4 beta.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Request-scoped app services belong in the implementation layer that owns request semantics, such as `HttpRouter` middleware, RPC middleware, or explicit `Effect.provide(...)` inside a custom fetch effect. `Worker.make` stays focused on Cloudflare boundary services: `NativeRequest`, `HttpServerRequest`, `WorkerEnvironment`, `ExecutionContext`, and `WorkerContext`.
|
|
9
|
+
```bash
|
|
10
|
+
bun add effect-cf "effect@^4.0.0-beta.65"
|
|
11
|
+
```
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add effect-cf "effect@^4.0.0-beta.65"
|
|
15
|
+
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npm install effect-cf "effect@^4.0.0-beta.65"
|
|
19
|
+
```
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
## Goal
|
|
20
22
|
|
|
21
|
-
- `
|
|
22
|
-
- `rpc(...)` returns Cloudflare's native RPC thenable/stub without awaiting it, preserving promise pipelining.
|
|
23
|
-
- `scopedCall(...)` awaits the result and registers any `Symbol.dispose` disposer with the current Effect `Scope`.
|
|
23
|
+
Cloudflare APIs return promises and expose platform-specific bindings. `effect-cf` wraps those boundaries as `Context`, `Layer`, and `Effect` values so application code stays inside one managed Effect runtime.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Runtime creation belongs at Cloudflare entrypoints, not inside binding helpers.
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## Exports
|
|
28
28
|
|
|
29
|
-
- `Worker` -
|
|
30
|
-
- `DurableObject` - Durable Object
|
|
31
|
-
- `
|
|
32
|
-
- `
|
|
33
|
-
- `DurableObjectState`, `DurableObjectStorage` - small Effect wrappers for Durable Object state, alarms, SQL, and embedded KV storage
|
|
34
|
-
- `DurableObjectWebSocket` - Durable Object WebSocket upgrade helpers built on `WebSocketPair` and `acceptWebSocket`
|
|
35
|
-
- `Binding` - low-level binding service helper
|
|
29
|
+
- `Worker` - Worker entrypoint factory, request services, and typed Worker bindings
|
|
30
|
+
- `DurableObject` - Durable Object entrypoint factory and typed namespace helpers
|
|
31
|
+
- `DurableObjectState` / `DurableObjectStorage` - Effect wrappers for state, alarms, SQL, and embedded KV
|
|
32
|
+
- `DurableObjectWebSocket` - WebSocket upgrade helpers for Durable Objects
|
|
36
33
|
- `Kv` - typed KV namespace helper
|
|
37
|
-
- `Rpc` - Cloudflare RPC type helpers
|
|
38
|
-
- `
|
|
39
|
-
- `WorkerEnvironment` - request environment service
|
|
40
|
-
- `WorkerConfig` - Effect `Config` helpers backed by Cloudflare worker `env`, including `layerWith` for custom env-to-config mapping
|
|
41
|
-
|
|
42
|
-
## RPC Values
|
|
43
|
-
|
|
44
|
-
Definition-backed RPC methods are intended for schema-backed structured values. If a Cloudflare RPC method returns a lifecycle-managed RPC object or stub, expose that through a lower-level binding API instead of a `WorkerDefinition` / `DurableObjectDefinition` method so disposal remains explicit.
|
|
34
|
+
- `Rpc` - Cloudflare RPC type helpers and scoped disposal utilities
|
|
35
|
+
- `WorkerConfig` - Effect `Config` helpers backed by Cloudflare `env`
|
|
45
36
|
|
|
46
|
-
##
|
|
37
|
+
## Worker Example
|
|
47
38
|
|
|
48
39
|
```ts
|
|
49
|
-
|
|
40
|
+
import { HttpServerResponse } from "effect";
|
|
41
|
+
import { Worker } from "effect-cf";
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const upgrade =
|
|
56
|
-
yield *
|
|
57
|
-
DurableObjectWebSocket.acceptUpgrade({
|
|
58
|
-
tags: ["room:general"],
|
|
59
|
-
attachment: { roomId: "general" },
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return upgrade.response;
|
|
43
|
+
export default Worker.make({
|
|
44
|
+
fetch: () => HttpServerResponse.text("ok"),
|
|
45
|
+
});
|
|
63
46
|
```
|
|
64
47
|
|
|
65
48
|
## Durable Object Example
|
|
66
49
|
|
|
67
50
|
```ts
|
|
51
|
+
import { Effect, Layer, Schema as S } from "effect";
|
|
52
|
+
import { DurableObject, DurableObjectState } from "effect-cf";
|
|
53
|
+
|
|
68
54
|
export class Counter extends DurableObject.Tag<Counter>()("Counter", {
|
|
69
55
|
get: DurableObject.method({ success: S.Number }),
|
|
70
56
|
}) {}
|
|
@@ -83,4 +69,8 @@ export const CounterDurableObject = Counter.make(Layer.empty, {
|
|
|
83
69
|
});
|
|
84
70
|
```
|
|
85
71
|
|
|
86
|
-
Define
|
|
72
|
+
Define Wrangler bindings and migrations in the consuming application.
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
76
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cause, Config, ConfigProvider, Context, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
1
|
+
import { Cause, Config, ConfigProvider, Context, DateTime, Duration, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
2
2
|
import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
|
|
3
3
|
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
|
|
4
4
|
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
@@ -440,7 +440,7 @@ declare class DurableObjectState extends DurableObjectState_base {}
|
|
|
440
440
|
*/
|
|
441
441
|
declare const fromDurableObjectState: (state: globalThis.DurableObjectState) => DurableObjectStateService;
|
|
442
442
|
declare namespace DurableObjectDefinition_d_exports {
|
|
443
|
-
export { Api$3 as Api, Definition$3 as Definition, DurableObject$1 as DurableObject, HandlerEffect$3 as HandlerEffect, Handlers$3 as Handlers, NamespaceService, Options$3 as Options, ReservedMethodName$4 as ReservedMethodName, ServerApi$4 as ServerApi, Tag$
|
|
443
|
+
export { Api$3 as Api, Definition$3 as Definition, DurableObject$1 as DurableObject, HandlerEffect$3 as HandlerEffect, Handlers$3 as Handlers, NamespaceService, Options$3 as Options, ReservedMethodName$4 as ReservedMethodName, ServerApi$4 as ServerApi, Tag$4 as Tag, implement$3 as implement, make$4 as make, method$3 as method };
|
|
444
444
|
}
|
|
445
445
|
/**
|
|
446
446
|
* RPC contract for a Durable Object class.
|
|
@@ -533,7 +533,7 @@ declare const make$4: <Id extends string, const MethodsShape extends Methods>(id
|
|
|
533
533
|
readonly scopedCall: <Method extends (([Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>) extends infer T_2 ? { [Key in keyof T_2]-?: Key extends string ? T_2[Key] extends ((...args: any[]) => Promise<unknown>) ? Key : never : never } : never)[keyof ([Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>)]>(stub: DurableObjectStubClient<[Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>>, method: Method, ...args: AsyncMethodArgs<[Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>, Method>) => Effect.Effect<Awaited<Result<AsyncMethodSuccess<[Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>, Method>>>, unknown, _$effect_Scope0.Scope>;
|
|
534
534
|
};
|
|
535
535
|
};
|
|
536
|
-
declare const Tag$
|
|
536
|
+
declare const Tag$4: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$4>) => (abstract new () => object) & (Definition$3<Id, MethodsShape> & {
|
|
537
537
|
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>, options: Options$3<ROut, Definition$3<Id, MethodsShape>>) => DurableObjectClass<BoundaryHandlers$1<ROut, Definition$3<Id, MethodsShape>>, ROut>;
|
|
538
538
|
Namespace: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<DurableObjectNamespaceBindingDefinition<Definition$3<Id, MethodsShape>>, "definition">) => _$effect_Context0.ServiceClass<Self, BindingId, DurableObjectNamespaceClient<[Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>>> & {
|
|
539
539
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
@@ -771,7 +771,7 @@ declare const Service$2: <Self, Api extends object = never>() => <Id extends str
|
|
|
771
771
|
readonly scopedCall: <Method extends StubMethodKey<ApiOrDefinition$1<Api, Definition>>>(stub: DurableObjectStubClient<ApiOrDefinition$1<Api, Definition>>, method: Method, ...args: StubMethodArgs<ApiOrDefinition$1<Api, Definition>, Method>) => Effect.Effect<Awaited<Result<AsyncMethodSuccess<ApiOrDefinition$1<Api, Definition>, Method>>>, unknown, _$effect_Scope0.Scope>;
|
|
772
772
|
};
|
|
773
773
|
declare namespace WorkerDefinition_d_exports {
|
|
774
|
-
export { Api$2 as Api, BindingService, Definition$2 as Definition, HandlerEffect$2 as HandlerEffect, Handlers$2 as Handlers, Options$2 as Options, ReservedMethodName$2 as ReservedMethodName, ServerApi$3 as ServerApi, Tag$
|
|
774
|
+
export { Api$2 as Api, BindingService, Definition$2 as Definition, HandlerEffect$2 as HandlerEffect, Handlers$2 as Handlers, Options$2 as Options, ReservedMethodName$2 as ReservedMethodName, ServerApi$3 as ServerApi, Tag$3 as Tag, Worker, implement$2 as implement, make$3 as make, method$2 as method };
|
|
775
775
|
}
|
|
776
776
|
/**
|
|
777
777
|
* RPC contract for a Worker service.
|
|
@@ -841,7 +841,7 @@ declare const make$3: <Id extends string, const MethodsShape extends Methods>(id
|
|
|
841
841
|
readonly scopedCall: <Method extends (([Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>) extends infer T_2 ? { [Key in keyof T_2]-?: Key extends string ? T_2[Key] extends ((...args: any[]) => Promise<unknown>) ? Key : never : never } : never)[keyof ([Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>)]>(method: Method, ...args: AsyncMethodArgs<[Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>, Method>) => Effect.Effect<Awaited<Result<AsyncMethodSuccess<[Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>, Method>>>, unknown, BindingService<BindingId, Definition$2<Id, MethodsShape>> | _$effect_Scope0.Scope>;
|
|
842
842
|
};
|
|
843
843
|
};
|
|
844
|
-
declare const Tag$
|
|
844
|
+
declare const Tag$3: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => (abstract new () => object) & (Definition$2<Id, MethodsShape> & {
|
|
845
845
|
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>>) => WorkerClass<BoundaryHandlers<ROut, Definition$2<Id, MethodsShape>>, ROut>;
|
|
846
846
|
Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => _$effect_Context0.ServiceClass<Self, BindingId, ServiceBindingClient<[Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>>> & {
|
|
847
847
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
@@ -994,7 +994,7 @@ declare const Service$1: <Self, Api extends object = never>() => <Id extends str
|
|
|
994
994
|
readonly scopedCall: <Method extends ServiceMethodKey<ApiOrDefinition<Api, Definition>>>(method: Method, ...args: ServiceMethodArgs<ApiOrDefinition<Api, Definition>, Method>) => Effect.Effect<Awaited<Result<AsyncMethodSuccess<ApiOrDefinition<Api, Definition>, Method>>>, unknown, Self | _$effect_Scope0.Scope>;
|
|
995
995
|
};
|
|
996
996
|
declare namespace Worker_d_exports {
|
|
997
|
-
export { Api$1 as Api, Definition$1 as Definition, ExecutionContext, FetchHandler, FetchWorkerOptions, HandlerEffect$1 as HandlerEffect, Handlers$1 as Handlers, NativeRequest, Options$1 as Options, ReservedMethodName, RpcHandlers$1 as RpcHandlers, ServerApi$2 as ServerApi, Tag$
|
|
997
|
+
export { Api$1 as Api, Definition$1 as Definition, ExecutionContext, FetchHandler, FetchWorkerOptions, HandlerEffect$1 as HandlerEffect, Handlers$1 as Handlers, NativeRequest, Options$1 as Options, ReservedMethodName, RpcHandlers$1 as RpcHandlers, ServerApi$2 as ServerApi, Tag$2 as Tag, WorkerClass, WorkerContext, WorkerContextService, WorkerContextWaitUntilOptions, WorkerFetchSuccess, WorkerHandler, WorkerOptions, WorkerRpc, WorkerRpcHandler, WorkerRpcShape, implement$1 as implement, isWebSocketUpgrade, make$2 as make, makeFetchHandler, method$1 as method, renderHttpResponse };
|
|
998
998
|
}
|
|
999
999
|
declare const ExecutionContext_base: Context.ServiceClass<ExecutionContext, "effect-cf/ExecutionContext", globalThis.ExecutionContext<unknown>>;
|
|
1000
1000
|
declare class ExecutionContext extends ExecutionContext_base {}
|
|
@@ -1037,7 +1037,7 @@ interface FetchHandler<Env extends WorkerEnv = WorkerEnv> {
|
|
|
1037
1037
|
declare const renderHttpResponse: <A extends HttpServerResponse.HttpServerResponse, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<Response, E, R>;
|
|
1038
1038
|
declare const make$2: <ROut, LayerError, const Rpc extends WorkerRpc<ROut> = Record<never, never>>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: WorkerOptions<ROut, Rpc>) => WorkerClass<Rpc, ROut>;
|
|
1039
1039
|
declare const makeFetchHandler: <ROut, LayerError, Env extends WorkerEnv = WorkerEnv>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: FetchWorkerOptions<ROut>) => FetchHandler<Env>;
|
|
1040
|
-
declare const Tag$
|
|
1040
|
+
declare const Tag$2: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$2>) => (abstract new () => object) & (Definition$2<Id, MethodsShape> & {
|
|
1041
1041
|
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, ExecutionContext | WorkerContext | WorkerEnvironment>, options: Options$2<ROut, Definition$2<Id, MethodsShape>>) => WorkerClass<{ readonly [Key in keyof MethodsShape]: (...args: Array<unknown>) => WorkerRpcHandler<ROut, _$effect_Schema0.Codec.Encoded<MethodsShape[Key]["success"]>> }, ROut>;
|
|
1042
1042
|
Binding: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<ServiceBindingDefinition<Definition$2<Id, MethodsShape>>, "definition">) => Context.ServiceClass<Self, BindingId, ServiceBindingClient<[Api$2<Definition$2<Id, MethodsShape>>] extends [never] ? Definition$2<Id, MethodsShape> extends infer T ? T extends Definition$2<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$3<T> : never : never : never : Api$2<Definition$2<Id, MethodsShape>>>> & {
|
|
1043
1043
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
@@ -1076,7 +1076,7 @@ type Handlers$1<ROut, Self extends Definition$2.Any> = Handlers$2<ROut, Self>;
|
|
|
1076
1076
|
type Options$1<ROut, Self extends Definition$2.Any> = Options$2<ROut, Self>;
|
|
1077
1077
|
type HandlerEffect$1<ROut, Self extends Definition$2.Any, Key extends keyof Self["methods"]> = HandlerEffect$2<ROut, Self, Key>;
|
|
1078
1078
|
declare namespace DurableObject_d_exports {
|
|
1079
|
-
export { Api, Definition, DurableObjectClass, DurableObjectHandler, DurableObjectOptions, DurableObjectRpc, DurableObjectRpcShape, HandlerEffect, Handlers, Options, RpcHandlers, ServerApi$1 as ServerApi, Tag, implement, make$1 as make, method };
|
|
1079
|
+
export { Api, Definition, DurableObjectClass, DurableObjectHandler, DurableObjectOptions, DurableObjectRpc, DurableObjectRpcShape, HandlerEffect, Handlers, Options, RpcHandlers, ServerApi$1 as ServerApi, Tag$1 as Tag, implement, make$1 as make, method };
|
|
1080
1080
|
}
|
|
1081
1081
|
type RuntimeContext<ROut> = DurableObjectState | WorkerEnvironment | ROut;
|
|
1082
1082
|
type HandlerContext<ROut> = RuntimeContext<ROut> | Scope.Scope;
|
|
@@ -1099,6 +1099,14 @@ interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>> {
|
|
|
1099
1099
|
readonly rpc?: Rpc;
|
|
1100
1100
|
/** Optional fetch handler for HTTP/WebSocket requests. */
|
|
1101
1101
|
readonly fetch?: Effect.Effect<Response, unknown, FetchContext<ROut>>;
|
|
1102
|
+
/**
|
|
1103
|
+
* Optional logical alarm processing effect.
|
|
1104
|
+
*
|
|
1105
|
+
* This runs before `alarm` and should be built with helpers such as
|
|
1106
|
+
* `DurableObjectAlarm.processDue(...)` so the reusable scheduler stays inside
|
|
1107
|
+
* the Durable Object's single managed runtime boundary.
|
|
1108
|
+
*/
|
|
1109
|
+
readonly alarms?: Effect.Effect<unknown, unknown, HandlerContext<ROut>>;
|
|
1102
1110
|
readonly alarm?: (alarmInfo?: globalThis.AlarmInvocationInfo) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
|
|
1103
1111
|
readonly webSocketMessage?: (socket: WebSocket, message: string | ArrayBuffer) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
|
|
1104
1112
|
readonly webSocketClose?: (socket: WebSocket, code: number, reason: string, wasClean: boolean) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
|
|
@@ -1112,7 +1120,7 @@ type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (state:
|
|
|
1112
1120
|
* Creates a Durable Object class backed by a single managed Effect runtime.
|
|
1113
1121
|
*/
|
|
1114
1122
|
declare const make$1: <ROut, LayerError, const Rpc extends DurableObjectRpc<ROut> = Record<never, never>>(layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>, options?: DurableObjectOptions<ROut, Rpc>) => DurableObjectClass<Rpc, ROut>;
|
|
1115
|
-
declare const Tag: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$4>) => (abstract new () => object) & (Definition$3<Id, MethodsShape> & {
|
|
1123
|
+
declare const Tag$1: <_Self>() => <Id extends string, const MethodsShape extends Methods>(id: Id, methods: MethodsShape & NoReservedMethods<MethodsShape, ReservedMethodName$4>) => (abstract new () => object) & (Definition$3<Id, MethodsShape> & {
|
|
1116
1124
|
make: <ROut, LayerError>(layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>, options: Options$3<ROut, Definition$3<Id, MethodsShape>>) => DurableObjectClass<{ readonly [Key in keyof MethodsShape]: (...args: Array<unknown>) => DurableObjectHandler<ROut, _$effect_Schema0.Codec.Encoded<MethodsShape[Key]["success"]>> }, ROut>;
|
|
1117
1125
|
Namespace: <Self>() => <BindingId extends string>(bindingId: BindingId, binding: Omit<DurableObjectNamespaceBindingDefinition<Definition$3<Id, MethodsShape>>, "definition">) => _$effect_Context0.ServiceClass<Self, BindingId, DurableObjectNamespaceClient<[Api$3<Definition$3<Id, MethodsShape>>] extends [never] ? Definition$3<Id, MethodsShape> extends infer T ? T extends Definition$3<Id, MethodsShape> ? T extends Definition$4.Any ? ServerApi$4<T> : never : never : never : Api$3<Definition$3<Id, MethodsShape>>>> & {
|
|
1118
1126
|
"effect-cf/Binding": "effect-cf/Binding";
|
|
@@ -1176,6 +1184,330 @@ type Api<Self extends Definition$3.Any> = Api$3<Self>;
|
|
|
1176
1184
|
type Handlers<ROut, Self extends Definition$3.Any> = Handlers$3<ROut, Self>;
|
|
1177
1185
|
type Options<ROut, Self extends Definition$3.Any> = Options$3<ROut, Self>;
|
|
1178
1186
|
type HandlerEffect<ROut, Self extends Definition$3.Any, Key extends keyof Self["methods"]> = HandlerEffect$3<ROut, Self, Key>;
|
|
1187
|
+
declare namespace DurableObjectAlarm_d_exports {
|
|
1188
|
+
export { AlarmDefinitionConfig, AlarmDefinitionEntry, AlarmDefinitionPayload, AlarmDefinitions, AlarmFailurePolicy, AlarmPayload, AlarmPayloadSchema, AlarmRef, AlarmRetryPolicy, AlarmScheduler, DefinedAlarmEvent, DefinedAlarmHandlers, DurableObjectAlarm, DurableObjectAlarmError, DurableObjectAlarmEvent, InvalidAlarmPayloadError, InvalidAlarmRefError, InvalidProcessDueAlarmsOptionsError, InvalidRepeatEveryError, ProcessDueAlarmsFailure, ProcessDueAlarmsFailureAction, ProcessDueAlarmsHandler, ProcessDueAlarmsMode, ProcessDueAlarmsOptions, ProcessDueAlarmsResult, ScheduleAlarmInput, StoredAlarmDecodeError, define, process, processDue };
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* JSON-serializable alarm payload stored with each scheduled alarm.
|
|
1192
|
+
*
|
|
1193
|
+
* Payloads are intentionally opaque to `DurableObjectAlarm`. Consumers should use
|
|
1194
|
+
* stable `tag` values to route due alarms and then decode the payload for that
|
|
1195
|
+
* tag in their own domain layer.
|
|
1196
|
+
*/
|
|
1197
|
+
type AlarmPayload = Schema.Json;
|
|
1198
|
+
declare const InvalidAlarmRefError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1199
|
+
readonly _tag: "InvalidAlarmRefError";
|
|
1200
|
+
} & Readonly<A>;
|
|
1201
|
+
declare class InvalidAlarmRefError extends InvalidAlarmRefError_base<{
|
|
1202
|
+
readonly cause: unknown;
|
|
1203
|
+
}> {}
|
|
1204
|
+
declare const InvalidAlarmPayloadError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1205
|
+
readonly _tag: "InvalidAlarmPayloadError";
|
|
1206
|
+
} & Readonly<A>;
|
|
1207
|
+
declare class InvalidAlarmPayloadError extends InvalidAlarmPayloadError_base<{
|
|
1208
|
+
readonly cause: unknown;
|
|
1209
|
+
}> {}
|
|
1210
|
+
declare const InvalidRepeatEveryError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1211
|
+
readonly _tag: "InvalidRepeatEveryError";
|
|
1212
|
+
} & Readonly<A>;
|
|
1213
|
+
declare class InvalidRepeatEveryError extends InvalidRepeatEveryError_base<{
|
|
1214
|
+
readonly cause: unknown;
|
|
1215
|
+
}> {}
|
|
1216
|
+
declare const InvalidProcessDueAlarmsOptionsError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1217
|
+
readonly _tag: "InvalidProcessDueAlarmsOptionsError";
|
|
1218
|
+
} & Readonly<A>;
|
|
1219
|
+
declare class InvalidProcessDueAlarmsOptionsError extends InvalidProcessDueAlarmsOptionsError_base<{
|
|
1220
|
+
readonly cause: unknown;
|
|
1221
|
+
}> {}
|
|
1222
|
+
declare const StoredAlarmDecodeError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1223
|
+
readonly _tag: "StoredAlarmDecodeError";
|
|
1224
|
+
} & Readonly<A>;
|
|
1225
|
+
declare class StoredAlarmDecodeError extends StoredAlarmDecodeError_base<{
|
|
1226
|
+
readonly cause: unknown;
|
|
1227
|
+
readonly storageId: string;
|
|
1228
|
+
}> {}
|
|
1229
|
+
type DurableObjectAlarmError = InvalidAlarmPayloadError | InvalidAlarmRefError | InvalidProcessDueAlarmsOptionsError | InvalidRepeatEveryError | StorageOperationError | StoredAlarmDecodeError;
|
|
1230
|
+
/**
|
|
1231
|
+
* Events handled by `processDueAlarms`.
|
|
1232
|
+
*
|
|
1233
|
+
* `AlarmDue` represents one durable alarm whose scheduled time is now due. The
|
|
1234
|
+
* `tag` is the consumer-owned routing key, while `id` is the stable alarm id
|
|
1235
|
+
* within that tag.
|
|
1236
|
+
*
|
|
1237
|
+
* `scheduledAt` is the logical due-after timestamp from durable storage. It is
|
|
1238
|
+
* not expected to match the current Durable Object alarm invocation time,
|
|
1239
|
+
* especially during retries. Retries re-scan durable rows with `run_at <= now`
|
|
1240
|
+
* and identify logical events by `{ tag, id }`.
|
|
1241
|
+
*
|
|
1242
|
+
* @example Handling events by tag
|
|
1243
|
+
* ```ts
|
|
1244
|
+
* yield* durableObjectAlarm.processDueAlarms((event) =>
|
|
1245
|
+
* event.tag === "heartbeat"
|
|
1246
|
+
* ? heartbeatManager.handleAlarmEvent(event).pipe(Effect.asVoid)
|
|
1247
|
+
* : Effect.void
|
|
1248
|
+
* );
|
|
1249
|
+
* ```
|
|
1250
|
+
*/
|
|
1251
|
+
declare const DurableObjectAlarmEvent: Schema.TaggedUnion<{
|
|
1252
|
+
readonly AlarmDue: Schema.TaggedStruct<"AlarmDue", {
|
|
1253
|
+
readonly id: Schema.NonEmptyString;
|
|
1254
|
+
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
1255
|
+
readonly scheduledAt: Schema.DateTimeUtc;
|
|
1256
|
+
readonly tag: Schema.NonEmptyString;
|
|
1257
|
+
}>;
|
|
1258
|
+
}>;
|
|
1259
|
+
type DurableObjectAlarmEvent = typeof DurableObjectAlarmEvent.Type;
|
|
1260
|
+
/**
|
|
1261
|
+
* Stable reference for a scheduled alarm.
|
|
1262
|
+
*
|
|
1263
|
+
* The pair of `tag` and `id` is the durable identity. Scheduling another alarm
|
|
1264
|
+
* with the same pair replaces the previous alarm, and cancellation uses the
|
|
1265
|
+
* same pair.
|
|
1266
|
+
*
|
|
1267
|
+
* @example Cancel an alarm
|
|
1268
|
+
* ```ts
|
|
1269
|
+
* yield* durableObjectAlarm.cancelAlarm({
|
|
1270
|
+
* tag: "connection-reconnect-grace",
|
|
1271
|
+
* id: "reconnect-grace-check",
|
|
1272
|
+
* });
|
|
1273
|
+
* ```
|
|
1274
|
+
*/
|
|
1275
|
+
type AlarmRef<Tag extends string = string> = {
|
|
1276
|
+
readonly id: string;
|
|
1277
|
+
readonly tag: Tag;
|
|
1278
|
+
};
|
|
1279
|
+
/**
|
|
1280
|
+
* Input for scheduling or replacing an alarm.
|
|
1281
|
+
*
|
|
1282
|
+
* `runAt` is the absolute first fire time. `repeatEvery` can be any Effect
|
|
1283
|
+
* `Duration.Input`, including strings like `"5 seconds"`, numbers interpreted
|
|
1284
|
+
* as milliseconds, or `Duration` values.
|
|
1285
|
+
*
|
|
1286
|
+
* @example One-shot alarm
|
|
1287
|
+
* ```ts
|
|
1288
|
+
* const now = yield* DateTime.now;
|
|
1289
|
+
*
|
|
1290
|
+
* yield* durableObjectAlarm.scheduleAlarm({
|
|
1291
|
+
* tag: "connection-reconnect-grace",
|
|
1292
|
+
* id: "reconnect-grace-check",
|
|
1293
|
+
* runAt: DateTime.add(now, { seconds: 15 }),
|
|
1294
|
+
* payload: { reason: "socket-closed" },
|
|
1295
|
+
* });
|
|
1296
|
+
* ```
|
|
1297
|
+
*
|
|
1298
|
+
* @example Repeating alarm
|
|
1299
|
+
* ```ts
|
|
1300
|
+
* const now = yield* DateTime.now;
|
|
1301
|
+
*
|
|
1302
|
+
* yield* durableObjectAlarm.scheduleAlarm({
|
|
1303
|
+
* tag: "heartbeat",
|
|
1304
|
+
* id: "heartbeat-check",
|
|
1305
|
+
* runAt: DateTime.add(now, { seconds: 5 }),
|
|
1306
|
+
* repeatEvery: "5 seconds",
|
|
1307
|
+
* payload: null,
|
|
1308
|
+
* });
|
|
1309
|
+
* ```
|
|
1310
|
+
*/
|
|
1311
|
+
type ScheduleAlarmInput<Tag extends string = string> = AlarmRef<Tag> & {
|
|
1312
|
+
readonly payload: AlarmPayload;
|
|
1313
|
+
readonly repeatEvery?: Duration.Input;
|
|
1314
|
+
readonly runAt: DateTime.Utc;
|
|
1315
|
+
};
|
|
1316
|
+
type ProcessDueAlarmsMode = "isolated" | "ordered";
|
|
1317
|
+
interface ProcessDueAlarmsFailure {
|
|
1318
|
+
readonly cause: unknown;
|
|
1319
|
+
readonly event?: DurableObjectAlarmEvent;
|
|
1320
|
+
readonly id: string;
|
|
1321
|
+
readonly storageId: string;
|
|
1322
|
+
readonly tag: string;
|
|
1323
|
+
}
|
|
1324
|
+
interface ProcessDueAlarmsResult {
|
|
1325
|
+
readonly failed: readonly ProcessDueAlarmsFailure[];
|
|
1326
|
+
readonly handled: readonly DurableObjectAlarmEvent[];
|
|
1327
|
+
}
|
|
1328
|
+
type ProcessDueAlarmsFailureAction = "ordered" | "retry" | "skip-and-advance-repeat" | {
|
|
1329
|
+
readonly mode: "ordered";
|
|
1330
|
+
} | {
|
|
1331
|
+
readonly mode: "retry";
|
|
1332
|
+
readonly retryFailedAfter?: Duration.Input;
|
|
1333
|
+
} | {
|
|
1334
|
+
readonly mode: "skip-and-advance-repeat";
|
|
1335
|
+
};
|
|
1336
|
+
interface ProcessDueAlarmsOptions<OnFailureR = never, OnFailureE = never> {
|
|
1337
|
+
/** Maximum due rows to load and process in one invocation. Defaults to 100. */
|
|
1338
|
+
readonly limit?: number;
|
|
1339
|
+
/**
|
|
1340
|
+
* Failure isolation mode. Defaults to `isolated` so one poison logical alarm
|
|
1341
|
+
* does not block unrelated maintenance work.
|
|
1342
|
+
*/
|
|
1343
|
+
readonly mode?: ProcessDueAlarmsMode;
|
|
1344
|
+
/**
|
|
1345
|
+
* Callback for logical handler/decode failures. Return a failure action to
|
|
1346
|
+
* override the global failure behavior for this row.
|
|
1347
|
+
*/
|
|
1348
|
+
readonly onFailure?: (failure: ProcessDueAlarmsFailure) => Effect.Effect<ProcessDueAlarmsFailureAction | void, OnFailureE, OnFailureR>;
|
|
1349
|
+
/**
|
|
1350
|
+
* Delay before retrying a failed logical row in isolated mode. Defaults to 30
|
|
1351
|
+
* seconds. Ignored by ordered mode unless supplied explicitly.
|
|
1352
|
+
*
|
|
1353
|
+
* Retrying updates only the selected occurrence, so replacements scheduled by
|
|
1354
|
+
* a long-running handler are not clobbered by failure handling.
|
|
1355
|
+
*/
|
|
1356
|
+
readonly retryFailedAfter?: Duration.Input;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Handler invoked for each due alarm before that alarm is acknowledged.
|
|
1360
|
+
*
|
|
1361
|
+
* If the handler fails, `DurableObjectAlarm` leaves the row due and fails the alarm
|
|
1362
|
+
* processing effect. Cloudflare can then retry the Durable Object alarm handler,
|
|
1363
|
+
* and the scheduler will find the same logical row again because `run_at` is
|
|
1364
|
+
* still less than or equal to the retry time.
|
|
1365
|
+
*/
|
|
1366
|
+
type ProcessDueAlarmsHandler<R = never, E = never> = (event: DurableObjectAlarmEvent) => Effect.Effect<void, E, R>;
|
|
1367
|
+
/**
|
|
1368
|
+
* Durable alarm scheduler API.
|
|
1369
|
+
*
|
|
1370
|
+
* This service only owns scheduling semantics. It does not publish, subscribe,
|
|
1371
|
+
* or dispatch events to consumers. Durable Object alarm handlers should pass a
|
|
1372
|
+
* domain handler to `processDueAlarms`; rows are acknowledged only after that
|
|
1373
|
+
* handler succeeds.
|
|
1374
|
+
*
|
|
1375
|
+
* This service must be the only code path that owns `storage.setAlarm()` inside
|
|
1376
|
+
* a Durable Object instance. Cloudflare exposes one platform alarm timestamp per
|
|
1377
|
+
* object, so another scheduler in the same object can clobber this service's
|
|
1378
|
+
* reconciled alarm timestamp.
|
|
1379
|
+
*
|
|
1380
|
+
* @example Durable Object alarm handler
|
|
1381
|
+
* ```ts
|
|
1382
|
+
* const onAlarm = Effect.gen(function* () {
|
|
1383
|
+
* const durableObjectAlarm = yield* DurableObjectAlarm;
|
|
1384
|
+
*
|
|
1385
|
+
* yield* durableObjectAlarm.processDueAlarms((event) =>
|
|
1386
|
+
* Effect.gen(function* () {
|
|
1387
|
+
* if (event.tag === "heartbeat") {
|
|
1388
|
+
* yield* heartbeatManager.handleAlarmEvent(event);
|
|
1389
|
+
* return;
|
|
1390
|
+
* }
|
|
1391
|
+
*
|
|
1392
|
+
* if (event.tag === "connection-reconnect-grace") {
|
|
1393
|
+
* yield* connectionManager.expireReconnectGracePeriods();
|
|
1394
|
+
* }
|
|
1395
|
+
* })
|
|
1396
|
+
* );
|
|
1397
|
+
* });
|
|
1398
|
+
* ```
|
|
1399
|
+
*/
|
|
1400
|
+
type AlarmScheduler = {
|
|
1401
|
+
/**
|
|
1402
|
+
* Cancel a scheduled alarm by `{ tag, id }`.
|
|
1403
|
+
*
|
|
1404
|
+
* This is idempotent. Cancelling a missing alarm is a no-op. The underlying
|
|
1405
|
+
* Durable Object alarm is reconciled afterward.
|
|
1406
|
+
*/
|
|
1407
|
+
readonly cancelAlarm: (input: AlarmRef) => Effect.Effect<void, InvalidAlarmRefError | StorageOperationError>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Process due alarms according to `Clock.currentTimeMillis`.
|
|
1410
|
+
*
|
|
1411
|
+
* Each row is acknowledged only after `handle` succeeds. One-shot alarms are
|
|
1412
|
+
* then deleted. Repeating alarms are rescheduled to `acknowledgedAt +
|
|
1413
|
+
* repeatEvery`, which intentionally behaves as delay-after-success rather
|
|
1414
|
+
* than fixed-cadence catch-up. Acknowledgements are conditional on the stored
|
|
1415
|
+
* row still matching the selected occurrence, so a handler that reschedules
|
|
1416
|
+
* the same `{ tag, id }` will not have its replacement clobbered by the old
|
|
1417
|
+
* acknowledgement.
|
|
1418
|
+
*
|
|
1419
|
+
* Processing is ordered by `runAt` and stable alarm id. By default, logical
|
|
1420
|
+
* handler/decode failures are isolated to the failing row: the row is retried
|
|
1421
|
+
* after `retryFailedAfter`, later due rows still run, and the result reports
|
|
1422
|
+
* both handled and failed rows. Use `mode: "ordered"` when a workflow
|
|
1423
|
+
* intentionally requires strict head-of-line blocking.
|
|
1424
|
+
*
|
|
1425
|
+
* Handlers must still be idempotent: Cloudflare alarms are at-least-once, and
|
|
1426
|
+
* if a handler succeeds but the acknowledgement write fails, the logical event
|
|
1427
|
+
* can be delivered again.
|
|
1428
|
+
*/
|
|
1429
|
+
readonly processDueAlarms: <R = never, E = never, OnFailureR = never, OnFailureE = never>(handle: ProcessDueAlarmsHandler<R, E>, options?: ProcessDueAlarmsOptions<OnFailureR, OnFailureE>) => Effect.Effect<ProcessDueAlarmsResult, E | OnFailureE | DurableObjectAlarmError, R | OnFailureR>;
|
|
1430
|
+
/**
|
|
1431
|
+
* Schedule or replace an alarm by `{ tag, id }`.
|
|
1432
|
+
*
|
|
1433
|
+
* The durable row write and underlying platform alarm reconciliation are run
|
|
1434
|
+
* in one Durable Object storage transaction, so a failed `setAlarm` rolls back
|
|
1435
|
+
* the logical schedule instead of leaving a stored-but-unarmed alarm.
|
|
1436
|
+
*/
|
|
1437
|
+
readonly scheduleAlarm: (input: ScheduleAlarmInput) => Effect.Effect<void, InvalidAlarmPayloadError | InvalidAlarmRefError | InvalidRepeatEveryError | StorageOperationError>;
|
|
1438
|
+
};
|
|
1439
|
+
declare const processDue: <R = never, E = never, OnFailureR = never, OnFailureE = never>(handle: ProcessDueAlarmsHandler<R, E>, options?: ProcessDueAlarmsOptions<OnFailureR, OnFailureE>) => Effect.Effect<ProcessDueAlarmsResult, E | OnFailureE | DurableObjectAlarmError, OnFailureR | R | DurableObjectAlarm>;
|
|
1440
|
+
declare const process: <R = never, E = never, OnFailureR = never, OnFailureE = never>(handle: ProcessDueAlarmsHandler<R, E>, options?: ProcessDueAlarmsOptions<OnFailureR, OnFailureE>) => Effect.Effect<ProcessDueAlarmsResult, E | OnFailureE | DurableObjectAlarmError, OnFailureR | R | DurableObjectAlarm>;
|
|
1441
|
+
type AlarmPayloadSchema = Schema.Codec<any, any, never, never>;
|
|
1442
|
+
type AlarmFailurePolicy = "ordered" | "retry" | "skip-and-advance-repeat";
|
|
1443
|
+
interface AlarmRetryPolicy {
|
|
1444
|
+
readonly initialDelay?: Duration.Input;
|
|
1445
|
+
}
|
|
1446
|
+
interface AlarmDefinitionConfig<Payload extends AlarmPayloadSchema = AlarmPayloadSchema> {
|
|
1447
|
+
readonly failure?: AlarmFailurePolicy;
|
|
1448
|
+
readonly payload: Payload;
|
|
1449
|
+
readonly retry?: AlarmRetryPolicy;
|
|
1450
|
+
}
|
|
1451
|
+
type AlarmDefinitionEntry = AlarmDefinitionConfig | AlarmPayloadSchema;
|
|
1452
|
+
type AlarmDefinitions = Readonly<Record<string, AlarmDefinitionEntry>>;
|
|
1453
|
+
type AlarmDefinitionSchema<Definition> = Definition extends AlarmDefinitionConfig<infer Payload> ? Payload : Definition;
|
|
1454
|
+
type AlarmDefinitionPayload<Definition> = AlarmDefinitionSchema<Definition> extends Schema.Codec<infer A, any, never, never> ? A : never;
|
|
1455
|
+
type DefinedAlarmEvent<Tag extends string, Payload> = Omit<DurableObjectAlarmEvent, "payload" | "tag"> & {
|
|
1456
|
+
readonly payload: Payload;
|
|
1457
|
+
readonly tag: Tag;
|
|
1458
|
+
};
|
|
1459
|
+
type DefinedAlarmHandlers<Definitions extends AlarmDefinitions, R = never, E = never> = { readonly [Tag in keyof Definitions & string]: (event: DefinedAlarmEvent<Tag, AlarmDefinitionPayload<Definitions[Tag]>>) => Effect.Effect<void, E, R> };
|
|
1460
|
+
declare const define: <const Definitions extends AlarmDefinitions>(definitions: Definitions) => {
|
|
1461
|
+
handlers: <R = never, E = never>(handlers: DefinedAlarmHandlers<Definitions, R, E>, options?: ProcessDueAlarmsOptions<R, E>) => Effect.Effect<ProcessDueAlarmsResult, E | DurableObjectAlarmError, R | DurableObjectAlarm>;
|
|
1462
|
+
};
|
|
1463
|
+
declare const DurableObjectAlarm_base: Context.ServiceClass<DurableObjectAlarm, "effect-cf/DurableObjectAlarm", AlarmScheduler>;
|
|
1464
|
+
/**
|
|
1465
|
+
* SQLite-backed Durable Object alarm scheduler.
|
|
1466
|
+
*
|
|
1467
|
+
* `DurableObjectAlarm` stores alarms in the current Durable Object's SQLite storage
|
|
1468
|
+
* and keeps the platform alarm set to the earliest pending scheduled alarm. The
|
|
1469
|
+
* platform alarm timestamp is only a wake-up hint; logical event identity comes
|
|
1470
|
+
* from persisted `{ tag, id }` rows.
|
|
1471
|
+
*
|
|
1472
|
+
* Retry safety depends on acknowledgement ordering. `processDueAlarms` runs the
|
|
1473
|
+
* caller's handler first and only then conditionally deletes or advances the
|
|
1474
|
+
* same selected row. By default, handler failures are isolated: the failed row
|
|
1475
|
+
* is retried after a delay, later due rows continue, and the platform alarm is
|
|
1476
|
+
* reconciled once at the end. Use `mode: "ordered"` for strict workflows where
|
|
1477
|
+
* one failed logical alarm should block later due alarms.
|
|
1478
|
+
*
|
|
1479
|
+
* Provide `DurableObjectAlarm.layer` anywhere `DurableObjectState` is available.
|
|
1480
|
+
*
|
|
1481
|
+
* @example Providing the service
|
|
1482
|
+
* ```ts
|
|
1483
|
+
* const program = Effect.gen(function* () {
|
|
1484
|
+
* const durableObjectAlarm = yield* DurableObjectAlarm;
|
|
1485
|
+
* const now = yield* DateTime.now;
|
|
1486
|
+
*
|
|
1487
|
+
* yield* durableObjectAlarm.scheduleAlarm({
|
|
1488
|
+
* tag: "heartbeat",
|
|
1489
|
+
* id: "heartbeat-check",
|
|
1490
|
+
* runAt: DateTime.add(now, { seconds: 5 }),
|
|
1491
|
+
* repeatEvery: Duration.seconds(5),
|
|
1492
|
+
* payload: null,
|
|
1493
|
+
* });
|
|
1494
|
+
* }).pipe(Effect.provide(DurableObjectAlarm.layer));
|
|
1495
|
+
* ```
|
|
1496
|
+
*
|
|
1497
|
+
* @example Processing due alarms
|
|
1498
|
+
* ```ts
|
|
1499
|
+
* const handledEvents = yield* durableObjectAlarm.processDueAlarms((event) =>
|
|
1500
|
+
* Effect.gen(function* () {
|
|
1501
|
+
* if (event.tag === "heartbeat") {
|
|
1502
|
+
* yield* heartbeatManager.handleAlarmEvent(event);
|
|
1503
|
+
* }
|
|
1504
|
+
* })
|
|
1505
|
+
* );
|
|
1506
|
+
* ```
|
|
1507
|
+
*/
|
|
1508
|
+
declare class DurableObjectAlarm extends DurableObjectAlarm_base {
|
|
1509
|
+
static readonly layer: Layer.Layer<DurableObjectAlarm, never, DurableObjectState>;
|
|
1510
|
+
}
|
|
1179
1511
|
declare namespace DurableObjectRpcWebSocket_d_exports {
|
|
1180
1512
|
export { DurableObjectRpcWebSocket, DurableObjectRpcWebSocketService, LayerOptions, NativeWebSocketMessage, layer };
|
|
1181
1513
|
}
|
|
@@ -1377,4 +1709,4 @@ declare const Service: <Self>() => <Id extends string, Key, Value, EncodedValue>
|
|
|
1377
1709
|
unsafeRaw: () => Effect.Effect<KVNamespace<string>, never, Self>;
|
|
1378
1710
|
};
|
|
1379
1711
|
//#endregion
|
|
1380
|
-
export { Binding_d_exports as Binding, DurableObject_d_exports as DurableObject, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Kv_d_exports as Kv, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, ServiceBinding_d_exports as ServiceBinding, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment };
|
|
1712
|
+
export { Binding_d_exports as Binding, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Kv_d_exports as Kv, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, ServiceBinding_d_exports as ServiceBinding, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment };
|