rivetkit 2.3.11-rc.9 → 2.3.11

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.
Files changed (152) hide show
  1. package/dist/browser/client.d.ts +96 -14
  2. package/dist/browser/client.js +12 -2
  3. package/dist/browser/client.js.map +1 -1
  4. package/dist/browser/inspector/client.d.ts +139 -139
  5. package/dist/browser/inspector/client.js +12 -2
  6. package/dist/browser/inspector/client.js.map +1 -1
  7. package/dist/tsup/agent-os/index.cjs +146 -76
  8. package/dist/tsup/agent-os/index.cjs.map +1 -1
  9. package/dist/tsup/agent-os/index.d.cts +83 -9
  10. package/dist/tsup/agent-os/index.d.ts +83 -9
  11. package/dist/tsup/agent-os/index.js +146 -76
  12. package/dist/tsup/agent-os/index.js.map +1 -1
  13. package/dist/tsup/{chunk-HOZ3ZLZ2.js → chunk-3ZR542SK.js} +6 -6
  14. package/dist/tsup/chunk-3ZR542SK.js.map +1 -0
  15. package/dist/tsup/{chunk-HNHLBM3Y.cjs → chunk-4KGXFC6M.cjs} +42 -5
  16. package/dist/tsup/chunk-4KGXFC6M.cjs.map +1 -0
  17. package/dist/tsup/chunk-5UYWLCRO.js +14 -0
  18. package/dist/tsup/chunk-5UYWLCRO.js.map +1 -0
  19. package/dist/tsup/{chunk-QWLJCP3X.js → chunk-6W5VGLFT.js} +42 -5
  20. package/dist/tsup/chunk-6W5VGLFT.js.map +1 -0
  21. package/dist/tsup/chunk-6WYATRS4.js +243 -0
  22. package/dist/tsup/chunk-6WYATRS4.js.map +1 -0
  23. package/dist/tsup/chunk-B3W74FBB.cjs +14 -0
  24. package/dist/tsup/chunk-B3W74FBB.cjs.map +1 -0
  25. package/dist/tsup/{chunk-XOOESJY7.js → chunk-FWROC5LA.js} +2 -2
  26. package/dist/tsup/{chunk-V5CVATRU.cjs → chunk-GLIPKKEC.cjs} +7 -7
  27. package/dist/tsup/{chunk-V5CVATRU.cjs.map → chunk-GLIPKKEC.cjs.map} +1 -1
  28. package/dist/tsup/{chunk-T5XVTVQY.cjs → chunk-GN2EUMEW.cjs} +3 -3
  29. package/dist/tsup/{chunk-T5XVTVQY.cjs.map → chunk-GN2EUMEW.cjs.map} +1 -1
  30. package/dist/tsup/{chunk-T56IZ42M.cjs → chunk-GU7ML3KE.cjs} +13 -3
  31. package/dist/tsup/chunk-GU7ML3KE.cjs.map +1 -0
  32. package/dist/tsup/{chunk-IOLZ3ONU.js → chunk-JQ2AZ5FN.js} +3 -3
  33. package/dist/tsup/{chunk-7AFZMFPQ.js → chunk-JTHHCZCZ.js} +10 -1
  34. package/dist/tsup/chunk-JTHHCZCZ.js.map +1 -0
  35. package/dist/tsup/{chunk-UEULFXUI.cjs → chunk-KVFIONKQ.cjs} +11 -2
  36. package/dist/tsup/chunk-KVFIONKQ.cjs.map +1 -0
  37. package/dist/tsup/chunk-LWQFXGKS.cjs +243 -0
  38. package/dist/tsup/chunk-LWQFXGKS.cjs.map +1 -0
  39. package/dist/tsup/{chunk-QVCO6DTZ.js → chunk-MO2SSUOR.js} +8 -7
  40. package/dist/tsup/chunk-MO2SSUOR.js.map +1 -0
  41. package/dist/tsup/{chunk-LZ4TSBIP.js → chunk-OUENKQHB.js} +13 -3
  42. package/dist/tsup/chunk-OUENKQHB.js.map +1 -0
  43. package/dist/tsup/{chunk-6SBPWTXJ.cjs → chunk-PUZY2XCD.cjs} +10 -9
  44. package/dist/tsup/chunk-PUZY2XCD.cjs.map +1 -0
  45. package/dist/tsup/{chunk-C5TORAVP.js → chunk-QFJ24MBU.js} +27 -2
  46. package/dist/tsup/{chunk-C5TORAVP.js.map → chunk-QFJ24MBU.js.map} +1 -1
  47. package/dist/tsup/{chunk-BZWKY6WO.cjs → chunk-RXXDNJXB.cjs} +140 -140
  48. package/dist/tsup/{chunk-BZWKY6WO.cjs.map → chunk-RXXDNJXB.cjs.map} +1 -1
  49. package/dist/tsup/{chunk-IMRDHZM2.cjs → chunk-VBJQIBXI.cjs} +30 -5
  50. package/dist/tsup/chunk-VBJQIBXI.cjs.map +1 -0
  51. package/dist/tsup/client/mod.cjs +6 -6
  52. package/dist/tsup/client/mod.d.cts +4 -3
  53. package/dist/tsup/client/mod.d.ts +4 -3
  54. package/dist/tsup/client/mod.js +5 -5
  55. package/dist/tsup/common/log.cjs +2 -2
  56. package/dist/tsup/common/log.js +1 -1
  57. package/dist/tsup/common/websocket.cjs +3 -3
  58. package/dist/tsup/common/websocket.js +2 -2
  59. package/dist/tsup/{config-CZcjyT5B.d.ts → config-C4OSv0QP.d.ts} +92 -7
  60. package/dist/tsup/{config-rGWrumlO.d.cts → config-DTGoWdkG.d.cts} +92 -7
  61. package/dist/tsup/{config-BxOimoGX.d.cts → config-ey2I2zz5.d.cts} +46 -6
  62. package/dist/tsup/{config-BxOimoGX.d.ts → config-ey2I2zz5.d.ts} +46 -6
  63. package/dist/tsup/{context-B8W8E8nW.d.cts → context-C6cvDNyk.d.cts} +2 -2
  64. package/dist/tsup/{context-_UrDZhEh.d.ts → context-CMygeBxS.d.ts} +2 -2
  65. package/dist/tsup/db/drizzle.cjs +18 -10
  66. package/dist/tsup/db/drizzle.cjs.map +1 -1
  67. package/dist/tsup/db/drizzle.d.cts +5 -6
  68. package/dist/tsup/db/drizzle.d.ts +5 -6
  69. package/dist/tsup/db/drizzle.js +13 -5
  70. package/dist/tsup/db/drizzle.js.map +1 -1
  71. package/dist/tsup/db/mod.cjs +5 -175
  72. package/dist/tsup/db/mod.cjs.map +1 -1
  73. package/dist/tsup/db/mod.d.cts +11 -4
  74. package/dist/tsup/db/mod.d.ts +11 -4
  75. package/dist/tsup/db/mod.js +4 -174
  76. package/dist/tsup/db/mod.js.map +1 -1
  77. package/dist/tsup/dynamic/mod.cjs +2 -2
  78. package/dist/tsup/dynamic/mod.d.cts +3 -2
  79. package/dist/tsup/dynamic/mod.d.ts +3 -2
  80. package/dist/tsup/dynamic/mod.js +1 -1
  81. package/dist/tsup/inspector/mod.cjs +5 -4
  82. package/dist/tsup/inspector/mod.cjs.map +1 -1
  83. package/dist/tsup/inspector/mod.d.cts +12 -146
  84. package/dist/tsup/inspector/mod.d.ts +12 -146
  85. package/dist/tsup/inspector/mod.js +4 -3
  86. package/dist/tsup/inspector/mod.js.map +1 -1
  87. package/dist/tsup/inspector/workflow.cjs +21 -0
  88. package/dist/tsup/inspector/workflow.cjs.map +1 -0
  89. package/dist/tsup/inspector/workflow.d.cts +144 -0
  90. package/dist/tsup/inspector/workflow.d.ts +144 -0
  91. package/dist/tsup/inspector/workflow.js +21 -0
  92. package/dist/tsup/inspector/workflow.js.map +1 -0
  93. package/dist/tsup/inspector-tab/mod.cjs +2 -2
  94. package/dist/tsup/inspector-tab/mod.d.cts +4 -3
  95. package/dist/tsup/inspector-tab/mod.d.ts +4 -3
  96. package/dist/tsup/inspector-tab/mod.js +1 -1
  97. package/dist/tsup/mod.cjs +743 -247
  98. package/dist/tsup/mod.cjs.map +1 -1
  99. package/dist/tsup/mod.d.cts +6 -5
  100. package/dist/tsup/mod.d.ts +6 -5
  101. package/dist/tsup/mod.js +670 -174
  102. package/dist/tsup/mod.js.map +1 -1
  103. package/dist/tsup/test/mod.cjs +7 -7
  104. package/dist/tsup/test/mod.d.cts +3 -2
  105. package/dist/tsup/test/mod.d.ts +3 -2
  106. package/dist/tsup/test/mod.js +5 -5
  107. package/dist/tsup/unstable/migrations.d.cts +1 -1
  108. package/dist/tsup/unstable/migrations.d.ts +1 -1
  109. package/dist/tsup/utils.cjs +2 -2
  110. package/dist/tsup/utils.js +1 -1
  111. package/dist/tsup/workflow/mod.cjs +89 -95
  112. package/dist/tsup/workflow/mod.cjs.map +1 -1
  113. package/dist/tsup/workflow/mod.d.cts +5 -4
  114. package/dist/tsup/workflow/mod.d.ts +5 -4
  115. package/dist/tsup/workflow/mod.js +86 -92
  116. package/dist/tsup/workflow/mod.js.map +1 -1
  117. package/package.json +19 -9
  118. package/src/actor/config.test.ts +65 -1
  119. package/src/actor/config.ts +169 -5
  120. package/src/actor/mod.ts +10 -1
  121. package/src/client/actor-common.ts +5 -14
  122. package/src/common/database/config.ts +50 -3
  123. package/src/common/database/mod.test.ts +142 -4
  124. package/src/common/database/mod.ts +210 -98
  125. package/src/common/database/native-database.ts +42 -3
  126. package/src/common/database/shared.ts +9 -0
  127. package/src/db/drizzle.test.ts +8 -1
  128. package/src/db/drizzle.ts +15 -4
  129. package/src/db/mod.ts +3 -0
  130. package/src/inspector/workflow.ts +61 -0
  131. package/src/registry/napi-runtime.ts +69 -1
  132. package/src/registry/native.ts +474 -161
  133. package/src/registry/run-handler-coordinator.test.ts +252 -0
  134. package/src/registry/run-handler-coordinator.ts +219 -0
  135. package/src/registry/runtime.ts +34 -1
  136. package/src/registry/wasm-runtime.ts +122 -17
  137. package/src/utils/node.ts +44 -28
  138. package/src/workflow/driver.ts +56 -58
  139. package/src/workflow/inspector.ts +10 -15
  140. package/src/workflow/mod.ts +48 -70
  141. package/dist/tsup/chunk-6SBPWTXJ.cjs.map +0 -1
  142. package/dist/tsup/chunk-7AFZMFPQ.js.map +0 -1
  143. package/dist/tsup/chunk-HNHLBM3Y.cjs.map +0 -1
  144. package/dist/tsup/chunk-HOZ3ZLZ2.js.map +0 -1
  145. package/dist/tsup/chunk-IMRDHZM2.cjs.map +0 -1
  146. package/dist/tsup/chunk-LZ4TSBIP.js.map +0 -1
  147. package/dist/tsup/chunk-QVCO6DTZ.js.map +0 -1
  148. package/dist/tsup/chunk-QWLJCP3X.js.map +0 -1
  149. package/dist/tsup/chunk-T56IZ42M.cjs.map +0 -1
  150. package/dist/tsup/chunk-UEULFXUI.cjs.map +0 -1
  151. /package/dist/tsup/{chunk-XOOESJY7.js.map → chunk-FWROC5LA.js.map} +0 -0
  152. /package/dist/tsup/{chunk-IOLZ3ONU.js.map → chunk-JQ2AZ5FN.js.map} +0 -0
