rivetkit 2.3.11-rc.8 → 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.
- package/dist/browser/client.d.ts +96 -14
- package/dist/browser/client.js +12 -2
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.d.ts +139 -139
- package/dist/browser/inspector/client.js +12 -2
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/agent-os/index.cjs +146 -76
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +83 -9
- package/dist/tsup/agent-os/index.d.ts +83 -9
- package/dist/tsup/agent-os/index.js +146 -76
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-JBQ63WSM.js → chunk-3ZR542SK.js} +4 -4
- package/dist/tsup/chunk-3ZR542SK.js.map +1 -0
- package/dist/tsup/{chunk-HNHLBM3Y.cjs → chunk-4KGXFC6M.cjs} +42 -5
- package/dist/tsup/chunk-4KGXFC6M.cjs.map +1 -0
- package/dist/tsup/chunk-5UYWLCRO.js +14 -0
- package/dist/tsup/chunk-5UYWLCRO.js.map +1 -0
- package/dist/tsup/{chunk-QWLJCP3X.js → chunk-6W5VGLFT.js} +42 -5
- package/dist/tsup/chunk-6W5VGLFT.js.map +1 -0
- package/dist/tsup/chunk-6WYATRS4.js +243 -0
- package/dist/tsup/chunk-6WYATRS4.js.map +1 -0
- package/dist/tsup/chunk-B3W74FBB.cjs +14 -0
- package/dist/tsup/chunk-B3W74FBB.cjs.map +1 -0
- package/dist/tsup/{chunk-V6ANE6JM.js → chunk-FWROC5LA.js} +2 -2
- package/dist/tsup/{chunk-ZVM35ICH.cjs → chunk-GLIPKKEC.cjs} +7 -7
- package/dist/tsup/{chunk-ZVM35ICH.cjs.map → chunk-GLIPKKEC.cjs.map} +1 -1
- package/dist/tsup/{chunk-CCUO5OKD.cjs → chunk-GN2EUMEW.cjs} +3 -3
- package/dist/tsup/{chunk-CCUO5OKD.cjs.map → chunk-GN2EUMEW.cjs.map} +1 -1
- package/dist/tsup/{chunk-2ROUKH7W.cjs → chunk-GU7ML3KE.cjs} +13 -3
- package/dist/tsup/chunk-GU7ML3KE.cjs.map +1 -0
- package/dist/tsup/{chunk-WRRB24UY.js → chunk-JQ2AZ5FN.js} +3 -3
- package/dist/tsup/{chunk-7AFZMFPQ.js → chunk-JTHHCZCZ.js} +10 -1
- package/dist/tsup/chunk-JTHHCZCZ.js.map +1 -0
- package/dist/tsup/{chunk-UEULFXUI.cjs → chunk-KVFIONKQ.cjs} +11 -2
- package/dist/tsup/chunk-KVFIONKQ.cjs.map +1 -0
- package/dist/tsup/chunk-LWQFXGKS.cjs +243 -0
- package/dist/tsup/chunk-LWQFXGKS.cjs.map +1 -0
- package/dist/tsup/{chunk-KVD6ZWNM.js → chunk-MO2SSUOR.js} +8 -7
- package/dist/tsup/chunk-MO2SSUOR.js.map +1 -0
- package/dist/tsup/{chunk-AGNBLCNJ.js → chunk-OUENKQHB.js} +13 -3
- package/dist/tsup/chunk-OUENKQHB.js.map +1 -0
- package/dist/tsup/{chunk-E6WB2BLO.cjs → chunk-PUZY2XCD.cjs} +10 -9
- package/dist/tsup/chunk-PUZY2XCD.cjs.map +1 -0
- package/dist/tsup/{chunk-4EDGOGCB.js → chunk-QFJ24MBU.js} +27 -2
- package/dist/tsup/{chunk-4EDGOGCB.js.map → chunk-QFJ24MBU.js.map} +1 -1
- package/dist/tsup/{chunk-A55CGZIV.cjs → chunk-RXXDNJXB.cjs} +140 -140
- package/dist/tsup/{chunk-A55CGZIV.cjs.map → chunk-RXXDNJXB.cjs.map} +1 -1
- package/dist/tsup/{chunk-N6MBTYGX.cjs → chunk-VBJQIBXI.cjs} +30 -5
- package/dist/tsup/chunk-VBJQIBXI.cjs.map +1 -0
- package/dist/tsup/client/mod.cjs +6 -6
- package/dist/tsup/client/mod.d.cts +4 -3
- package/dist/tsup/client/mod.d.ts +4 -3
- package/dist/tsup/client/mod.js +5 -5
- package/dist/tsup/common/log.cjs +2 -2
- package/dist/tsup/common/log.js +1 -1
- package/dist/tsup/common/websocket.cjs +3 -3
- package/dist/tsup/common/websocket.js +2 -2
- package/dist/tsup/{config-CZcjyT5B.d.ts → config-C4OSv0QP.d.ts} +92 -7
- package/dist/tsup/{config-rGWrumlO.d.cts → config-DTGoWdkG.d.cts} +92 -7
- package/dist/tsup/{config-BxOimoGX.d.cts → config-ey2I2zz5.d.cts} +46 -6
- package/dist/tsup/{config-BxOimoGX.d.ts → config-ey2I2zz5.d.ts} +46 -6
- package/dist/tsup/{context-B8W8E8nW.d.cts → context-C6cvDNyk.d.cts} +2 -2
- package/dist/tsup/{context-_UrDZhEh.d.ts → context-CMygeBxS.d.ts} +2 -2
- package/dist/tsup/db/drizzle.cjs +18 -10
- package/dist/tsup/db/drizzle.cjs.map +1 -1
- package/dist/tsup/db/drizzle.d.cts +5 -6
- package/dist/tsup/db/drizzle.d.ts +5 -6
- package/dist/tsup/db/drizzle.js +13 -5
- package/dist/tsup/db/drizzle.js.map +1 -1
- package/dist/tsup/db/mod.cjs +5 -175
- package/dist/tsup/db/mod.cjs.map +1 -1
- package/dist/tsup/db/mod.d.cts +11 -4
- package/dist/tsup/db/mod.d.ts +11 -4
- package/dist/tsup/db/mod.js +4 -174
- package/dist/tsup/db/mod.js.map +1 -1
- package/dist/tsup/dynamic/mod.cjs +2 -2
- package/dist/tsup/dynamic/mod.d.cts +3 -2
- package/dist/tsup/dynamic/mod.d.ts +3 -2
- package/dist/tsup/dynamic/mod.js +1 -1
- package/dist/tsup/inspector/mod.cjs +5 -4
- package/dist/tsup/inspector/mod.cjs.map +1 -1
- package/dist/tsup/inspector/mod.d.cts +12 -146
- package/dist/tsup/inspector/mod.d.ts +12 -146
- package/dist/tsup/inspector/mod.js +4 -3
- package/dist/tsup/inspector/mod.js.map +1 -1
- package/dist/tsup/inspector/workflow.cjs +21 -0
- package/dist/tsup/inspector/workflow.cjs.map +1 -0
- package/dist/tsup/inspector/workflow.d.cts +144 -0
- package/dist/tsup/inspector/workflow.d.ts +144 -0
- package/dist/tsup/inspector/workflow.js +21 -0
- package/dist/tsup/inspector/workflow.js.map +1 -0
- package/dist/tsup/inspector-tab/mod.cjs +2 -2
- package/dist/tsup/inspector-tab/mod.d.cts +4 -3
- package/dist/tsup/inspector-tab/mod.d.ts +4 -3
- package/dist/tsup/inspector-tab/mod.js +1 -1
- package/dist/tsup/mod.cjs +837 -243
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +6 -5
- package/dist/tsup/mod.d.ts +6 -5
- package/dist/tsup/mod.js +766 -172
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/test/mod.cjs +7 -7
- package/dist/tsup/test/mod.d.cts +3 -2
- package/dist/tsup/test/mod.d.ts +3 -2
- package/dist/tsup/test/mod.js +5 -5
- package/dist/tsup/unstable/migrations.d.cts +1 -1
- package/dist/tsup/unstable/migrations.d.ts +1 -1
- package/dist/tsup/utils.cjs +2 -2
- package/dist/tsup/utils.js +1 -1
- package/dist/tsup/workflow/mod.cjs +89 -95
- package/dist/tsup/workflow/mod.cjs.map +1 -1
- package/dist/tsup/workflow/mod.d.cts +5 -4
- package/dist/tsup/workflow/mod.d.ts +5 -4
- package/dist/tsup/workflow/mod.js +86 -92
- package/dist/tsup/workflow/mod.js.map +1 -1
- package/package.json +19 -9
- package/src/actor/config.test.ts +65 -1
- package/src/actor/config.ts +169 -5
- package/src/actor/mod.ts +10 -1
- package/src/client/actor-common.ts +5 -14
- package/src/common/database/config.ts +50 -3
- package/src/common/database/mod.test.ts +142 -4
- package/src/common/database/mod.ts +210 -98
- package/src/common/database/native-database.ts +42 -3
- package/src/common/database/shared.ts +9 -0
- package/src/db/drizzle.test.ts +8 -1
- package/src/db/drizzle.ts +15 -4
- package/src/db/mod.ts +3 -0
- package/src/inspector/workflow.ts +61 -0
- package/src/registry/napi-runtime.ts +69 -1
- package/src/registry/native.ts +505 -155
- package/src/registry/run-handler-coordinator.test.ts +252 -0
- package/src/registry/run-handler-coordinator.ts +219 -0
- package/src/registry/runtime.ts +34 -1
- package/src/registry/wasm-runtime.ts +122 -17
- package/src/registry/write-through-proxy.ts +101 -0
- package/src/utils/node.ts +44 -28
- package/src/workflow/driver.ts +56 -58
- package/src/workflow/inspector.ts +10 -15
- package/src/workflow/mod.ts +48 -70
- package/dist/tsup/chunk-2ROUKH7W.cjs.map +0 -1
- package/dist/tsup/chunk-7AFZMFPQ.js.map +0 -1
- package/dist/tsup/chunk-AGNBLCNJ.js.map +0 -1
- package/dist/tsup/chunk-E6WB2BLO.cjs.map +0 -1
- package/dist/tsup/chunk-HNHLBM3Y.cjs.map +0 -1
- package/dist/tsup/chunk-JBQ63WSM.js.map +0 -1
- package/dist/tsup/chunk-KVD6ZWNM.js.map +0 -1
- package/dist/tsup/chunk-N6MBTYGX.cjs.map +0 -1
- package/dist/tsup/chunk-QWLJCP3X.js.map +0 -1
- package/dist/tsup/chunk-UEULFXUI.cjs.map +0 -1
- /package/dist/tsup/{chunk-V6ANE6JM.js.map → chunk-FWROC5LA.js.map} +0 -0
- /package/dist/tsup/{chunk-WRRB24UY.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
|
+
}
|
package/src/registry/runtime.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
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,
|