rivetkit 2.3.0-rc.5 → 2.3.0-rc.7
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 +35 -3
- package/dist/browser/client.js +795 -200
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.js +4 -3
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/actor/errors.cjs +4 -2
- package/dist/tsup/actor/errors.cjs.map +1 -1
- package/dist/tsup/actor/errors.d.cts +1 -74
- package/dist/tsup/actor/errors.d.ts +1 -74
- package/dist/tsup/actor/errors.js +3 -1
- package/dist/tsup/agent-os/index.cjs +7 -5
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +35 -3
- package/dist/tsup/agent-os/index.d.ts +35 -3
- package/dist/tsup/agent-os/index.js +7 -5
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-2GANBXVP.cjs → chunk-2H4ISA4Y.cjs} +10 -10
- package/dist/tsup/chunk-2H4ISA4Y.cjs.map +1 -0
- package/dist/tsup/{chunk-UXTP4EBU.js → chunk-4DJMFOSU.js} +2 -2
- package/dist/tsup/{chunk-LDTT6WKJ.js → chunk-4LTY5TOO.js} +132 -4
- package/dist/tsup/chunk-4LTY5TOO.js.map +1 -0
- package/dist/tsup/{chunk-PGYEMIOE.js → chunk-52TPEKEC.js} +2 -2
- package/dist/tsup/{chunk-OVJX4IFY.cjs → chunk-55E7IR6D.cjs} +4 -4
- package/dist/tsup/{chunk-OVJX4IFY.cjs.map → chunk-55E7IR6D.cjs.map} +1 -1
- package/dist/tsup/{chunk-V3QNBJ7N.cjs → chunk-63WNTDRC.cjs} +31 -10
- package/dist/tsup/chunk-63WNTDRC.cjs.map +1 -0
- package/dist/tsup/{chunk-SULB574D.js → chunk-CMV6N5OX.js} +3 -3
- package/dist/tsup/{chunk-T6YVRM4K.js → chunk-D5G75T7J.js} +3 -1
- package/dist/tsup/chunk-D5G75T7J.js.map +1 -0
- package/dist/tsup/{chunk-NW2J4SOL.cjs → chunk-FEOG44WH.cjs} +137 -9
- package/dist/tsup/chunk-FEOG44WH.cjs.map +1 -0
- package/dist/tsup/{chunk-HR547GVH.cjs → chunk-G5HUSWP4.cjs} +8 -8
- package/dist/tsup/{chunk-HR547GVH.cjs.map → chunk-G5HUSWP4.cjs.map} +1 -1
- package/dist/tsup/{chunk-N2DQSJIW.js → chunk-HERL2VQ2.js} +17 -48
- package/dist/tsup/chunk-HERL2VQ2.js.map +1 -0
- package/dist/tsup/{chunk-LELRJK66.cjs → chunk-SJLPZEA3.cjs} +3 -3
- package/dist/tsup/{chunk-LELRJK66.cjs.map → chunk-SJLPZEA3.cjs.map} +1 -1
- package/dist/tsup/{chunk-WQ4HNA4W.cjs → chunk-SRNOPUC6.cjs} +4 -2
- package/dist/tsup/chunk-SRNOPUC6.cjs.map +1 -0
- package/dist/tsup/{chunk-K34B3OVG.js → chunk-TMLOKTRB.js} +30 -9
- package/dist/tsup/chunk-TMLOKTRB.js.map +1 -0
- package/dist/tsup/{chunk-NATOT3ET.js → chunk-VFIY6GWO.js} +4 -4
- package/dist/tsup/chunk-VFIY6GWO.js.map +1 -0
- package/dist/tsup/{chunk-JY73X7VU.js → chunk-VJ4Y4WBT.js} +692 -114
- package/dist/tsup/chunk-VJ4Y4WBT.js.map +1 -0
- package/dist/tsup/{chunk-JRCZDHXT.cjs → chunk-X6HIFXNK.cjs} +23 -54
- package/dist/tsup/chunk-X6HIFXNK.cjs.map +1 -0
- package/dist/tsup/{chunk-FTZIZ3JG.cjs → chunk-ZGPX6KAH.cjs} +838 -260
- package/dist/tsup/chunk-ZGPX6KAH.cjs.map +1 -0
- package/dist/tsup/client/mod.cjs +7 -7
- package/dist/tsup/client/mod.d.cts +3 -4
- package/dist/tsup/client/mod.d.ts +3 -4
- package/dist/tsup/client/mod.js +6 -6
- 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/{config-CvQUtDp9.d.ts → config-Ak1lv4gF.d.ts} +27 -5
- package/dist/tsup/{config-C-a9vrke.d.cts → config-DU_xj4qZ.d.cts} +27 -5
- package/dist/tsup/{context-A7R0bsZL.d.ts → context-DAAp4Lpg.d.ts} +1 -1
- package/dist/tsup/{context-CA3r-pf2.d.cts → context-Dt_L55q8.d.cts} +1 -1
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/mod.cjs +507 -308
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +4 -5
- package/dist/tsup/mod.d.ts +4 -5
- package/dist/tsup/mod.js +432 -233
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/process-metrics-NW754INA.js +118 -0
- package/dist/tsup/process-metrics-NW754INA.js.map +1 -0
- package/dist/tsup/process-metrics-TYAGKCEJ.cjs +118 -0
- package/dist/tsup/process-metrics-TYAGKCEJ.cjs.map +1 -0
- package/dist/tsup/test/mod.cjs +10 -10
- package/dist/tsup/test/mod.d.cts +2 -3
- package/dist/tsup/test/mod.d.ts +2 -3
- package/dist/tsup/test/mod.js +6 -6
- package/dist/tsup/utils-DVekpm4I.d.cts +103 -0
- package/dist/tsup/utils-DVekpm4I.d.ts +103 -0
- package/dist/tsup/utils.cjs +3 -3
- package/dist/tsup/utils.d.cts +1 -1
- package/dist/tsup/utils.d.ts +1 -1
- package/dist/tsup/utils.js +2 -2
- package/dist/tsup/workflow/mod.cjs +41 -16
- package/dist/tsup/workflow/mod.cjs.map +1 -1
- package/dist/tsup/workflow/mod.d.cts +4 -5
- package/dist/tsup/workflow/mod.d.ts +4 -5
- package/dist/tsup/workflow/mod.js +35 -10
- package/dist/tsup/workflow/mod.js.map +1 -1
- package/package.json +11 -10
- package/src/actor/config.ts +3 -0
- package/src/actor/errors.ts +53 -7
- package/src/agent-os/actor/session.ts +2 -2
- package/src/client/actor-conn.ts +55 -60
- package/src/client/actor-handle.ts +59 -24
- package/src/client/errors.ts +2 -1
- package/src/client/queue.ts +2 -1
- package/src/client/raw-utils.ts +2 -4
- package/src/client/utils.ts +32 -4
- package/src/common/actor-router-consts.ts +4 -0
- package/src/common/bare/generated/client-protocol/v4.ts +599 -0
- package/src/common/client-protocol-versioned.ts +125 -18
- package/src/common/client-protocol-zod.ts +7 -0
- package/src/common/client-protocol.ts +1 -1
- package/src/common/database/native-database.test.ts +35 -0
- package/src/common/database/native-database.ts +8 -4
- package/src/common/encoding.ts +243 -5
- package/src/common/inline-websocket-adapter.ts +12 -12
- package/src/common/log.ts +1 -0
- package/src/common/router.ts +40 -10
- package/src/common/utils.ts +9 -200
- package/src/drivers/engine/actor-driver.ts +29 -28
- package/src/engine-client/actor-websocket-client.ts +2 -1
- package/src/engine-client/mod.ts +3 -2
- package/src/registry/config/index.ts +3 -5
- package/src/registry/index.ts +90 -16
- package/src/registry/napi-runtime.ts +15 -0
- package/src/registry/native.ts +197 -255
- package/src/registry/process-metrics.ts +183 -0
- package/src/registry/runtime.ts +4 -0
- package/src/registry/wasm-runtime.ts +9 -0
- package/src/registry/write-through-proxy.ts +40 -0
- package/src/serde.ts +2 -2
- package/src/workflow/context.ts +32 -5
- package/src/workflow/inspector.ts +2 -1
- package/dist/tsup/chunk-2GANBXVP.cjs.map +0 -1
- package/dist/tsup/chunk-FTZIZ3JG.cjs.map +0 -1
- package/dist/tsup/chunk-JRCZDHXT.cjs.map +0 -1
- package/dist/tsup/chunk-JY73X7VU.js.map +0 -1
- package/dist/tsup/chunk-K34B3OVG.js.map +0 -1
- package/dist/tsup/chunk-LDTT6WKJ.js.map +0 -1
- package/dist/tsup/chunk-N2DQSJIW.js.map +0 -1
- package/dist/tsup/chunk-NATOT3ET.js.map +0 -1
- package/dist/tsup/chunk-NW2J4SOL.cjs.map +0 -1
- package/dist/tsup/chunk-T6YVRM4K.js.map +0 -1
- package/dist/tsup/chunk-V3QNBJ7N.cjs.map +0 -1
- package/dist/tsup/chunk-WQ4HNA4W.cjs.map +0 -1
- package/dist/tsup/utils-fwx3o3K9.d.cts +0 -18
- package/dist/tsup/utils-fwx3o3K9.d.ts +0 -18
- /package/dist/tsup/{chunk-UXTP4EBU.js.map → chunk-4DJMFOSU.js.map} +0 -0
- /package/dist/tsup/{chunk-PGYEMIOE.js.map → chunk-52TPEKEC.js.map} +0 -0
- /package/dist/tsup/{chunk-SULB574D.js.map → chunk-CMV6N5OX.js.map} +0 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js runtime health metrics.
|
|
3
|
+
*
|
|
4
|
+
* Collects JS-internal data (event loop lag, GC, heap, libuv handles,
|
|
5
|
+
* event loop utilization, CPU) using Node built-ins (`node:perf_hooks`,
|
|
6
|
+
* `process`, `node:v8`, `PerformanceObserver`) and pushes them across NAPI
|
|
7
|
+
* into Rust-side prometheus collectors registered with
|
|
8
|
+
* `rivet_metrics::REGISTRY` so they appear on the existing `/metrics`
|
|
9
|
+
* endpoint.
|
|
10
|
+
*
|
|
11
|
+
* All data collection happens here in TypeScript. The NAPI bridge is pure
|
|
12
|
+
* type marshalling and the Rust side only registers + stores the metrics.
|
|
13
|
+
*/
|
|
14
|
+
import { monitorEventLoopDelay, performance, PerformanceObserver } from "node:perf_hooks";
|
|
15
|
+
import { getHeapStatistics } from "node:v8";
|
|
16
|
+
import * as napi from "@rivetkit/rivetkit-napi";
|
|
17
|
+
|
|
18
|
+
// Some napi process-metrics symbols may be missing on older native binaries
|
|
19
|
+
// (the auto-generated index.js destructures them as `undefined` if the
|
|
20
|
+
// underlying `.node` was built before they were added). Guard each call so
|
|
21
|
+
// the metrics collection runs as a no-op instead of throwing
|
|
22
|
+
// `TypeError: napi.jsXxx is not a function` on every interval tick.
|
|
23
|
+
function callIfFn<T extends unknown[]>(
|
|
24
|
+
fn: ((...args: T) => void) | undefined,
|
|
25
|
+
...args: T
|
|
26
|
+
): void {
|
|
27
|
+
if (typeof fn === "function") {
|
|
28
|
+
fn(...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SCRAPE_INTERVAL_MS = 5_000;
|
|
33
|
+
const HEARTBEAT_INTERVAL_MS = 100;
|
|
34
|
+
const EVENTLOOP_DELAY_RESOLUTION_MS = 20;
|
|
35
|
+
const NS_PER_SECOND = 1e9;
|
|
36
|
+
const US_PER_SECOND = 1e6;
|
|
37
|
+
|
|
38
|
+
// V8 GC kind bitfield from Node's perf_hooks documentation. A `gc` performance
|
|
39
|
+
// entry's `kind` field is one of these values.
|
|
40
|
+
const GC_KIND_NAMES: Record<number, string> = {
|
|
41
|
+
1: "minor",
|
|
42
|
+
2: "major",
|
|
43
|
+
4: "incremental",
|
|
44
|
+
8: "weakcb",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
interface ProcessMetricsState {
|
|
48
|
+
scrapeInterval: NodeJS.Timeout;
|
|
49
|
+
heartbeatInterval: NodeJS.Timeout;
|
|
50
|
+
gcObserver: PerformanceObserver;
|
|
51
|
+
eventLoopHistogram: ReturnType<typeof monitorEventLoopDelay>;
|
|
52
|
+
lastCpuUsage: NodeJS.CpuUsage;
|
|
53
|
+
lastEventLoopUtilization: ReturnType<typeof performance.eventLoopUtilization>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let state: ProcessMetricsState | undefined;
|
|
57
|
+
|
|
58
|
+
export function startProcessMetrics(): void {
|
|
59
|
+
if (state) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const eventLoopHistogram = monitorEventLoopDelay({
|
|
64
|
+
resolution: EVENTLOOP_DELAY_RESOLUTION_MS,
|
|
65
|
+
});
|
|
66
|
+
eventLoopHistogram.enable();
|
|
67
|
+
|
|
68
|
+
const gcObserver = new PerformanceObserver((list) => {
|
|
69
|
+
for (const entry of list.getEntries()) {
|
|
70
|
+
const kind =
|
|
71
|
+
(entry as PerformanceEntry & { detail?: { kind?: number }; kind?: number }).detail
|
|
72
|
+
?.kind ??
|
|
73
|
+
(entry as PerformanceEntry & { kind?: number }).kind;
|
|
74
|
+
if (typeof kind !== "number") continue;
|
|
75
|
+
const kindName = GC_KIND_NAMES[kind];
|
|
76
|
+
if (!kindName) continue;
|
|
77
|
+
// `entry.duration` is in milliseconds; convert to seconds.
|
|
78
|
+
callIfFn(napi.jsObserveGcDuration, kindName, entry.duration / 1000);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
gcObserver.observe({ entryTypes: ["gc"], buffered: false });
|
|
82
|
+
|
|
83
|
+
const lastCpuUsage = process.cpuUsage();
|
|
84
|
+
const lastEventLoopUtilization = performance.eventLoopUtilization();
|
|
85
|
+
|
|
86
|
+
const heartbeatInterval = setInterval(() => {
|
|
87
|
+
callIfFn(napi.jsSetEventloopHeartbeatTsMs, Date.now());
|
|
88
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
89
|
+
heartbeatInterval.unref();
|
|
90
|
+
|
|
91
|
+
const scrapeInterval = setInterval(() => {
|
|
92
|
+
try {
|
|
93
|
+
collectAndPush();
|
|
94
|
+
} catch {
|
|
95
|
+
// Collection errors must never bring down the process; metrics
|
|
96
|
+
// are best-effort.
|
|
97
|
+
}
|
|
98
|
+
}, SCRAPE_INTERVAL_MS);
|
|
99
|
+
scrapeInterval.unref();
|
|
100
|
+
|
|
101
|
+
state = {
|
|
102
|
+
scrapeInterval,
|
|
103
|
+
heartbeatInterval,
|
|
104
|
+
gcObserver,
|
|
105
|
+
eventLoopHistogram,
|
|
106
|
+
lastCpuUsage,
|
|
107
|
+
lastEventLoopUtilization,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Emit one snapshot immediately so freshly-scraped instances have data.
|
|
111
|
+
callIfFn(napi.jsSetEventloopHeartbeatTsMs, Date.now());
|
|
112
|
+
try {
|
|
113
|
+
collectAndPush();
|
|
114
|
+
} catch {
|
|
115
|
+
// As above; best-effort.
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function stopProcessMetrics(): void {
|
|
120
|
+
if (!state) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
clearInterval(state.scrapeInterval);
|
|
124
|
+
clearInterval(state.heartbeatInterval);
|
|
125
|
+
state.gcObserver.disconnect();
|
|
126
|
+
state.eventLoopHistogram.disable();
|
|
127
|
+
state = undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function collectAndPush(): void {
|
|
131
|
+
if (!state) return;
|
|
132
|
+
|
|
133
|
+
// Event loop delay quantiles. `monitorEventLoopDelay()` reports values in
|
|
134
|
+
// nanoseconds; convert to seconds. Reset after reading so the next window
|
|
135
|
+
// reflects only the new interval.
|
|
136
|
+
const hist = state.eventLoopHistogram;
|
|
137
|
+
callIfFn(napi.jsSetEventloopLagQuantile, "p50", hist.percentile(50) / NS_PER_SECOND);
|
|
138
|
+
callIfFn(napi.jsSetEventloopLagQuantile, "p90", hist.percentile(90) / NS_PER_SECOND);
|
|
139
|
+
callIfFn(napi.jsSetEventloopLagQuantile, "p99", hist.percentile(99) / NS_PER_SECOND);
|
|
140
|
+
callIfFn(napi.jsSetEventloopLagQuantile, "max", hist.max / NS_PER_SECOND);
|
|
141
|
+
hist.reset();
|
|
142
|
+
|
|
143
|
+
// Event loop utilization delta over the scrape window.
|
|
144
|
+
const nextElu = performance.eventLoopUtilization();
|
|
145
|
+
const eluDelta = performance.eventLoopUtilization(nextElu, state.lastEventLoopUtilization);
|
|
146
|
+
state.lastEventLoopUtilization = nextElu;
|
|
147
|
+
callIfFn(napi.jsSetEventloopUtilization, eluDelta.utilization);
|
|
148
|
+
|
|
149
|
+
// CPU usage delta. `process.cpuUsage()` returns microseconds.
|
|
150
|
+
const nextCpu = process.cpuUsage();
|
|
151
|
+
const userDeltaUs = nextCpu.user - state.lastCpuUsage.user;
|
|
152
|
+
const systemDeltaUs = nextCpu.system - state.lastCpuUsage.system;
|
|
153
|
+
state.lastCpuUsage = nextCpu;
|
|
154
|
+
if (userDeltaUs > 0) {
|
|
155
|
+
callIfFn(napi.jsAddProcessCpuSeconds, "user", userDeltaUs / US_PER_SECOND);
|
|
156
|
+
}
|
|
157
|
+
if (systemDeltaUs > 0) {
|
|
158
|
+
callIfFn(napi.jsAddProcessCpuSeconds, "system", systemDeltaUs / US_PER_SECOND);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Memory + heap.
|
|
162
|
+
const mem = process.memoryUsage();
|
|
163
|
+
callIfFn(napi.jsSetProcessResidentMemoryBytes, mem.rss);
|
|
164
|
+
callIfFn(napi.jsSetHeapBytes, "used", mem.heapUsed);
|
|
165
|
+
callIfFn(napi.jsSetHeapBytes, "total", mem.heapTotal);
|
|
166
|
+
const heapLimit = getHeapStatistics().heap_size_limit;
|
|
167
|
+
callIfFn(napi.jsSetHeapBytes, "limit", heapLimit);
|
|
168
|
+
|
|
169
|
+
// libuv active handles + requests. These are unstable Node internals
|
|
170
|
+
// guarded behind underscore-prefixed names; if a future Node release
|
|
171
|
+
// removes them the try/catch above keeps the rest of the collection
|
|
172
|
+
// alive.
|
|
173
|
+
const proc = process as unknown as {
|
|
174
|
+
_getActiveHandles?: () => unknown[];
|
|
175
|
+
_getActiveRequests?: () => unknown[];
|
|
176
|
+
};
|
|
177
|
+
if (typeof proc._getActiveHandles === "function") {
|
|
178
|
+
callIfFn(napi.jsSetActiveHandles, proc._getActiveHandles().length);
|
|
179
|
+
}
|
|
180
|
+
if (typeof proc._getActiveRequests === "function") {
|
|
181
|
+
callIfFn(napi.jsSetActiveRequests, proc._getActiveRequests().length);
|
|
182
|
+
}
|
|
183
|
+
}
|
package/src/registry/runtime.ts
CHANGED
|
@@ -309,6 +309,9 @@ export interface CoreRuntime {
|
|
|
309
309
|
config: RuntimeServeConfig,
|
|
310
310
|
): Promise<void>;
|
|
311
311
|
shutdownRegistry(registry: RegistryHandle): Promise<void>;
|
|
312
|
+
registryActorStopThresholdMs?(
|
|
313
|
+
registry: RegistryHandle,
|
|
314
|
+
): Promise<number | undefined>;
|
|
312
315
|
handleServerlessRequest(
|
|
313
316
|
registry: RegistryHandle,
|
|
314
317
|
req: RuntimeServerlessRequest,
|
|
@@ -388,6 +391,7 @@ export interface CoreRuntime {
|
|
|
388
391
|
args: RuntimeBytes,
|
|
389
392
|
): void;
|
|
390
393
|
actorWaitUntil(ctx: ActorContextHandle, promise: Promise<unknown>): void;
|
|
394
|
+
actorWaitForTrackedShutdownWork(ctx: ActorContextHandle): Promise<boolean>;
|
|
391
395
|
actorKeepAwake(ctx: ActorContextHandle, promise: Promise<unknown>): void;
|
|
392
396
|
actorBeginKeepAwake(ctx: ActorContextHandle): number;
|
|
393
397
|
actorEndKeepAwake(ctx: ActorContextHandle, regionId: number): void;
|
|
@@ -499,6 +499,15 @@ export class WasmCoreRuntime implements CoreRuntime {
|
|
|
499
499
|
callHandle(asWasmActorContext(ctx), "waitUntil", promise);
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
async actorWaitForTrackedShutdownWork(
|
|
503
|
+
ctx: ActorContextHandle,
|
|
504
|
+
): Promise<boolean> {
|
|
505
|
+
return await callHandle<Promise<boolean>>(
|
|
506
|
+
asWasmActorContext(ctx),
|
|
507
|
+
"waitForTrackedShutdownWork",
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
502
511
|
actorKeepAwake(ctx: ActorContextHandle, promise: Promise<unknown>): void {
|
|
503
512
|
const wasmCtx = asWasmActorContext(ctx);
|
|
504
513
|
const regionId = callHandle<number>(wasmCtx, "beginKeepAwake");
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import onChange from "@rivetkit/on-change";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a proxy that tracks deep mutations on an object and calls `commit`
|
|
5
|
+
* after every change. Uses `@rivetkit/on-change` internally, which correctly
|
|
6
|
+
* detects mutations via methods on Map, Set, Date, TypedArrays, and arrays.
|
|
7
|
+
*
|
|
8
|
+
* If the value is not an object (primitive, null, undefined), it is returned
|
|
9
|
+
* as-is since primitives cannot be proxied or mutated.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The root value to watch.
|
|
12
|
+
* @param commit - Called after every detected mutation with the root object.
|
|
13
|
+
* @param beforeChange - Called before every mutation with the new value being
|
|
14
|
+
* assigned. Throw to reject the change.
|
|
15
|
+
*/
|
|
16
|
+
export function createWriteThroughProxy<T>(
|
|
17
|
+
value: T,
|
|
18
|
+
commit: (next: T) => void,
|
|
19
|
+
beforeChange?: (newValue: unknown) => void,
|
|
20
|
+
): T {
|
|
21
|
+
if (!value || typeof value !== "object") {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return onChange(
|
|
26
|
+
value as T & Record<string, any>,
|
|
27
|
+
() => {
|
|
28
|
+
commit(value);
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
// Rejection is throw-based: beforeChange throws to prevent the
|
|
32
|
+
// mutation. We always return true so on-change applies the change
|
|
33
|
+
// if beforeChange did not throw.
|
|
34
|
+
onValidate(_path: string, newValue: unknown) {
|
|
35
|
+
beforeChange?.(newValue);
|
|
36
|
+
return true;
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
) as T;
|
|
40
|
+
}
|
package/src/serde.ts
CHANGED
|
@@ -3,7 +3,7 @@ import invariant from "invariant";
|
|
|
3
3
|
import type { VersionedDataHandler } from "vbare";
|
|
4
4
|
import type { z } from "zod/v4";
|
|
5
5
|
import { assertUnreachable } from "@/common/utils";
|
|
6
|
-
import type { Encoding } from "@/common/encoding";
|
|
6
|
+
import type { JsonCompatValue, Encoding } from "@/common/encoding";
|
|
7
7
|
import {
|
|
8
8
|
encodeJsonCompatValue,
|
|
9
9
|
jsonParseCompat,
|
|
@@ -46,7 +46,7 @@ export function contentTypeForEncoding(encoding: Encoding): string {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export function encodeCborCompat(value:
|
|
49
|
+
export function encodeCborCompat(value: JsonCompatValue): Uint8Array {
|
|
50
50
|
return cbor.encode(encodeJsonCompatValue(value));
|
|
51
51
|
}
|
|
52
52
|
|
package/src/workflow/context.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
|
-
import type
|
|
2
|
+
import { RAW_STATE_SYMBOL, type RunContext } from "@/actor/config";
|
|
3
3
|
import type {
|
|
4
4
|
QueueFilterName,
|
|
5
5
|
QueueNextBatchOptions,
|
|
@@ -247,14 +247,14 @@ export class ActorWorkflowContext<
|
|
|
247
247
|
}
|
|
248
248
|
return await this.#wrapActive(() =>
|
|
249
249
|
this.#inner.step(nameOrConfig, () =>
|
|
250
|
-
this.#
|
|
250
|
+
this.#withActorAccessAndStateRollback(run),
|
|
251
251
|
),
|
|
252
252
|
);
|
|
253
253
|
}
|
|
254
254
|
const stepConfig = nameOrConfig as StepConfig<T>;
|
|
255
255
|
const config: StepConfig<T> = {
|
|
256
256
|
...stepConfig,
|
|
257
|
-
run: () => this.#
|
|
257
|
+
run: () => this.#withActorAccessAndStateRollback(stepConfig.run),
|
|
258
258
|
};
|
|
259
259
|
return await this.#wrapActive(() => this.#inner.step(config));
|
|
260
260
|
}
|
|
@@ -271,14 +271,14 @@ export class ActorWorkflowContext<
|
|
|
271
271
|
}
|
|
272
272
|
return await this.#wrapActive(() =>
|
|
273
273
|
this.#inner.tryStep(nameOrConfig, () =>
|
|
274
|
-
this.#
|
|
274
|
+
this.#withActorAccessAndStateRollback(run),
|
|
275
275
|
),
|
|
276
276
|
);
|
|
277
277
|
}
|
|
278
278
|
const stepConfig = nameOrConfig as TryStepConfig<T>;
|
|
279
279
|
const config: TryStepConfig<T> = {
|
|
280
280
|
...stepConfig,
|
|
281
|
-
run: () => this.#
|
|
281
|
+
run: () => this.#withActorAccessAndStateRollback(stepConfig.run),
|
|
282
282
|
};
|
|
283
283
|
return await this.#wrapActive(() => this.#inner.tryStep(config));
|
|
284
284
|
}
|
|
@@ -612,6 +612,33 @@ export class ActorWorkflowContext<
|
|
|
612
612
|
}
|
|
613
613
|
}
|
|
614
614
|
|
|
615
|
+
async #withActorAccessAndStateRollback<T>(
|
|
616
|
+
run: () => Promise<T>,
|
|
617
|
+
): Promise<T> {
|
|
618
|
+
let stateSnapshot: { state: TState } | null = null;
|
|
619
|
+
try {
|
|
620
|
+
stateSnapshot = { state: this.#runCtx[RAW_STATE_SYMBOL]() };
|
|
621
|
+
} catch (error) {
|
|
622
|
+
this.#runCtx.log.debug({
|
|
623
|
+
msg: "failed to get state, likely due to being stateless workflow",
|
|
624
|
+
error,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
if (stateSnapshot) {
|
|
628
|
+
stateSnapshot.state = structuredClone(stateSnapshot.state);
|
|
629
|
+
}
|
|
630
|
+
const varsSnapshot = structuredClone(this.#runCtx.vars);
|
|
631
|
+
try {
|
|
632
|
+
return await this.#withActorAccess(run);
|
|
633
|
+
} catch (error) {
|
|
634
|
+
if (stateSnapshot) {
|
|
635
|
+
this.#runCtx.state = stateSnapshot.state;
|
|
636
|
+
}
|
|
637
|
+
this.#runCtx.vars = varsSnapshot;
|
|
638
|
+
throw error;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
615
642
|
#ensureActorAccess(feature: string): void {
|
|
616
643
|
if (!this.#allowActorAccess) {
|
|
617
644
|
this.#guardViolation = true;
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
import { encodeWorkflowHistoryTransport } from "@/common/inspector-transport";
|
|
14
14
|
import type * as inspectorSchema from "@/common/bare/generated/inspector/v4";
|
|
15
15
|
import * as transport from "@/common/bare/transport/v1";
|
|
16
|
+
import type { JsonCompatValue } from "@/common/encoding";
|
|
16
17
|
import { encodeCborCompat } from "@/serde";
|
|
17
18
|
import { assertUnreachable, bufferToArrayBuffer } from "@/utils";
|
|
18
19
|
|
|
@@ -91,7 +92,7 @@ export function createWorkflowInspectorAdapter(): {
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
function encodeCbor(value: unknown): ArrayBuffer {
|
|
94
|
-
return bufferToArrayBuffer(encodeCborCompat(value));
|
|
95
|
+
return bufferToArrayBuffer(encodeCborCompat(value as JsonCompatValue));
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
function encodeOptionalCbor(value: unknown): ArrayBuffer | null {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/rivet/rivet/rivetkit-typescript/packages/rivetkit/dist/tsup/chunk-2GANBXVP.cjs","../../src/workflow/inspector.ts"],"names":[],"mappings":"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","file":"/home/runner/work/rivet/rivet/rivetkit-typescript/packages/rivetkit/dist/tsup/chunk-2GANBXVP.cjs","sourcesContent":[null,"import type {\n\tBranchStatus,\n\tBranchStatusType,\n\tEntryKind,\n\tEntryStatus,\n\tLocation,\n\tSleepState,\n\tWorkflowHistoryEntry,\n\tWorkflowHistorySnapshot,\n\tWorkflowEntryMetadataSnapshot,\n\tWorkflowState,\n} from \"@rivetkit/workflow-engine\";\nimport { encodeWorkflowHistoryTransport } from \"@/common/inspector-transport\";\nimport type * as inspectorSchema from \"@/common/bare/generated/inspector/v4\";\nimport * as transport from \"@/common/bare/transport/v1\";\nimport { encodeCborCompat } from \"@/serde\";\nimport { assertUnreachable, bufferToArrayBuffer } from \"@/utils\";\n\ntype HistoryListener = (history: inspectorSchema.WorkflowHistory) => void;\n\nfunction createHistoryEmitter() {\n\tconst listeners = new Set<HistoryListener>();\n\n\treturn {\n\t\ton: (listener: HistoryListener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => listeners.delete(listener);\n\t\t},\n\t\temit: (history: inspectorSchema.WorkflowHistory) => {\n\t\t\tfor (const listener of listeners) {\n\t\t\t\tlistener(history);\n\t\t\t}\n\t\t},\n\t};\n}\n\nexport interface WorkflowInspectorAdapter {\n\tgetHistory: () => inspectorSchema.WorkflowHistory | null;\n\tgetState: () => Promise<WorkflowState | null>;\n\tonHistoryUpdated: (\n\t\tlistener: (history: inspectorSchema.WorkflowHistory) => void,\n\t) => () => void;\n\treplayFromStep: (\n\t\tentryId?: string,\n\t) => Promise<inspectorSchema.WorkflowHistory | null>;\n}\n\nexport function createWorkflowInspectorAdapter(): {\n\tadapter: WorkflowInspectorAdapter;\n\tupdate: (snapshot: WorkflowHistorySnapshot) => void;\n\tsetGetState: (fn: () => Promise<WorkflowState | null>) => void;\n\tsetReplayFromStep: (\n\t\tfn: (\n\t\t\tentryId?: string,\n\t\t) => Promise<inspectorSchema.WorkflowHistory | null>,\n\t) => void;\n} {\n\tconst emitter = createHistoryEmitter();\n\tlet history: inspectorSchema.WorkflowHistory | null = null;\n\tlet getState: () => Promise<WorkflowState | null> = async () => null;\n\tlet replayFromStep: (\n\t\tentryId?: string,\n\t) => Promise<inspectorSchema.WorkflowHistory | null> = async () => {\n\t\tthrow new Error(\"Workflow replay controls are not initialized\");\n\t};\n\n\tconst adapter: WorkflowInspectorAdapter = {\n\t\tgetHistory: () => history,\n\t\tgetState: async () => await getState(),\n\t\tonHistoryUpdated: (listener) => emitter.on(listener),\n\t\treplayFromStep: async (entryId) => await replayFromStep(entryId),\n\t};\n\n\tconst update = (snapshot: WorkflowHistorySnapshot) => {\n\t\tconst transportHistory = toWorkflowHistory(snapshot);\n\t\tconst next = encodeWorkflowHistoryTransport(transportHistory);\n\t\thistory = next;\n\t\temitter.emit(next);\n\t};\n\n\treturn {\n\t\tadapter,\n\t\tupdate,\n\t\tsetGetState: (fn) => {\n\t\t\tgetState = fn;\n\t\t},\n\t\tsetReplayFromStep: (fn) => {\n\t\t\treplayFromStep = fn;\n\t\t},\n\t};\n}\n\nfunction encodeCbor(value: unknown): ArrayBuffer {\n\treturn bufferToArrayBuffer(encodeCborCompat(value));\n}\n\nfunction encodeOptionalCbor(value: unknown): ArrayBuffer | null {\n\tif (value === undefined) {\n\t\treturn null;\n\t}\n\treturn encodeCbor(value);\n}\n\nfunction toU64(value: number): bigint {\n\treturn BigInt(Math.max(0, Math.floor(value)));\n}\n\nfunction toWorkflowLocation(location: Location): transport.WorkflowLocation {\n\treturn location.map((segment) => {\n\t\tif (typeof segment === \"number\") {\n\t\t\treturn { tag: \"WorkflowNameIndex\", val: segment };\n\t\t}\n\t\treturn {\n\t\t\ttag: \"WorkflowLoopIterationMarker\",\n\t\t\tval: {\n\t\t\t\tloop: segment.loop,\n\t\t\t\titeration: segment.iteration,\n\t\t\t},\n\t\t};\n\t});\n}\n\nfunction toWorkflowEntryKind(kind: EntryKind): transport.WorkflowEntryKind {\n\tswitch (kind.type) {\n\t\tcase \"step\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowStepEntry\",\n\t\t\t\tval: {\n\t\t\t\t\toutput: encodeOptionalCbor(kind.data.output),\n\t\t\t\t\terror: kind.data.error ?? null,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"loop\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowLoopEntry\",\n\t\t\t\tval: {\n\t\t\t\t\tstate: encodeCbor(kind.data.state),\n\t\t\t\t\titeration: kind.data.iteration,\n\t\t\t\t\toutput: encodeOptionalCbor(kind.data.output),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"sleep\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowSleepEntry\",\n\t\t\t\tval: {\n\t\t\t\t\tdeadline: toU64(kind.data.deadline),\n\t\t\t\t\tstate: toWorkflowSleepState(kind.data.state),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"message\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowMessageEntry\",\n\t\t\t\tval: {\n\t\t\t\t\tname: kind.data.name,\n\t\t\t\t\tmessageData: encodeCbor(kind.data.data),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"rollback_checkpoint\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowRollbackCheckpointEntry\",\n\t\t\t\tval: { name: kind.data.name },\n\t\t\t};\n\t\tcase \"join\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowJoinEntry\",\n\t\t\t\tval: {\n\t\t\t\t\tbranches: toWorkflowBranchStatusMap(kind.data.branches),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"race\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowRaceEntry\",\n\t\t\t\tval: {\n\t\t\t\t\twinner: kind.data.winner ?? null,\n\t\t\t\t\tbranches: toWorkflowBranchStatusMap(kind.data.branches),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"removed\":\n\t\t\treturn {\n\t\t\t\ttag: \"WorkflowRemovedEntry\",\n\t\t\t\tval: {\n\t\t\t\t\toriginalType: kind.data.originalType,\n\t\t\t\t\toriginalName: kind.data.originalName ?? null,\n\t\t\t\t},\n\t\t\t};\n\t\tdefault:\n\t\t\tassertUnreachable(kind as never);\n\t}\n}\n\nfunction toWorkflowEntry(entry: WorkflowHistoryEntry): transport.WorkflowEntry {\n\treturn {\n\t\tid: entry.id,\n\t\tlocation: toWorkflowLocation(entry.location),\n\t\tkind: toWorkflowEntryKind(entry.kind),\n\t};\n}\n\nfunction toWorkflowEntryStatus(\n\tstatus: EntryStatus,\n): transport.WorkflowEntryStatus {\n\tswitch (status) {\n\t\tcase \"pending\":\n\t\t\treturn transport.WorkflowEntryStatus.PENDING;\n\t\tcase \"running\":\n\t\t\treturn transport.WorkflowEntryStatus.RUNNING;\n\t\tcase \"completed\":\n\t\t\treturn transport.WorkflowEntryStatus.COMPLETED;\n\t\tcase \"failed\":\n\t\t\treturn transport.WorkflowEntryStatus.FAILED;\n\t\tcase \"exhausted\":\n\t\t\treturn transport.WorkflowEntryStatus.EXHAUSTED;\n\t\tdefault:\n\t\t\tassertUnreachable(status as never);\n\t}\n}\n\nfunction toWorkflowSleepState(state: SleepState): transport.WorkflowSleepState {\n\tswitch (state) {\n\t\tcase \"pending\":\n\t\t\treturn transport.WorkflowSleepState.PENDING;\n\t\tcase \"completed\":\n\t\t\treturn transport.WorkflowSleepState.COMPLETED;\n\t\tcase \"interrupted\":\n\t\t\treturn transport.WorkflowSleepState.INTERRUPTED;\n\t\tdefault:\n\t\t\tassertUnreachable(state as never);\n\t}\n}\n\nfunction toWorkflowBranchStatusType(\n\tstatus: BranchStatusType,\n): transport.WorkflowBranchStatusType {\n\tswitch (status) {\n\t\tcase \"pending\":\n\t\t\treturn transport.WorkflowBranchStatusType.PENDING;\n\t\tcase \"running\":\n\t\t\treturn transport.WorkflowBranchStatusType.RUNNING;\n\t\tcase \"completed\":\n\t\t\treturn transport.WorkflowBranchStatusType.COMPLETED;\n\t\tcase \"failed\":\n\t\t\treturn transport.WorkflowBranchStatusType.FAILED;\n\t\tcase \"cancelled\":\n\t\t\treturn transport.WorkflowBranchStatusType.CANCELLED;\n\t\tdefault:\n\t\t\tassertUnreachable(status as never);\n\t}\n}\n\nfunction toWorkflowBranchStatus(\n\tstatus: BranchStatus,\n): transport.WorkflowBranchStatus {\n\treturn {\n\t\tstatus: toWorkflowBranchStatusType(status.status),\n\t\toutput: encodeOptionalCbor(status.output),\n\t\terror: status.error ?? null,\n\t};\n}\n\nfunction toWorkflowBranchStatusMap(\n\tbranches: Record<string, BranchStatus>,\n): ReadonlyMap<string, transport.WorkflowBranchStatus> {\n\treturn new Map(\n\t\tObject.entries(branches).map(([name, status]) => [\n\t\t\tname,\n\t\t\ttoWorkflowBranchStatus(status),\n\t\t]),\n\t);\n}\n\nfunction toWorkflowEntryMetadata(\n\tmetadata: WorkflowEntryMetadataSnapshot,\n): transport.WorkflowEntryMetadata {\n\tconst rollbackCompletedAt = (\n\t\tmetadata as WorkflowEntryMetadataSnapshot & {\n\t\t\trollbackCompletedAt?: number;\n\t\t}\n\t).rollbackCompletedAt;\n\tconst rollbackError = (\n\t\tmetadata as WorkflowEntryMetadataSnapshot & {\n\t\t\trollbackError?: string | null;\n\t\t}\n\t).rollbackError;\n\n\treturn {\n\t\tstatus: toWorkflowEntryStatus(metadata.status),\n\t\terror: metadata.error ?? null,\n\t\tattempts: metadata.attempts,\n\t\tlastAttemptAt: toU64(metadata.lastAttemptAt),\n\t\tcreatedAt: toU64(metadata.createdAt),\n\t\tcompletedAt:\n\t\t\tmetadata.completedAt === undefined\n\t\t\t\t? null\n\t\t\t\t: toU64(metadata.completedAt),\n\t\trollbackCompletedAt:\n\t\t\trollbackCompletedAt === undefined\n\t\t\t\t? null\n\t\t\t\t: toU64(rollbackCompletedAt),\n\t\trollbackError: rollbackError ?? null,\n\t};\n}\n\nfunction toWorkflowHistory(\n\tsnapshot: WorkflowHistorySnapshot,\n): transport.WorkflowHistory {\n\tconst entryMetadata = new Map<string, transport.WorkflowEntryMetadata>();\n\tfor (const [id, metadata] of snapshot.entryMetadata) {\n\t\tentryMetadata.set(id, toWorkflowEntryMetadata(metadata));\n\t}\n\n\treturn {\n\t\tnameRegistry: [...snapshot.nameRegistry],\n\t\tentries: snapshot.entries.map(toWorkflowEntry),\n\t\tentryMetadata,\n\t};\n}\n"]}
|