rivetkit 2.0.24-rc.1 → 2.0.25-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/schemas/actor-persist/v1.ts +6 -0
- package/dist/schemas/actor-persist/v2.ts +9 -3
- package/dist/schemas/actor-persist/v3.ts +280 -0
- package/dist/schemas/client-protocol/v1.ts +6 -0
- package/dist/schemas/client-protocol/v2.ts +438 -0
- package/dist/schemas/file-system-driver/v1.ts +6 -0
- package/dist/schemas/file-system-driver/v2.ts +142 -0
- package/dist/tsup/actor/errors.cjs +2 -4
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/actor/errors.d.cts +7 -10
- package/dist/tsup/actor/errors.d.ts +7 -10
- package/dist/tsup/actor/errors.js +9 -11
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.cts → actor-router-consts-DzI2szci.d.cts} +5 -9
- package/dist/tsup/{actor-router-consts-B3Lu87yJ.d.ts → actor-router-consts-DzI2szci.d.ts} +5 -9
- package/dist/tsup/{chunk-ZTH3KYFH.cjs → chunk-3FG5OJ3G.cjs} +3 -3
- package/dist/tsup/{chunk-ZTH3KYFH.cjs.map → chunk-3FG5OJ3G.cjs.map} +1 -1
- package/dist/tsup/{chunk-BLK27ES3.js → chunk-6JN6W6G3.js} +44 -56
- package/dist/tsup/chunk-6JN6W6G3.js.map +1 -0
- package/dist/tsup/chunk-7IBNNGQ2.js +514 -0
- package/dist/tsup/chunk-7IBNNGQ2.js.map +1 -0
- package/dist/tsup/{chunk-36JJ4IQB.cjs → chunk-AZATXPR4.cjs} +4 -8
- package/dist/tsup/chunk-AZATXPR4.cjs.map +1 -0
- package/dist/tsup/chunk-B7MENRD5.cjs +5694 -0
- package/dist/tsup/chunk-B7MENRD5.cjs.map +1 -0
- package/dist/tsup/{chunk-BOMZS2TJ.js → chunk-BBVFDEYD.js} +9 -9
- package/dist/tsup/chunk-BBVFDEYD.js.map +1 -0
- package/dist/tsup/{chunk-KSRXX3Z4.cjs → chunk-D6762AOA.cjs} +20 -25
- package/dist/tsup/chunk-D6762AOA.cjs.map +1 -0
- package/dist/tsup/{chunk-2JYPS5YM.cjs → chunk-E63WZNMR.cjs} +6 -6
- package/dist/tsup/chunk-E63WZNMR.cjs.map +1 -0
- package/dist/tsup/{chunk-YBG6R7LX.js → chunk-EDGN4OC7.js} +3 -7
- package/dist/tsup/chunk-EDGN4OC7.js.map +1 -0
- package/dist/tsup/{chunk-BYMKMOBS.js → chunk-FLOQ3UWM.js} +1844 -1681
- package/dist/tsup/chunk-FLOQ3UWM.js.map +1 -0
- package/dist/tsup/{chunk-7L65NNWP.cjs → chunk-H7GV5DIW.cjs} +187 -185
- package/dist/tsup/chunk-H7GV5DIW.cjs.map +1 -0
- package/dist/tsup/{chunk-227FEWMB.js → chunk-HZYZ7JSF.js} +3322 -2251
- package/dist/tsup/chunk-HZYZ7JSF.js.map +1 -0
- package/dist/tsup/{chunk-FX7TWFQR.js → chunk-IDJK7ILQ.js} +2 -6
- package/dist/tsup/chunk-IDJK7ILQ.js.map +1 -0
- package/dist/tsup/{chunk-VHGY7PU5.cjs → chunk-ILFXA4AL.cjs} +1900 -1737
- package/dist/tsup/chunk-ILFXA4AL.cjs.map +1 -0
- package/dist/tsup/chunk-MV6M3FDL.cjs +514 -0
- package/dist/tsup/chunk-MV6M3FDL.cjs.map +1 -0
- package/dist/tsup/{chunk-PLUN2NQT.js → chunk-NWBKMCWC.js} +189 -187
- package/dist/tsup/chunk-NWBKMCWC.js.map +1 -0
- package/dist/tsup/{chunk-CD33GT6Z.js → chunk-QIHBDXTO.js} +2 -2
- package/dist/tsup/{chunk-G64QUEDJ.js → chunk-W6RDS6NW.js} +23 -28
- package/dist/tsup/chunk-W6RDS6NW.js.map +1 -0
- package/dist/tsup/{chunk-INNFK746.cjs → chunk-WQU4M4ZC.cjs} +10 -14
- package/dist/tsup/chunk-WQU4M4ZC.cjs.map +1 -0
- package/dist/tsup/{chunk-SHVX2QUR.cjs → chunk-XKZA47XS.cjs} +17 -17
- package/dist/tsup/chunk-XKZA47XS.cjs.map +1 -0
- package/dist/tsup/{chunk-HHFKKVLR.cjs → chunk-YHWIOWVA.cjs} +45 -57
- package/dist/tsup/chunk-YHWIOWVA.cjs.map +1 -0
- package/dist/tsup/{chunk-YBHYXIP6.js → chunk-YVL6IRUM.js} +3 -3
- package/dist/tsup/chunk-YVL6IRUM.js.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +5 -7
- package/dist/tsup/client/mod.d.ts +5 -7
- package/dist/tsup/client/mod.js +8 -8
- package/dist/tsup/common/log.cjs +3 -3
- package/dist/tsup/common/log.js +2 -2
- package/dist/tsup/common/websocket.cjs +4 -4
- package/dist/tsup/common/websocket.js +3 -3
- package/dist/tsup/{conn-B3Vhbgnd.d.ts → config-BRDYDraU.d.cts} +1119 -1047
- package/dist/tsup/{conn-DJWL3nGx.d.cts → config-Bo-blHpJ.d.ts} +1119 -1047
- package/dist/tsup/driver-helpers/mod.cjs +5 -13
- package/dist/tsup/driver-helpers/mod.cjs.map +1 -1
- package/dist/tsup/driver-helpers/mod.d.cts +11 -9
- package/dist/tsup/driver-helpers/mod.d.ts +11 -9
- package/dist/tsup/driver-helpers/mod.js +14 -22
- package/dist/tsup/driver-test-suite/mod.cjs +474 -303
- package/dist/tsup/driver-test-suite/mod.cjs.map +1 -1
- package/dist/tsup/driver-test-suite/mod.d.cts +6 -9
- package/dist/tsup/driver-test-suite/mod.d.ts +6 -9
- package/dist/tsup/driver-test-suite/mod.js +1085 -914
- package/dist/tsup/driver-test-suite/mod.js.map +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.d.cts +5 -7
- package/dist/tsup/inspector/mod.d.ts +5 -7
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +10 -16
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +23 -25
- package/dist/tsup/mod.d.ts +23 -25
- package/dist/tsup/mod.js +17 -23
- package/dist/tsup/test/mod.cjs +11 -11
- package/dist/tsup/test/mod.d.cts +4 -6
- package/dist/tsup/test/mod.d.ts +4 -6
- package/dist/tsup/test/mod.js +10 -10
- package/dist/tsup/utils.cjs +3 -5
- package/dist/tsup/utils.cjs.map +1 -1
- package/dist/tsup/utils.d.cts +1 -2
- package/dist/tsup/utils.d.ts +1 -2
- package/dist/tsup/utils.js +2 -4
- package/package.json +13 -6
- package/src/actor/config.ts +56 -44
- package/src/actor/conn/driver.ts +61 -0
- package/src/actor/conn/drivers/http.ts +17 -0
- package/src/actor/conn/drivers/raw-request.ts +24 -0
- package/src/actor/conn/drivers/raw-websocket.ts +65 -0
- package/src/actor/conn/drivers/websocket.ts +129 -0
- package/src/actor/conn/mod.ts +232 -0
- package/src/actor/conn/persisted.ts +81 -0
- package/src/actor/conn/state-manager.ts +196 -0
- package/src/actor/contexts/action.ts +23 -0
- package/src/actor/{context.ts → contexts/actor.ts} +19 -8
- package/src/actor/contexts/conn-init.ts +31 -0
- package/src/actor/contexts/conn.ts +48 -0
- package/src/actor/contexts/create-conn-state.ts +13 -0
- package/src/actor/contexts/on-before-connect.ts +13 -0
- package/src/actor/contexts/on-connect.ts +22 -0
- package/src/actor/contexts/request.ts +48 -0
- package/src/actor/contexts/websocket.ts +48 -0
- package/src/actor/definition.ts +3 -3
- package/src/actor/driver.ts +36 -5
- package/src/actor/errors.ts +19 -24
- package/src/actor/instance/connection-manager.ts +465 -0
- package/src/actor/instance/event-manager.ts +292 -0
- package/src/actor/instance/kv.ts +15 -0
- package/src/actor/instance/mod.ts +1107 -0
- package/src/actor/instance/persisted.ts +67 -0
- package/src/actor/instance/schedule-manager.ts +349 -0
- package/src/actor/instance/state-manager.ts +502 -0
- package/src/actor/mod.ts +13 -16
- package/src/actor/protocol/old.ts +131 -43
- package/src/actor/protocol/serde.ts +19 -4
- package/src/actor/router-endpoints.ts +61 -586
- package/src/actor/router-websocket-endpoints.ts +408 -0
- package/src/actor/router.ts +63 -197
- package/src/actor/schedule.ts +1 -1
- package/src/client/actor-conn.ts +183 -249
- package/src/client/actor-handle.ts +29 -6
- package/src/client/client.ts +0 -4
- package/src/client/config.ts +1 -4
- package/src/client/mod.ts +0 -1
- package/src/client/raw-utils.ts +3 -3
- package/src/client/utils.ts +85 -39
- package/src/common/actor-router-consts.ts +5 -12
- package/src/common/{inline-websocket-adapter2.ts → inline-websocket-adapter.ts} +26 -48
- package/src/common/log.ts +1 -1
- package/src/common/router.ts +28 -17
- package/src/common/utils.ts +2 -0
- package/src/driver-helpers/mod.ts +7 -10
- package/src/driver-helpers/utils.ts +18 -9
- package/src/driver-test-suite/mod.ts +26 -50
- package/src/driver-test-suite/test-inline-client-driver.ts +27 -51
- package/src/driver-test-suite/tests/actor-conn-hibernation.ts +150 -0
- package/src/driver-test-suite/tests/actor-conn-state.ts +1 -4
- package/src/driver-test-suite/tests/actor-conn.ts +5 -9
- package/src/driver-test-suite/tests/actor-destroy.ts +294 -0
- package/src/driver-test-suite/tests/actor-driver.ts +0 -7
- package/src/driver-test-suite/tests/actor-handle.ts +12 -12
- package/src/driver-test-suite/tests/actor-metadata.ts +1 -1
- package/src/driver-test-suite/tests/manager-driver.ts +1 -1
- package/src/driver-test-suite/tests/raw-http-direct-registry.ts +8 -8
- package/src/driver-test-suite/tests/raw-http-request-properties.ts +6 -5
- package/src/driver-test-suite/tests/raw-http.ts +5 -5
- package/src/driver-test-suite/tests/raw-websocket-direct-registry.ts +7 -7
- package/src/driver-test-suite/tests/request-access.ts +4 -4
- package/src/driver-test-suite/utils.ts +6 -10
- package/src/drivers/engine/actor-driver.ts +614 -424
- package/src/drivers/engine/mod.ts +0 -1
- package/src/drivers/file-system/actor.ts +24 -12
- package/src/drivers/file-system/global-state.ts +427 -37
- package/src/drivers/file-system/manager.ts +71 -83
- package/src/drivers/file-system/mod.ts +3 -0
- package/src/drivers/file-system/utils.ts +18 -8
- package/src/engine-process/mod.ts +38 -38
- package/src/inspector/utils.ts +7 -5
- package/src/manager/driver.ts +11 -4
- package/src/manager/gateway.ts +4 -29
- package/src/manager/protocol/mod.ts +0 -2
- package/src/manager/protocol/query.ts +0 -4
- package/src/manager/router.ts +67 -64
- package/src/manager-api/actors.ts +13 -0
- package/src/mod.ts +1 -3
- package/src/registry/mod.ts +20 -20
- package/src/registry/serve.ts +9 -14
- package/src/remote-manager-driver/actor-websocket-client.ts +1 -16
- package/src/remote-manager-driver/api-endpoints.ts +13 -1
- package/src/remote-manager-driver/api-utils.ts +8 -0
- package/src/remote-manager-driver/metadata.ts +58 -0
- package/src/remote-manager-driver/mod.ts +47 -62
- package/src/remote-manager-driver/ws-proxy.ts +1 -1
- package/src/schemas/actor-persist/mod.ts +1 -1
- package/src/schemas/actor-persist/versioned.ts +56 -31
- package/src/schemas/client-protocol/mod.ts +1 -1
- package/src/schemas/client-protocol/versioned.ts +41 -21
- package/src/schemas/client-protocol-zod/mod.ts +103 -0
- package/src/schemas/file-system-driver/mod.ts +1 -1
- package/src/schemas/file-system-driver/versioned.ts +42 -19
- package/src/serde.ts +33 -11
- package/src/test/mod.ts +7 -3
- package/src/utils/node.ts +173 -0
- package/src/utils.ts +0 -4
- package/dist/tsup/chunk-227FEWMB.js.map +0 -1
- package/dist/tsup/chunk-2JYPS5YM.cjs.map +0 -1
- package/dist/tsup/chunk-36JJ4IQB.cjs.map +0 -1
- package/dist/tsup/chunk-7L65NNWP.cjs.map +0 -1
- package/dist/tsup/chunk-BLK27ES3.js.map +0 -1
- package/dist/tsup/chunk-BOMZS2TJ.js.map +0 -1
- package/dist/tsup/chunk-BYMKMOBS.js.map +0 -1
- package/dist/tsup/chunk-FX7TWFQR.js.map +0 -1
- package/dist/tsup/chunk-G64QUEDJ.js.map +0 -1
- package/dist/tsup/chunk-HHFKKVLR.cjs.map +0 -1
- package/dist/tsup/chunk-INNFK746.cjs.map +0 -1
- package/dist/tsup/chunk-KSRXX3Z4.cjs.map +0 -1
- package/dist/tsup/chunk-O44LFKSB.cjs +0 -4623
- package/dist/tsup/chunk-O44LFKSB.cjs.map +0 -1
- package/dist/tsup/chunk-PLUN2NQT.js.map +0 -1
- package/dist/tsup/chunk-S4UJG7ZE.js +0 -1119
- package/dist/tsup/chunk-S4UJG7ZE.js.map +0 -1
- package/dist/tsup/chunk-SHVX2QUR.cjs.map +0 -1
- package/dist/tsup/chunk-VFB23BYZ.cjs +0 -1119
- package/dist/tsup/chunk-VFB23BYZ.cjs.map +0 -1
- package/dist/tsup/chunk-VHGY7PU5.cjs.map +0 -1
- package/dist/tsup/chunk-YBG6R7LX.js.map +0 -1
- package/dist/tsup/chunk-YBHYXIP6.js.map +0 -1
- package/src/actor/action.ts +0 -178
- package/src/actor/conn-drivers.ts +0 -216
- package/src/actor/conn-socket.ts +0 -8
- package/src/actor/conn.ts +0 -272
- package/src/actor/instance.ts +0 -2336
- package/src/actor/persisted.ts +0 -49
- package/src/actor/unstable-react.ts +0 -110
- package/src/driver-test-suite/tests/actor-reconnect.ts +0 -170
- package/src/drivers/engine/kv.ts +0 -3
- package/src/manager/hono-websocket-adapter.ts +0 -393
- /package/dist/tsup/{chunk-CD33GT6Z.js.map → chunk-QIHBDXTO.js.map} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted data structures for actors.
|
|
3
|
+
*
|
|
4
|
+
* Keep this file in sync with the Connection section of rivetkit-typescript/packages/rivetkit/schemas/actor-persist/
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as cbor from "cbor-x";
|
|
8
|
+
import type * as persistSchema from "@/schemas/actor-persist/mod";
|
|
9
|
+
import { bufferToArrayBuffer } from "@/utils";
|
|
10
|
+
|
|
11
|
+
export type Cbor = ArrayBuffer;
|
|
12
|
+
|
|
13
|
+
// MARK: Schedule Event
|
|
14
|
+
/** Scheduled event to be executed at a specific timestamp */
|
|
15
|
+
export interface PersistedScheduleEvent {
|
|
16
|
+
eventId: string;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
action: string;
|
|
19
|
+
args?: Cbor;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// MARK: Actor
|
|
23
|
+
/** State object that gets automatically persisted to storage */
|
|
24
|
+
export interface PersistedActor<S, I> {
|
|
25
|
+
/** Input data passed to the actor on initialization */
|
|
26
|
+
input?: I;
|
|
27
|
+
hasInitialized: boolean;
|
|
28
|
+
state: S;
|
|
29
|
+
scheduledEvents: PersistedScheduleEvent[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function convertActorToBarePersisted<S, I>(
|
|
33
|
+
persist: PersistedActor<S, I>,
|
|
34
|
+
): persistSchema.Actor {
|
|
35
|
+
return {
|
|
36
|
+
input:
|
|
37
|
+
persist.input !== undefined
|
|
38
|
+
? bufferToArrayBuffer(cbor.encode(persist.input))
|
|
39
|
+
: null,
|
|
40
|
+
hasInitialized: persist.hasInitialized,
|
|
41
|
+
state: bufferToArrayBuffer(cbor.encode(persist.state)),
|
|
42
|
+
scheduledEvents: persist.scheduledEvents.map((event) => ({
|
|
43
|
+
eventId: event.eventId,
|
|
44
|
+
timestamp: BigInt(event.timestamp),
|
|
45
|
+
action: event.action,
|
|
46
|
+
args: event.args ?? null,
|
|
47
|
+
})),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function convertActorFromBarePersisted<S, I>(
|
|
52
|
+
bareData: persistSchema.Actor,
|
|
53
|
+
): PersistedActor<S, I> {
|
|
54
|
+
return {
|
|
55
|
+
input: bareData.input
|
|
56
|
+
? cbor.decode(new Uint8Array(bareData.input))
|
|
57
|
+
: undefined,
|
|
58
|
+
hasInitialized: bareData.hasInitialized,
|
|
59
|
+
state: cbor.decode(new Uint8Array(bareData.state)),
|
|
60
|
+
scheduledEvents: bareData.scheduledEvents.map((event) => ({
|
|
61
|
+
eventId: event.eventId,
|
|
62
|
+
timestamp: Number(event.timestamp),
|
|
63
|
+
action: event.action,
|
|
64
|
+
args: event.args ?? undefined,
|
|
65
|
+
})),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import * as cbor from "cbor-x";
|
|
2
|
+
import {
|
|
3
|
+
bufferToArrayBuffer,
|
|
4
|
+
SinglePromiseQueue,
|
|
5
|
+
stringifyError,
|
|
6
|
+
} from "@/utils";
|
|
7
|
+
import type { AnyDatabaseProvider } from "../database";
|
|
8
|
+
import type { ActorDriver } from "../driver";
|
|
9
|
+
import type { ActorInstance } from "./mod";
|
|
10
|
+
import type { PersistedScheduleEvent } from "./persisted";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Manages scheduled events and alarms for actor instances.
|
|
14
|
+
* Handles event scheduling, alarm triggers, and automatic event execution.
|
|
15
|
+
*/
|
|
16
|
+
export class ScheduleManager<S, CP, CS, V, I, DB extends AnyDatabaseProvider> {
|
|
17
|
+
#actor: ActorInstance<S, CP, CS, V, I, DB>;
|
|
18
|
+
#actorDriver: ActorDriver;
|
|
19
|
+
#alarmWriteQueue = new SinglePromiseQueue();
|
|
20
|
+
#config: any; // ActorConfig type
|
|
21
|
+
#persist: any; // Reference to PersistedActor
|
|
22
|
+
|
|
23
|
+
constructor(
|
|
24
|
+
actor: ActorInstance<S, CP, CS, V, I, DB>,
|
|
25
|
+
actorDriver: ActorDriver,
|
|
26
|
+
config: any,
|
|
27
|
+
) {
|
|
28
|
+
this.#actor = actor;
|
|
29
|
+
this.#actorDriver = actorDriver;
|
|
30
|
+
this.#config = config;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: - Public API
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sets the persist object reference.
|
|
37
|
+
* Called after StateManager initializes the persist proxy.
|
|
38
|
+
*/
|
|
39
|
+
setPersist(persist: any) {
|
|
40
|
+
this.#persist = persist;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Schedules an event to be executed at a specific timestamp.
|
|
45
|
+
*
|
|
46
|
+
* @param timestamp - Unix timestamp in milliseconds when the event should fire
|
|
47
|
+
* @param action - The name of the action to execute
|
|
48
|
+
* @param args - Arguments to pass to the action
|
|
49
|
+
*/
|
|
50
|
+
async scheduleEvent(
|
|
51
|
+
timestamp: number,
|
|
52
|
+
action: string,
|
|
53
|
+
args: unknown[],
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
const newEvent: PersistedScheduleEvent = {
|
|
56
|
+
eventId: crypto.randomUUID(),
|
|
57
|
+
timestamp,
|
|
58
|
+
action,
|
|
59
|
+
args: bufferToArrayBuffer(cbor.encode(args)),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
await this.#scheduleEventInner(newEvent);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Triggers any pending alarms that are due.
|
|
67
|
+
* This method is idempotent and safe to call multiple times.
|
|
68
|
+
*/
|
|
69
|
+
async onAlarm(): Promise<void> {
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
this.#actor.log.debug({
|
|
72
|
+
msg: "alarm triggered",
|
|
73
|
+
now,
|
|
74
|
+
events: this.#persist?.scheduledEvents?.length || 0,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!this.#persist?.scheduledEvents) {
|
|
78
|
+
this.#actor.rLog.debug({ msg: "no scheduled events" });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Find events that are due
|
|
83
|
+
const dueIndex = this.#persist.scheduledEvents.findIndex(
|
|
84
|
+
(x: PersistedScheduleEvent) => x.timestamp <= now,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (dueIndex === -1) {
|
|
88
|
+
// No events are due yet
|
|
89
|
+
this.#actor.rLog.debug({ msg: "no events are due yet" });
|
|
90
|
+
|
|
91
|
+
// Reschedule alarm for next event if any exist
|
|
92
|
+
if (this.#persist.scheduledEvents.length > 0) {
|
|
93
|
+
const nextTs = this.#persist.scheduledEvents[0].timestamp;
|
|
94
|
+
this.#actor.log.debug({
|
|
95
|
+
msg: "alarm fired early, rescheduling for next event",
|
|
96
|
+
now,
|
|
97
|
+
nextTs,
|
|
98
|
+
delta: nextTs - now,
|
|
99
|
+
});
|
|
100
|
+
await this.#queueSetAlarm(nextTs);
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Remove and process due events
|
|
106
|
+
const dueEvents = this.#persist.scheduledEvents.splice(0, dueIndex + 1);
|
|
107
|
+
this.#actor.log.debug({
|
|
108
|
+
msg: "running events",
|
|
109
|
+
count: dueEvents.length,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Schedule next alarm if more events remain
|
|
113
|
+
if (this.#persist.scheduledEvents.length > 0) {
|
|
114
|
+
const nextTs = this.#persist.scheduledEvents[0].timestamp;
|
|
115
|
+
this.#actor.log.info({
|
|
116
|
+
msg: "setting next alarm",
|
|
117
|
+
nextTs,
|
|
118
|
+
remainingEvents: this.#persist.scheduledEvents.length,
|
|
119
|
+
});
|
|
120
|
+
await this.#queueSetAlarm(nextTs);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Execute due events
|
|
124
|
+
await this.#executeDueEvents(dueEvents);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Initializes alarms on actor startup.
|
|
129
|
+
* Sets the alarm for the next scheduled event if any exist.
|
|
130
|
+
*/
|
|
131
|
+
async initializeAlarms(): Promise<void> {
|
|
132
|
+
if (this.#persist?.scheduledEvents?.length > 0) {
|
|
133
|
+
await this.#queueSetAlarm(
|
|
134
|
+
this.#persist.scheduledEvents[0].timestamp,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Waits for any pending alarm write operations to complete.
|
|
141
|
+
*/
|
|
142
|
+
async waitForPendingAlarmWrites(): Promise<void> {
|
|
143
|
+
if (this.#alarmWriteQueue.runningDrainLoop) {
|
|
144
|
+
await this.#alarmWriteQueue.runningDrainLoop;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Gets statistics about scheduled events.
|
|
150
|
+
*/
|
|
151
|
+
getScheduleStats(): {
|
|
152
|
+
totalEvents: number;
|
|
153
|
+
nextEventTime: number | null;
|
|
154
|
+
overdueCount: number;
|
|
155
|
+
} {
|
|
156
|
+
if (!this.#persist?.scheduledEvents) {
|
|
157
|
+
return {
|
|
158
|
+
totalEvents: 0,
|
|
159
|
+
nextEventTime: null,
|
|
160
|
+
overdueCount: 0,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const now = Date.now();
|
|
165
|
+
const events = this.#persist.scheduledEvents;
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
totalEvents: events.length,
|
|
169
|
+
nextEventTime: events.length > 0 ? events[0].timestamp : null,
|
|
170
|
+
overdueCount: events.filter(
|
|
171
|
+
(e: PersistedScheduleEvent) => e.timestamp <= now,
|
|
172
|
+
).length,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Cancels a scheduled event by its ID.
|
|
178
|
+
*
|
|
179
|
+
* @param eventId - The ID of the event to cancel
|
|
180
|
+
* @returns True if the event was found and cancelled
|
|
181
|
+
*/
|
|
182
|
+
async cancelEvent(eventId: string): Promise<boolean> {
|
|
183
|
+
if (!this.#persist?.scheduledEvents) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const index = this.#persist.scheduledEvents.findIndex(
|
|
188
|
+
(e: PersistedScheduleEvent) => e.eventId === eventId,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (index === -1) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Remove the event
|
|
196
|
+
const wasFirst = index === 0;
|
|
197
|
+
this.#persist.scheduledEvents.splice(index, 1);
|
|
198
|
+
|
|
199
|
+
// If we removed the first event, update the alarm
|
|
200
|
+
if (wasFirst && this.#persist.scheduledEvents.length > 0) {
|
|
201
|
+
await this.#queueSetAlarm(
|
|
202
|
+
this.#persist.scheduledEvents[0].timestamp,
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.#actor.log.info({
|
|
207
|
+
msg: "cancelled scheduled event",
|
|
208
|
+
eventId,
|
|
209
|
+
remainingEvents: this.#persist.scheduledEvents.length,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// MARK: - Private Helpers
|
|
216
|
+
|
|
217
|
+
async #scheduleEventInner(newEvent: PersistedScheduleEvent): Promise<void> {
|
|
218
|
+
this.#actor.log.info({
|
|
219
|
+
msg: "scheduling event",
|
|
220
|
+
eventId: newEvent.eventId,
|
|
221
|
+
timestamp: newEvent.timestamp,
|
|
222
|
+
action: newEvent.action,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (!this.#persist?.scheduledEvents) {
|
|
226
|
+
throw new Error("Persist not initialized");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Find insertion point (events are sorted by timestamp)
|
|
230
|
+
const insertIndex = this.#persist.scheduledEvents.findIndex(
|
|
231
|
+
(x: PersistedScheduleEvent) => x.timestamp > newEvent.timestamp,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
if (insertIndex === -1) {
|
|
235
|
+
// Add to end
|
|
236
|
+
this.#persist.scheduledEvents.push(newEvent);
|
|
237
|
+
} else {
|
|
238
|
+
// Insert at correct position
|
|
239
|
+
this.#persist.scheduledEvents.splice(insertIndex, 0, newEvent);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Update alarm if this is the newest event
|
|
243
|
+
if (insertIndex === 0 || this.#persist.scheduledEvents.length === 1) {
|
|
244
|
+
this.#actor.log.info({
|
|
245
|
+
msg: "setting alarm for new event",
|
|
246
|
+
timestamp: newEvent.timestamp,
|
|
247
|
+
eventCount: this.#persist.scheduledEvents.length,
|
|
248
|
+
});
|
|
249
|
+
await this.#queueSetAlarm(newEvent.timestamp);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async #executeDueEvents(events: PersistedScheduleEvent[]): Promise<void> {
|
|
254
|
+
for (const event of events) {
|
|
255
|
+
try {
|
|
256
|
+
this.#actor.log.info({
|
|
257
|
+
msg: "executing scheduled event",
|
|
258
|
+
eventId: event.eventId,
|
|
259
|
+
timestamp: event.timestamp,
|
|
260
|
+
action: event.action,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Look up the action function
|
|
264
|
+
const fn = this.#config.actions[event.action];
|
|
265
|
+
|
|
266
|
+
if (!fn) {
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Missing action for scheduled event: ${event.action}`,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (typeof fn !== "function") {
|
|
273
|
+
throw new Error(
|
|
274
|
+
`Scheduled event action ${event.action} is not a function (got ${typeof fn})`,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Decode arguments and execute
|
|
279
|
+
const args = event.args
|
|
280
|
+
? cbor.decode(new Uint8Array(event.args))
|
|
281
|
+
: [];
|
|
282
|
+
|
|
283
|
+
const result = fn.call(
|
|
284
|
+
undefined,
|
|
285
|
+
this.#actor.actorContext,
|
|
286
|
+
...args,
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Handle async actions
|
|
290
|
+
if (result instanceof Promise) {
|
|
291
|
+
await result;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
this.#actor.log.debug({
|
|
295
|
+
msg: "scheduled event completed",
|
|
296
|
+
eventId: event.eventId,
|
|
297
|
+
action: event.action,
|
|
298
|
+
});
|
|
299
|
+
} catch (error) {
|
|
300
|
+
this.#actor.log.error({
|
|
301
|
+
msg: "error executing scheduled event",
|
|
302
|
+
error: stringifyError(error),
|
|
303
|
+
eventId: event.eventId,
|
|
304
|
+
timestamp: event.timestamp,
|
|
305
|
+
action: event.action,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Continue processing other events even if one fails
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async #queueSetAlarm(timestamp: number): Promise<void> {
|
|
314
|
+
await this.#alarmWriteQueue.enqueue(async () => {
|
|
315
|
+
await this.#actorDriver.setAlarm(this.#actor, timestamp);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Gets the next scheduled event, if any.
|
|
321
|
+
*/
|
|
322
|
+
getNextEvent(): PersistedScheduleEvent | null {
|
|
323
|
+
if (
|
|
324
|
+
!this.#persist?.scheduledEvents ||
|
|
325
|
+
this.#persist.scheduledEvents.length === 0
|
|
326
|
+
) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return this.#persist.scheduledEvents[0];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Gets all scheduled events.
|
|
334
|
+
*/
|
|
335
|
+
getAllEvents(): PersistedScheduleEvent[] {
|
|
336
|
+
return this.#persist?.scheduledEvents || [];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Clears all scheduled events.
|
|
341
|
+
* Use with caution - this removes all pending scheduled events.
|
|
342
|
+
*/
|
|
343
|
+
clearAllEvents(): void {
|
|
344
|
+
if (this.#persist?.scheduledEvents) {
|
|
345
|
+
this.#persist.scheduledEvents = [];
|
|
346
|
+
this.#actor.log.warn({ msg: "cleared all scheduled events" });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|