@@ -0,0 +1,252 @@
1
+ import { describe, expect, test, vi } from "vitest";
2
+ import { defineRunHandler, type RunControl } from "@/actor/config";
3
+ import { RunHandlerCoordinator } from "./run-handler-coordinator";
4
+
5
+ class Deferred<T = void> {
6
+ readonly promise: Promise<T>;
7
+ resolve!: (value: T | PromiseLike<T>) => void;
8
+ reject!: (reason?: unknown) => void;
9
+
10
+ constructor() {
11
+ this.promise = new Promise<T>((resolve, reject) => {
12
+ this.resolve = resolve;
13
+ this.reject = reject;
14
+ });
15
+ }
16
+ }
17
+
18
+ function createCoordinator() {
19
+ let control: RunControl | undefined;
20
+ const dispose = vi.fn();
21
+ const createInspector = vi.fn((context: { control: RunControl }) => {
22
+ control = context.control;
23
+ return {
24
+ inspector: {
25
+ workflow: {
26
+ getHistory: () => null,
27
+ getState: async () => null,
28
+ onHistoryUpdated: () => () => {},
29
+ replayFromStep: async () => null,
30
+ },
31
+ },
32
+ dispose,
33
+ };
34
+ });
35
+ const run = defineRunHandler(async () => {}, {
36
+ inspectorKind: "workflow",
37
+ createInspector,
38
+ });
39
+ const coordinator = new RunHandlerCoordinator(run);
40
+ const restart = vi.fn();
41
+ const inspector = coordinator.getInspector("actor-1", restart);
42
+
43
+ return {
44
+ control: control as unknown as RunControl,
45
+ coordinator,
46
+ createInspector,
47
+ dispose,
48
+ inspector,
49
+ restart,
50
+ };
51
+ }
52
+
53
+ describe("RunHandlerCoordinator", () => {
54
+ test("fails explicitly when a JavaScript factory omits required workflow controls", async () => {
55
+ const coordinator = new RunHandlerCoordinator(
56
+ defineRunHandler(async () => {}, {
57
+ inspectorKind: "workflow",
58
+ createInspector: (() => ({
59
+ inspector: { workflow: { getHistory: () => null } },
60
+ })) as never,
61
+ }),
62
+ );
63
+
64
+ await expect(
65
+ coordinator.run(
66
+ "actor-1",
67
+ () => {},
68
+ async () => {},
69
+ ),
70
+ ).rejects.toThrow(
71
+ "createInspector returned an invalid workflow adapter for actor actor-1",
72
+ );
73
+ });
74
+
75
+ test("initializes and disposes Inspector state even when it is never queried", async () => {
76
+ const dispose = vi.fn();
77
+ const createInspector = vi.fn(() => ({
78
+ inspector: {
79
+ workflow: {
80
+ getHistory: () => null,
81
+ getState: async () => null,
82
+ onHistoryUpdated: () => () => {},
83
+ replayFromStep: async () => null,
84
+ },
85
+ },
86
+ dispose,
87
+ }));
88
+ const coordinator = new RunHandlerCoordinator(
89
+ defineRunHandler(async () => {}, {
90
+ inspectorKind: "workflow",
91
+ createInspector,
92
+ }),
93
+ );
94
+
95
+ await coordinator.run(
96
+ "actor-1",
97
+ () => {},
98
+ async () => {},
99
+ );
100
+ expect(createInspector).toHaveBeenCalledOnce();
101
+ coordinator.destroy("actor-1");
102
+ expect(dispose).toHaveBeenCalledOnce();
103
+ });
104
+
105
+ test("creates one inspector per live actor and disposes it exactly once", () => {
106
+ const subject = createCoordinator();
107
+ expect(
108
+ subject.coordinator.getInspector("actor-1", subject.restart),
109
+ ).toBe(subject.inspector);
110
+ expect(subject.createInspector).toHaveBeenCalledOnce();
111
+
112
+ subject.coordinator.destroy("actor-1");
113
+ subject.coordinator.destroy("actor-1");
114
+ expect(subject.dispose).toHaveBeenCalledOnce();
115
+
116
+ const recreated = subject.coordinator.getInspector(
117
+ "actor-1",
118
+ subject.restart,
119
+ );
120
+ expect(recreated).not.toBe(subject.inspector);
121
+ expect(subject.createInspector).toHaveBeenCalledTimes(2);
122
+ });
123
+
124
+ test("rejects replay while a run is active", async () => {
125
+ const subject = createCoordinator();
126
+ const active = new Deferred();
127
+ const started = new Deferred();
128
+ const running = subject.coordinator.run(
129
+ "actor-1",
130
+ subject.restart,
131
+ async () => {
132
+ started.resolve();
133
+ await active.promise;
134
+ },
135
+ );
136
+ await started.promise;
137
+
138
+ await expect(
139
+ subject.control.run.withInactive(
140
+ { restartOnSuccess: true },
141
+ async () => {},
142
+ ),
143
+ ).rejects.toMatchObject({
144
+ group: "actor",
145
+ code: "run_handler_unavailable",
146
+ });
147
+
148
+ active.resolve();
149
+ await running;
150
+ });
151
+
152
+ test("uses a start queued during replay as the single restart", async () => {
153
+ const subject = createCoordinator();
154
+ const replay = new Deferred();
155
+ const replayStarted = new Deferred();
156
+ const events: string[] = [];
157
+ subject.restart.mockImplementation(() => {
158
+ events.push("restart");
159
+ });
160
+
161
+ const exclusive = subject.control.run.withInactive(
162
+ { restartOnSuccess: true },
163
+ async () => {
164
+ events.push("replay");
165
+ replayStarted.resolve();
166
+ await replay.promise;
167
+ },
168
+ );
169
+ await replayStarted.promise;
170
+
171
+ const queued = subject.coordinator.run(
172
+ "actor-1",
173
+ subject.restart,
174
+ async () => {
175
+ events.push("run");
176
+ },
177
+ );
178
+ await Promise.resolve();
179
+ expect(events).toEqual(["replay"]);
180
+
181
+ replay.resolve();
182
+ await exclusive;
183
+ await queued;
184
+ expect(events).toEqual(["replay", "run"]);
185
+ expect(subject.restart).not.toHaveBeenCalled();
186
+ });
187
+
188
+ test("drops starts queued behind a failed replay and leaves the run inactive", async () => {
189
+ const subject = createCoordinator();
190
+ const replay = new Deferred();
191
+ const replayStarted = new Deferred();
192
+ const run = vi.fn();
193
+ const expected = new Error("rewrite failed");
194
+
195
+ const exclusive = subject.control.run.withInactive(
196
+ { restartOnSuccess: true },
197
+ async () => {
198
+ replayStarted.resolve();
199
+ await replay.promise;
200
+ throw expected;
201
+ },
202
+ );
203
+ await replayStarted.promise;
204
+ const queued = subject.coordinator.run("actor-1", subject.restart, run);
205
+
206
+ replay.resolve();
207
+ await expect(exclusive).rejects.toBe(expected);
208
+ await expect(queued).resolves.toBe("suppressed");
209
+ expect(run).not.toHaveBeenCalled();
210
+ expect(subject.restart).not.toHaveBeenCalled();
211
+ });
212
+
213
+ test("restores a durable wake suppressed by a failed replay", async () => {
214
+ const subject = createCoordinator();
215
+ const replay = new Deferred();
216
+ const replayStarted = new Deferred();
217
+ const run = vi.fn();
218
+
219
+ const exclusive = subject.control.run.withInactive({}, async () => {
220
+ replayStarted.resolve();
221
+ await replay.promise;
222
+ throw new Error("rewrite failed");
223
+ });
224
+ await replayStarted.promise;
225
+ const queued = subject.coordinator.run("actor-1", subject.restart, run);
226
+
227
+ replay.resolve();
228
+ await expect(exclusive).rejects.toThrow("rewrite failed");
229
+ await expect(queued).resolves.toBe("suppressed");
230
+ expect(run).not.toHaveBeenCalled();
231
+ });
232
+
233
+ test("allows only one concurrent replay and closes old controls on destroy", async () => {
234
+ const subject = createCoordinator();
235
+ const replay = new Deferred();
236
+ const first = subject.control.run.withInactive({}, async () => {
237
+ await replay.promise;
238
+ });
239
+
240
+ await expect(
241
+ subject.control.run.withInactive({}, async () => {}),
242
+ ).rejects.toMatchObject({ code: "run_handler_unavailable" });
243
+ subject.coordinator.destroy("actor-1");
244
+ replay.resolve();
245
+ await expect(first).rejects.toMatchObject({
246
+ code: "run_handler_unavailable",
247
+ });
248
+ await expect(
249
+ subject.control.run.withInactive({}, async () => {}),
250
+ ).rejects.toMatchObject({ code: "run_handler_unavailable" });
251
+ });
252
+ });
@@ -0,0 +1,219 @@
1
+ import {
2
+ createRunInspector,
3
+ getRunInspectorKind,
4
+ type RunControl,
5
+ type RunInspectorConfig,
6
+ type RunInspectorFactoryResult,
7
+ type RunInspectorKind,
8
+ } from "@/actor/config";
9
+ import { RivetError } from "@/actor/errors";
10
+
11
+ type RunHandler = ((...args: any[]) => any) | { run: (...args: any[]) => any };
12
+
13
+ interface ActorRunState {
14
+ active: boolean;
15
+ closed: boolean;
16
+ exclusive: boolean;
17
+ exclusiveGeneration: number;
18
+ exclusiveOutcome?: "success" | "failure";
19
+ queued: number;
20
+ restart?: () => void | Promise<void>;
21
+ inspectorInitialized: boolean;
22
+ inspector?: RunInspectorFactoryResult;
23
+ waiters: Set<() => void>;
24
+ }
25
+
26
+ type RunHandlerOutcome = "ran" | "suppressed" | "closed";
27
+
28
+ function runUnavailable(actorId: string, reason: string): RivetError {
29
+ return new RivetError(
30
+ "actor",
31
+ "run_handler_unavailable",
32
+ `Run handler control is unavailable for actor ${actorId}: ${reason}.`,
33
+ {
34
+ public: true,
35
+ statusCode: 409,
36
+ metadata: { actorId, reason },
37
+ },
38
+ );
39
+ }
40
+
41
+ function notifyStateChanged(state: ActorRunState): void {
42
+ const waiters = [...state.waiters];
43
+ state.waiters.clear();
44
+ for (const waiter of waiters) {
45
+ waiter();
46
+ }
47
+ }
48
+
49
+ function waitForStateChange(state: ActorRunState): Promise<void> {
50
+ return new Promise((resolve) => {
51
+ state.waiters.add(resolve);
52
+ });
53
+ }
54
+
55
+ /** Coordinates a run handler and its Inspector controls within one registry. */
56
+ export class RunHandlerCoordinator {
57
+ readonly #run: RunHandler | undefined;
58
+ readonly #states = new Map<string, ActorRunState>();
59
+
60
+ constructor(run: RunHandler | undefined) {
61
+ this.#run = run;
62
+ }
63
+
64
+ get inspectorKind(): RunInspectorKind | undefined {
65
+ return getRunInspectorKind(this.#run);
66
+ }
67
+
68
+ async run(
69
+ actorId: string,
70
+ restart: () => void | Promise<void>,
71
+ callback: () => void | Promise<void>,
72
+ ): Promise<RunHandlerOutcome> {
73
+ const state = this.#getOrCreate(actorId);
74
+ state.restart = restart;
75
+ this.#initializeInspector(actorId, state);
76
+ state.queued += 1;
77
+ const blockedGeneration = state.exclusive
78
+ ? state.exclusiveGeneration
79
+ : undefined;
80
+
81
+ try {
82
+ while (!state.closed && (state.exclusive || state.active)) {
83
+ await waitForStateChange(state);
84
+ }
85
+ } finally {
86
+ state.queued -= 1;
87
+ }
88
+
89
+ if (state.closed) {
90
+ return "closed";
91
+ }
92
+ if (
93
+ blockedGeneration !== undefined &&
94
+ state.exclusiveGeneration === blockedGeneration &&
95
+ state.exclusiveOutcome === "failure"
96
+ ) {
97
+ return "suppressed";
98
+ }
99
+
100
+ state.active = true;
101
+ try {
102
+ await callback();
103
+ return "ran";
104
+ } finally {
105
+ state.active = false;
106
+ notifyStateChanged(state);
107
+ }
108
+ }
109
+
110
+ getInspector(
111
+ actorId: string,
112
+ restart: () => void | Promise<void>,
113
+ ): RunInspectorConfig | undefined {
114
+ const state = this.#getOrCreate(actorId);
115
+ state.restart = restart;
116
+ this.#initializeInspector(actorId, state);
117
+ return state.inspector?.inspector;
118
+ }
119
+
120
+ destroy(actorId: string): void {
121
+ const state = this.#states.get(actorId);
122
+ if (!state) return;
123
+
124
+ state.closed = true;
125
+ state.inspector?.dispose?.();
126
+ state.inspector = undefined;
127
+ notifyStateChanged(state);
128
+ this.#states.delete(actorId);
129
+ }
130
+
131
+ #control(actorId: string, state: ActorRunState): RunControl {
132
+ return {
133
+ run: {
134
+ withInactive: async (options, callback) => {
135
+ if (state.closed) {
136
+ throw runUnavailable(actorId, "actor is destroyed");
137
+ }
138
+ if (state.active || state.queued > 0 || state.exclusive) {
139
+ throw runUnavailable(
140
+ actorId,
141
+ "the run handler is active or waiting to start",
142
+ );
143
+ }
144
+
145
+ state.exclusive = true;
146
+ state.exclusiveGeneration += 1;
147
+ state.exclusiveOutcome = undefined;
148
+ try {
149
+ const result = await callback();
150
+ if (state.closed) {
151
+ throw runUnavailable(actorId, "actor is destroyed");
152
+ }
153
+ state.exclusiveOutcome = "success";
154
+ if (options.restartOnSuccess && state.queued === 0) {
155
+ if (!state.restart) {
156
+ throw runUnavailable(
157
+ actorId,
158
+ "the runtime restart callback is not available",
159
+ );
160
+ }
161
+ await state.restart();
162
+ }
163
+ return result;
164
+ } catch (error) {
165
+ state.exclusiveOutcome = "failure";
166
+ throw error;
167
+ } finally {
168
+ state.exclusive = false;
169
+ notifyStateChanged(state);
170
+ }
171
+ },
172
+ },
173
+ };
174
+ }
175
+
176
+ #getOrCreate(actorId: string): ActorRunState {
177
+ let state = this.#states.get(actorId);
178
+ if (!state) {
179
+ state = {
180
+ active: false,
181
+ closed: false,
182
+ exclusive: false,
183
+ exclusiveGeneration: 0,
184
+ inspectorInitialized: false,
185
+ queued: 0,
186
+ waiters: new Set(),
187
+ };
188
+ this.#states.set(actorId, state);
189
+ }
190
+ return state;
191
+ }
192
+
193
+ #initializeInspector(actorId: string, state: ActorRunState): void {
194
+ if (state.inspectorInitialized) return;
195
+ const inspector = createRunInspector(this.#run, {
196
+ actorId,
197
+ control: this.#control(actorId, state),
198
+ });
199
+ if (this.inspectorKind === "workflow") {
200
+ const workflow =
201
+ inspector === undefined
202
+ ? undefined
203
+ : inspector.inspector.workflow;
204
+ if (
205
+ !workflow ||
206
+ typeof workflow.getHistory !== "function" ||
207
+ typeof workflow.getState !== "function" ||
208
+ typeof workflow.onHistoryUpdated !== "function" ||
209
+ typeof workflow.replayFromStep !== "function"
210
+ ) {
211
+ throw new TypeError(
212
+ `defineRunHandler createInspector returned an invalid workflow adapter for actor ${actorId}`,
213
+ );
214
+ }
215
+ }
216
+ state.inspector = inspector;
217
+ state.inspectorInitialized = true;
218
+ }
219
+ }
@@ -1,4 +1,7 @@
1
- import type { SqliteNativeMetrics } from "@/common/database/config";
1
+ import type {
2
+ SqliteNativeMetrics,
3
+ SqliteProfilingOptions,
4
+ } from "@/common/database/config";
2
5
  import { stringifyError } from "@/common/utils";
3
6
  import type { RegistryConfig } from "./config";
4
7
  import { logger } from "./log";
@@ -15,6 +18,7 @@ export type ActorContextHandle = OpaqueHandle<"actorContext">;
15
18
  export type ConnHandle = OpaqueHandle<"conn">;
16
19
  export type WebSocketHandle = OpaqueHandle<"webSocket">;
17
20
  export type CancellationTokenHandle = OpaqueHandle<"cancellationToken">;
21
+ export type ActorStateTransactionHandle = OpaqueHandle<"actorStateTransaction">;
18
22
  export type SqliteTransactionHandle = OpaqueHandle<"sqliteTransaction">;
19
23
 
20
24
  export type RuntimeBytes = Uint8Array;
@@ -255,6 +259,7 @@ export interface RuntimeSqlDatabase {
255
259
  ): Promise<RuntimeSqlRunResult>;
256
260
  beginTransaction(
257
261
  timeoutMs?: number,
262
+ name?: string,
258
263
  ): Promise<RuntimeSqlTransactionDatabase>;
259
264
  metrics?(): SqliteNativeMetrics | null;
260
265
  takeLastKvError?(): string | null;
@@ -276,6 +281,7 @@ export interface RuntimeActorConfig {
276
281
  icon?: string;
277
282
  hasDatabase?: boolean;
278
283
  remoteSqlite?: boolean;
284
+ sqliteProfiling?: SqliteProfilingOptions;
279
285
  enableActorRuntimeSocket?: boolean;
280
286
  hasState?: boolean;
281
287
  canHibernateWebsocket?: boolean;
@@ -479,6 +485,10 @@ export interface CoreRuntime {
479
485
  ctx: ActorContextHandle,
480
486
  timestampMs?: number | undefined | null,
481
487
  ): void;
488
+ actorSetRunWakeAt(
489
+ ctx: ActorContextHandle,
490
+ timestampMs?: number | undefined | null,
491
+ ): Promise<void>;
482
492
  actorRequestSave(
483
493
  ctx: ActorContextHandle,
484
494
  opts?: RuntimeRequestSaveOpts | undefined | null,
@@ -601,6 +611,7 @@ export interface CoreRuntime {
601
611
  actorSqlBeginTransaction(
602
612
  ctx: ActorContextHandle,
603
613
  timeoutMs?: number,
614
+ name?: string,
604
615
  ): Promise<SqliteTransactionHandle>;
605
616
  actorSqlTransactionExec(
606
617
  transaction: SqliteTransactionHandle,
@@ -617,6 +628,22 @@ export interface CoreRuntime {
617
628
  actorSqlTransactionRollback(
618
629
  transaction: SqliteTransactionHandle,
619
630
  ): Promise<void>;
631
+ actorBeginStateTransaction(
632
+ ctx: ActorContextHandle,
633
+ timeoutMs?: number,
634
+ ): Promise<ActorStateTransactionHandle>;
635
+ actorStateTransactionExecute(
636
+ transaction: ActorStateTransactionHandle,
637
+ sql: string,
638
+ params?: RuntimeSqlBindParams,
639
+ ): Promise<RuntimeSqlExecuteResult>;
640
+ actorStateTransactionCommit(
641
+ transaction: ActorStateTransactionHandle,
642
+ payload: RuntimeStateDeltaPayload,
643
+ ): Promise<void>;
644
+ actorStateTransactionRollback(
645
+ transaction: ActorStateTransactionHandle,
646
+ ): Promise<void>;
620
647
  actorSqlQuery(
621
648
  ctx: ActorContextHandle,
622
649
  sql: string,
@@ -656,6 +683,12 @@ export interface CoreRuntime {
656
683
  options?: RuntimeQueueWaitOptions | undefined | null,
657
684
  signal?: CancellationTokenHandle | undefined | null,
658
685
  ): Promise<void>;
686
+ actorQueueCompletePersisted(
687
+ ctx: ActorContextHandle,
688
+ messageId: bigint,
689
+ expectedName: string,
690
+ response?: RuntimeBytes | undefined | null,
691
+ ): Promise<boolean>;
659
692
  actorQueueEnqueueAndWait(
660
693
  ctx: ActorContextHandle,
661
694
  name: string,