rivetkit 2.3.0-rc.8 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/client.d.ts +481 -74
- package/dist/browser/client.js +174 -148
- package/dist/browser/client.js.map +1 -1
- package/dist/browser/inspector/client.js +47 -18
- package/dist/browser/inspector/client.js.map +1 -1
- package/dist/tsup/actor/errors.cjs +2 -2
- package/dist/tsup/actor/errors.d.cts +1 -1
- package/dist/tsup/actor/errors.d.ts +1 -1
- package/dist/tsup/actor/errors.js +1 -1
- package/dist/tsup/agent-os/index.cjs +2160 -2086
- package/dist/tsup/agent-os/index.cjs.map +1 -1
- package/dist/tsup/agent-os/index.d.cts +479 -73
- package/dist/tsup/agent-os/index.d.ts +479 -73
- package/dist/tsup/agent-os/index.js +2160 -2086
- package/dist/tsup/agent-os/index.js.map +1 -1
- package/dist/tsup/{chunk-KY3CERZR.js → chunk-2OTRTA3J.js} +7 -21
- package/dist/tsup/chunk-2OTRTA3J.js.map +1 -0
- package/dist/tsup/{chunk-HGW6PBWR.cjs → chunk-3677IIOV.cjs} +11 -25
- package/dist/tsup/chunk-3677IIOV.cjs.map +1 -0
- package/dist/tsup/{chunk-OT7FF6GB.cjs → chunk-47HHIEXH.cjs} +24 -9
- package/dist/tsup/chunk-47HHIEXH.cjs.map +1 -0
- package/dist/tsup/{chunk-EMFKMVJR.js → chunk-4JDSFJS5.js} +69 -58
- package/dist/tsup/chunk-4JDSFJS5.js.map +1 -0
- package/dist/tsup/{chunk-7HLFSAJP.cjs → chunk-7QKCIVAY.cjs} +225 -214
- package/dist/tsup/chunk-7QKCIVAY.cjs.map +1 -0
- package/dist/tsup/{chunk-AWTPTUQ7.cjs → chunk-B6VUNZUD.cjs} +10 -10
- package/dist/tsup/{chunk-AWTPTUQ7.cjs.map → chunk-B6VUNZUD.cjs.map} +1 -1
- package/dist/tsup/{chunk-D3T3ZBSY.js → chunk-BEI24WTI.js} +2 -2
- package/dist/tsup/{chunk-TMLOKTRB.js → chunk-BRP62GZC.js} +1 -1
- package/dist/tsup/chunk-BRP62GZC.js.map +1 -0
- package/dist/tsup/{chunk-D5G75T7J.js → chunk-DPIMKYNB.js} +61 -2
- package/dist/tsup/chunk-DPIMKYNB.js.map +1 -0
- package/dist/tsup/{chunk-BATTOVHF.cjs → chunk-DXXJPH55.cjs} +40 -13
- package/dist/tsup/chunk-DXXJPH55.cjs.map +1 -0
- package/dist/tsup/{chunk-3YY5S6TV.js → chunk-HXUEHHJF.js} +2 -2
- package/dist/tsup/chunk-HXUEHHJF.js.map +1 -0
- package/dist/tsup/{chunk-4BPKKZJO.cjs → chunk-I4LN3FNT.cjs} +10 -10
- package/dist/tsup/chunk-I4LN3FNT.cjs.map +1 -0
- package/dist/tsup/{chunk-PCBNKI2J.js → chunk-JZ7TWV65.js} +1 -1
- package/dist/tsup/chunk-JZ7TWV65.js.map +1 -0
- package/dist/tsup/{chunk-63WNTDRC.cjs → chunk-KORQB2IR.cjs} +1 -1
- package/dist/tsup/{chunk-63WNTDRC.cjs.map → chunk-KORQB2IR.cjs.map} +1 -1
- package/dist/tsup/{chunk-6TQSSJ4F.cjs → chunk-LVTBW2RE.cjs} +3 -3
- package/dist/tsup/{chunk-6TQSSJ4F.cjs.map → chunk-LVTBW2RE.cjs.map} +1 -1
- package/dist/tsup/{chunk-4JU3IPG2.js → chunk-MEHBWPLJ.js} +6 -6
- package/dist/tsup/chunk-MEHBWPLJ.js.map +1 -0
- package/dist/tsup/{chunk-SRNOPUC6.cjs → chunk-NIY3RSPX.cjs} +62 -3
- package/dist/tsup/chunk-NIY3RSPX.cjs.map +1 -0
- package/dist/tsup/{chunk-UZXQEGVJ.js → chunk-P2GNQ4RN.js} +4 -4
- package/dist/tsup/{chunk-UZXQEGVJ.js.map → chunk-P2GNQ4RN.js.map} +1 -1
- package/dist/tsup/{chunk-VUGENVIK.js → chunk-UMZVD6DQ.js} +22 -7
- package/dist/tsup/chunk-UMZVD6DQ.js.map +1 -0
- package/dist/tsup/{chunk-LD5YASJU.cjs → chunk-VE2X4KMG.cjs} +2 -2
- package/dist/tsup/{chunk-LD5YASJU.cjs.map → chunk-VE2X4KMG.cjs.map} +1 -1
- package/dist/tsup/{chunk-GBG63SUG.js → chunk-VTTFNQQI.js} +32 -5
- package/dist/tsup/chunk-VTTFNQQI.js.map +1 -0
- package/dist/tsup/{chunk-2NDZ7JCR.cjs → chunk-ZA7FLHKH.cjs} +1 -1
- package/dist/tsup/chunk-ZA7FLHKH.cjs.map +1 -0
- package/dist/tsup/client/mod.cjs +9 -9
- package/dist/tsup/client/mod.d.cts +5 -5
- package/dist/tsup/client/mod.d.ts +5 -5
- 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/{config-Ak1lv4gF.d.ts → config-BxWAw3iH.d.ts} +512 -27
- package/dist/tsup/{config-DU_xj4qZ.d.cts → config-CZQQ-mso.d.cts} +512 -27
- package/dist/tsup/{config-CxjGYf4K.d.ts → config-D49x8NpL.d.cts} +1 -2
- package/dist/tsup/{config-CxjGYf4K.d.cts → config-D49x8NpL.d.ts} +1 -2
- package/dist/tsup/{context-DAAp4Lpg.d.ts → context-Bw7xq8w3.d.cts} +8 -8
- package/dist/tsup/{context-Dt_L55q8.d.cts → context-D8QA76sV.d.ts} +8 -8
- package/dist/tsup/db/drizzle.cjs +3 -3
- package/dist/tsup/db/drizzle.d.cts +1 -1
- package/dist/tsup/db/drizzle.d.ts +1 -1
- package/dist/tsup/db/drizzle.js +1 -1
- package/dist/tsup/db/mod.cjs +2 -2
- package/dist/tsup/db/mod.d.cts +2 -2
- package/dist/tsup/db/mod.d.ts +2 -2
- package/dist/tsup/db/mod.js +1 -1
- package/dist/tsup/dynamic/mod.cjs +24 -0
- package/dist/tsup/dynamic/mod.cjs.map +1 -0
- package/dist/tsup/dynamic/mod.d.cts +37 -0
- package/dist/tsup/dynamic/mod.d.ts +37 -0
- package/dist/tsup/dynamic/mod.js +24 -0
- package/dist/tsup/dynamic/mod.js.map +1 -0
- package/dist/tsup/inspector/mod.cjs +6 -6
- package/dist/tsup/inspector/mod.js +5 -5
- package/dist/tsup/inspector-tab/mod.cjs +173 -0
- package/dist/tsup/inspector-tab/mod.cjs.map +1 -0
- package/dist/tsup/inspector-tab/mod.d.cts +250 -0
- package/dist/tsup/inspector-tab/mod.d.ts +250 -0
- package/dist/tsup/inspector-tab/mod.js +173 -0
- package/dist/tsup/inspector-tab/mod.js.map +1 -0
- package/dist/tsup/mod.cjs +615 -348
- package/dist/tsup/mod.cjs.map +1 -1
- package/dist/tsup/mod.d.cts +5 -5
- package/dist/tsup/mod.d.ts +5 -5
- package/dist/tsup/mod.js +511 -244
- package/dist/tsup/mod.js.map +1 -1
- package/dist/tsup/test/mod.cjs +21 -18
- package/dist/tsup/test/mod.cjs.map +1 -1
- package/dist/tsup/test/mod.d.cts +4 -4
- package/dist/tsup/test/mod.d.ts +4 -4
- package/dist/tsup/test/mod.js +18 -15
- package/dist/tsup/test/mod.js.map +1 -1
- package/dist/tsup/{utils-DVekpm4I.d.cts → utils-DQosb24I.d.cts} +1 -1
- package/dist/tsup/{utils-DVekpm4I.d.ts → utils-DQosb24I.d.ts} +1 -1
- 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 +279 -279
- package/dist/tsup/workflow/mod.cjs.map +1 -1
- package/dist/tsup/workflow/mod.d.cts +6 -6
- package/dist/tsup/workflow/mod.d.ts +6 -6
- package/dist/tsup/workflow/mod.js +380 -380
- package/dist/tsup/workflow/mod.js.map +1 -1
- package/package.json +29 -9
- package/src/actor/config.ts +156 -51
- package/src/actor/contexts/index.ts +7 -2
- package/src/actor/definition.ts +17 -19
- package/src/actor/driver.ts +3 -3
- package/src/actor/errors.ts +8 -2
- package/src/actor/instance/mod.ts +26 -34
- package/src/actor/keys.ts +1 -1
- package/src/actor/mod.ts +22 -20
- package/src/actor/schema.ts +2 -2
- package/src/agent-os/actor/index.ts +38 -18
- package/src/agent-os/actor/preview.ts +1 -2
- package/src/agent-os/config.ts +1 -1
- package/src/agent-os/fs/database-vfs.ts +1 -1
- package/src/agent-os/index.ts +16 -15
- package/src/client/actor-common.ts +87 -54
- package/src/client/actor-conn.ts +11 -11
- package/src/client/actor-handle.ts +69 -52
- package/src/client/actor-query.ts +5 -5
- package/src/client/errors.ts +1 -1
- package/src/client/lifecycle-errors.ts +2 -4
- package/src/client/query.ts +1 -1
- package/src/client/queue.ts +8 -4
- package/src/client/raw-utils.ts +8 -6
- package/src/client/resolve-gateway-target.ts +1 -1
- package/src/client/utils.ts +2 -6
- package/src/common/actor-websocket.ts +3 -1
- package/src/common/bare/actor-persist/v1.ts +205 -163
- package/src/common/bare/actor-persist/v2.ts +265 -213
- package/src/common/bare/actor-persist/v3.ts +176 -172
- package/src/common/bare/actor-persist/v4.ts +254 -253
- package/src/common/bare/transport/v1.ts +659 -543
- package/src/common/client-protocol-versioned.ts +66 -64
- package/src/common/database/config.ts +2 -8
- package/src/common/database/native-database.ts +1 -1
- package/src/common/database/shared.ts +1 -0
- package/src/common/encoding.ts +13 -17
- package/src/common/engine.ts +28 -1
- package/src/common/eventsource.ts +1 -1
- package/src/common/inline-websocket-adapter.ts +3 -2
- package/src/common/router.ts +13 -17
- package/src/common/utils.ts +1 -2
- package/src/common/websocket-interface.ts +1 -1
- package/src/db/mod.ts +1 -1
- package/src/devtools-loader/index.ts +4 -7
- package/src/devtools-loader/serve-devtools.ts +26 -0
- package/src/drivers/engine/actor-driver.ts +48 -46
- package/src/dynamic/instance.ts +32 -0
- package/src/dynamic/internal.ts +50 -0
- package/src/dynamic/isolate-runtime.ts +66 -0
- package/src/dynamic/mod.ts +32 -0
- package/src/engine-client/actor-http-client.ts +3 -3
- package/src/engine-client/actor-websocket-client.ts +5 -5
- package/src/engine-client/api-endpoints.ts +51 -2
- package/src/engine-client/api-utils.ts +2 -2
- package/src/engine-client/driver.ts +1 -1
- package/src/engine-client/mod.ts +5 -3
- package/src/engine-client/ws-proxy.ts +9 -4
- package/src/inspector/client.browser.ts +5 -11
- package/src/inspector/mod.ts +1 -3
- package/src/inspector-tab/mod.ts +315 -0
- package/src/registry/config/envoy.ts +1 -2
- package/src/registry/config/index.ts +40 -16
- package/src/registry/index.ts +226 -83
- package/src/registry/napi-runtime.ts +46 -12
- package/src/registry/native-validation.ts +10 -12
- package/src/registry/native.ts +307 -164
- package/src/registry/process-metrics.ts +90 -23
- package/src/registry/runtime.ts +53 -6
- package/src/registry/wasm-runtime.ts +30 -3
- package/src/serde.ts +1 -1
- package/src/serverless/configure.ts +18 -7
- package/src/test/mod.ts +11 -8
- package/src/utils/endpoint-parser.ts +1 -1
- package/src/utils/env-vars.ts +6 -0
- package/src/utils/router.ts +1 -1
- package/src/utils/serve.ts +4 -5
- package/src/utils.ts +1 -2
- package/src/workflow/context.ts +30 -29
- package/src/workflow/driver.ts +4 -6
- package/src/workflow/inspector.ts +2 -2
- package/src/workflow/mod.ts +15 -17
- package/dist/tsup/chunk-2NDZ7JCR.cjs.map +0 -1
- package/dist/tsup/chunk-3YY5S6TV.js.map +0 -1
- package/dist/tsup/chunk-4BPKKZJO.cjs.map +0 -1
- package/dist/tsup/chunk-4JU3IPG2.js.map +0 -1
- package/dist/tsup/chunk-7HLFSAJP.cjs.map +0 -1
- package/dist/tsup/chunk-BATTOVHF.cjs.map +0 -1
- package/dist/tsup/chunk-D5G75T7J.js.map +0 -1
- package/dist/tsup/chunk-EMFKMVJR.js.map +0 -1
- package/dist/tsup/chunk-GBG63SUG.js.map +0 -1
- package/dist/tsup/chunk-HGW6PBWR.cjs.map +0 -1
- package/dist/tsup/chunk-KY3CERZR.js.map +0 -1
- package/dist/tsup/chunk-OT7FF6GB.cjs.map +0 -1
- package/dist/tsup/chunk-PCBNKI2J.js.map +0 -1
- package/dist/tsup/chunk-SRNOPUC6.cjs.map +0 -1
- package/dist/tsup/chunk-TMLOKTRB.js.map +0 -1
- package/dist/tsup/chunk-VUGENVIK.js.map +0 -1
- package/dist/tsup/process-metrics-NW754INA.js +0 -118
- package/dist/tsup/process-metrics-NW754INA.js.map +0 -1
- package/dist/tsup/process-metrics-TYAGKCEJ.cjs +0 -118
- package/dist/tsup/process-metrics-TYAGKCEJ.cjs.map +0 -1
- /package/dist/tsup/{chunk-D3T3ZBSY.js.map → chunk-BEI24WTI.js.map} +0 -0
package/src/registry/native.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { VirtualWebSocket } from "@rivetkit/virtual-websocket";
|
|
|
2
2
|
import {
|
|
3
3
|
ACTOR_CONTEXT_INTERNAL_SYMBOL,
|
|
4
4
|
CONN_STATE_MANAGER_SYMBOL,
|
|
5
|
-
RAW_STATE_SYMBOL,
|
|
6
5
|
getRunFunction,
|
|
7
6
|
getRunInspectorConfig,
|
|
7
|
+
RAW_STATE_SYMBOL,
|
|
8
8
|
type WorkflowInspectorConfig,
|
|
9
9
|
} from "@/actor/config";
|
|
10
10
|
import type { AnyActorDefinition } from "@/actor/definition";
|
|
@@ -33,11 +33,8 @@ import { convertRegistryConfigToClientConfig } from "@/client/config";
|
|
|
33
33
|
import { HEADER_CONN_PARAMS } from "@/common/actor-router-consts";
|
|
34
34
|
import type { AnyDatabaseProvider } from "@/common/database/config";
|
|
35
35
|
import { wrapJsNativeDatabase } from "@/common/database/native-database";
|
|
36
|
+
import { assertJsonCompatValue, type JsonCompatValue } from "@/common/encoding";
|
|
36
37
|
import { decodeWorkflowHistoryTransport } from "@/common/inspector-transport";
|
|
37
|
-
import {
|
|
38
|
-
assertJsonCompatValue,
|
|
39
|
-
type JsonCompatValue,
|
|
40
|
-
} from "@/common/encoding";
|
|
41
38
|
import { deconstructError, stringifyError } from "@/common/utils";
|
|
42
39
|
import type {
|
|
43
40
|
RivetCloseEvent,
|
|
@@ -52,14 +49,9 @@ import type {
|
|
|
52
49
|
RuntimeKind,
|
|
53
50
|
SqliteBackend,
|
|
54
51
|
} from "@/registry/config";
|
|
55
|
-
import {
|
|
56
|
-
decodeCborCompat,
|
|
57
|
-
decodeCborJsonCompat,
|
|
58
|
-
encodeCborCompat,
|
|
59
|
-
} from "@/serde";
|
|
52
|
+
import { decodeCborCompat, encodeCborCompat } from "@/serde";
|
|
60
53
|
import { getEnvUniversal, VERSION } from "@/utils";
|
|
61
54
|
import { logger } from "./log";
|
|
62
|
-
import { createWriteThroughProxy } from "./write-through-proxy";
|
|
63
55
|
import { loadNapiRuntime } from "./napi-runtime";
|
|
64
56
|
import {
|
|
65
57
|
type NativeValidationConfig,
|
|
@@ -79,12 +71,16 @@ import type {
|
|
|
79
71
|
RuntimeActorConfig,
|
|
80
72
|
RuntimeBytes,
|
|
81
73
|
RuntimeHttpResponse,
|
|
74
|
+
RuntimeInspectorTabEntry,
|
|
82
75
|
RuntimeQueueMessage,
|
|
83
76
|
RuntimeServeConfig,
|
|
84
77
|
RuntimeStateDeltaPayload,
|
|
85
78
|
WebSocketHandle,
|
|
86
79
|
} from "./runtime";
|
|
87
80
|
import { loadWasmRuntime } from "./wasm-runtime";
|
|
81
|
+
import nodeFs from "node:fs";
|
|
82
|
+
import nodePath from "node:path";
|
|
83
|
+
import { createWriteThroughProxy } from "./write-through-proxy";
|
|
88
84
|
|
|
89
85
|
const textEncoder = new TextEncoder();
|
|
90
86
|
const textDecoder = new TextDecoder();
|
|
@@ -249,6 +245,14 @@ type NativePersistActorState = {
|
|
|
249
245
|
state: unknown;
|
|
250
246
|
isInOnStateChange: boolean;
|
|
251
247
|
connStates: Map<string, NativePersistConnState>;
|
|
248
|
+
// Memoized deep write-through proxy and the state object it wraps. Rebuilt
|
|
249
|
+
// only when the underlying state object identity changes.
|
|
250
|
+
stateProxy?: unknown;
|
|
251
|
+
stateProxyTarget?: unknown;
|
|
252
|
+
// Set when a coalesced save and onStateChange flush is pending for the
|
|
253
|
+
// current event loop tick.
|
|
254
|
+
saveScheduled?: boolean;
|
|
255
|
+
pendingSaveHandle?: ReturnType<typeof setImmediate>;
|
|
252
256
|
};
|
|
253
257
|
type NativeDestroyGate = {
|
|
254
258
|
destroyCompletion?: Promise<void>;
|
|
@@ -420,21 +424,33 @@ function clearNativeRuntimeState(
|
|
|
420
424
|
async function cleanupNativeSleepRuntimeState(
|
|
421
425
|
runtime: CoreRuntime,
|
|
422
426
|
ctx: ActorContextHandle,
|
|
427
|
+
afterTrackedWorkDrained?: () => Promise<void>,
|
|
423
428
|
): Promise<void> {
|
|
424
|
-
|
|
429
|
+
// The bounded wait gives shutdown work one grace-period chance to finish.
|
|
430
|
+
// Drained means all tracked shutdown work completed before the deadline, so
|
|
431
|
+
// we can save final state and clear runtime state immediately. If it did not
|
|
432
|
+
// drain, close database handles now, then defer the final save and clear until
|
|
433
|
+
// the tracked work finishes without a deadline.
|
|
425
434
|
const drained = await runtime.actorWaitForTrackedShutdownWork(ctx);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
435
|
+
if (!drained) {
|
|
436
|
+
await closeNativeDatabaseClient(runtime, ctx);
|
|
437
|
+
await closeNativeSqlDatabase(runtime, ctx);
|
|
438
|
+
void runtime
|
|
439
|
+
.actorWaitForTrackedShutdownWorkUnbounded(ctx)
|
|
440
|
+
.then(async () => {
|
|
441
|
+
await afterTrackedWorkDrained?.();
|
|
442
|
+
clearNativeRuntimeState(runtime, ctx);
|
|
443
|
+
})
|
|
444
|
+
.catch((error) => {
|
|
445
|
+
logger().warn({
|
|
446
|
+
msg: "deferred native sleep cleanup failed",
|
|
447
|
+
error: stringifyError(error),
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
437
451
|
}
|
|
452
|
+
|
|
453
|
+
await afterTrackedWorkDrained?.();
|
|
438
454
|
await closeNativeDatabaseClient(runtime, ctx);
|
|
439
455
|
await closeNativeSqlDatabase(runtime, ctx);
|
|
440
456
|
clearNativeRuntimeState(runtime, ctx);
|
|
@@ -607,13 +623,21 @@ function decodeValue<T>(value?: RuntimeBytes | null): T {
|
|
|
607
623
|
return undefined as T;
|
|
608
624
|
}
|
|
609
625
|
|
|
610
|
-
return
|
|
626
|
+
return decodeCborCompat(value);
|
|
611
627
|
}
|
|
612
628
|
|
|
613
629
|
function encodeValue(value: unknown): RuntimeBytes {
|
|
614
630
|
return encodeCborCompat(value as JsonCompatValue);
|
|
615
631
|
}
|
|
616
632
|
|
|
633
|
+
function normalizeArgs(value: unknown): unknown[] {
|
|
634
|
+
return Array.isArray(value)
|
|
635
|
+
? value
|
|
636
|
+
: value === undefined || value === null
|
|
637
|
+
? []
|
|
638
|
+
: [value];
|
|
639
|
+
}
|
|
640
|
+
|
|
617
641
|
function unwrapTsfnPayload<T>(error: unknown, payload: T): T {
|
|
618
642
|
if (error !== null && error !== undefined) {
|
|
619
643
|
throw error;
|
|
@@ -944,6 +968,7 @@ function serializeWorkflowEntryKind(
|
|
|
944
968
|
}
|
|
945
969
|
}
|
|
946
970
|
|
|
971
|
+
// TODO: Switch inspector routes to CBOR encoding
|
|
947
972
|
function serializeWorkflowHistoryForJson(data: ArrayBuffer | null): {
|
|
948
973
|
nameRegistry: string[];
|
|
949
974
|
entries: Array<{
|
|
@@ -977,7 +1002,7 @@ function serializeWorkflowHistoryForJson(data: ArrayBuffer | null): {
|
|
|
977
1002
|
|
|
978
1003
|
const history = decodeWorkflowHistoryTransport(data);
|
|
979
1004
|
|
|
980
|
-
return {
|
|
1005
|
+
return jsonSafe({
|
|
981
1006
|
nameRegistry: [...history.nameRegistry],
|
|
982
1007
|
entries: history.entries.map((entry) => ({
|
|
983
1008
|
id: entry.id,
|
|
@@ -1007,7 +1032,7 @@ function serializeWorkflowHistoryForJson(data: ArrayBuffer | null): {
|
|
|
1007
1032
|
],
|
|
1008
1033
|
),
|
|
1009
1034
|
),
|
|
1010
|
-
};
|
|
1035
|
+
});
|
|
1011
1036
|
}
|
|
1012
1037
|
|
|
1013
1038
|
function toHttpJsonCompatible<T>(value: T): T {
|
|
@@ -1079,14 +1104,9 @@ function wrapNativeCallback<Args extends Array<unknown>, Result>(
|
|
|
1079
1104
|
|
|
1080
1105
|
function decodeArgs(value?: RuntimeBytes | null): unknown[] {
|
|
1081
1106
|
const decoded = decodeValue<unknown>(value);
|
|
1082
|
-
return
|
|
1083
|
-
? decoded
|
|
1084
|
-
: decoded === undefined
|
|
1085
|
-
? []
|
|
1086
|
-
: [decoded];
|
|
1107
|
+
return normalizeArgs(decoded);
|
|
1087
1108
|
}
|
|
1088
1109
|
|
|
1089
|
-
|
|
1090
1110
|
function buildRequest(init: {
|
|
1091
1111
|
method: string;
|
|
1092
1112
|
uri: string;
|
|
@@ -1182,11 +1202,15 @@ class NativeConnAdapter {
|
|
|
1182
1202
|
|
|
1183
1203
|
get state(): unknown {
|
|
1184
1204
|
const nextState = this.#readState();
|
|
1185
|
-
return createWriteThroughProxy(
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1205
|
+
return createWriteThroughProxy(
|
|
1206
|
+
nextState,
|
|
1207
|
+
(nextValue) => {
|
|
1208
|
+
this.#writeState(nextValue, { writeNative: true });
|
|
1209
|
+
},
|
|
1210
|
+
(newValue) => {
|
|
1211
|
+
assertJsonCompatValue(newValue);
|
|
1212
|
+
},
|
|
1213
|
+
);
|
|
1190
1214
|
}
|
|
1191
1215
|
|
|
1192
1216
|
set state(value: unknown) {
|
|
@@ -1662,7 +1686,12 @@ class NativeQueueAdapter {
|
|
|
1662
1686
|
signal?: AbortSignal;
|
|
1663
1687
|
},
|
|
1664
1688
|
) {
|
|
1665
|
-
|
|
1689
|
+
const { token, cleanup } = await createCancellationTokenHandle(
|
|
1690
|
+
this.#runtime,
|
|
1691
|
+
options?.signal,
|
|
1692
|
+
);
|
|
1693
|
+
|
|
1694
|
+
try {
|
|
1666
1695
|
await callNative(() =>
|
|
1667
1696
|
this.#runtime.actorQueueWaitForNamesAvailable(
|
|
1668
1697
|
this.#ctx,
|
|
@@ -1670,57 +1699,11 @@ class NativeQueueAdapter {
|
|
|
1670
1699
|
{
|
|
1671
1700
|
timeoutMs: options?.timeout,
|
|
1672
1701
|
},
|
|
1702
|
+
token,
|
|
1673
1703
|
),
|
|
1674
1704
|
);
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
const deadline =
|
|
1679
|
-
options.timeout === undefined
|
|
1680
|
-
? undefined
|
|
1681
|
-
: Date.now() + options.timeout;
|
|
1682
|
-
|
|
1683
|
-
for (;;) {
|
|
1684
|
-
if (options.signal.aborted) {
|
|
1685
|
-
throw actorAbortedError();
|
|
1686
|
-
}
|
|
1687
|
-
|
|
1688
|
-
const remainingTimeout =
|
|
1689
|
-
deadline === undefined
|
|
1690
|
-
? undefined
|
|
1691
|
-
: Math.max(0, deadline - Date.now());
|
|
1692
|
-
const sliceTimeout =
|
|
1693
|
-
remainingTimeout === undefined
|
|
1694
|
-
? 100
|
|
1695
|
-
: Math.min(remainingTimeout, 100);
|
|
1696
|
-
|
|
1697
|
-
try {
|
|
1698
|
-
await callNative(() =>
|
|
1699
|
-
this.#runtime.actorQueueWaitForNamesAvailable(
|
|
1700
|
-
this.#ctx,
|
|
1701
|
-
[...names],
|
|
1702
|
-
{
|
|
1703
|
-
timeoutMs: sliceTimeout,
|
|
1704
|
-
},
|
|
1705
|
-
),
|
|
1706
|
-
);
|
|
1707
|
-
return;
|
|
1708
|
-
} catch (error) {
|
|
1709
|
-
if (
|
|
1710
|
-
(error as { group?: string; code?: string }).group ===
|
|
1711
|
-
"queue" &&
|
|
1712
|
-
(error as { group?: string; code?: string }).code ===
|
|
1713
|
-
"timed_out"
|
|
1714
|
-
) {
|
|
1715
|
-
if (
|
|
1716
|
-
remainingTimeout === undefined ||
|
|
1717
|
-
remainingTimeout > 100
|
|
1718
|
-
) {
|
|
1719
|
-
continue;
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
throw error;
|
|
1723
|
-
}
|
|
1705
|
+
} finally {
|
|
1706
|
+
cleanup?.();
|
|
1724
1707
|
}
|
|
1725
1708
|
}
|
|
1726
1709
|
|
|
@@ -2339,9 +2322,7 @@ class NativeConnectionMap implements ReadonlyMap<string, NativeConnAdapter> {
|
|
|
2339
2322
|
|
|
2340
2323
|
get(key: string): NativeConnAdapter | undefined {
|
|
2341
2324
|
const conns = callNativeSync(() => this.#runtime.actorConns(this.#ctx));
|
|
2342
|
-
const conn = conns.find(
|
|
2343
|
-
(c) => this.#runtime.connId(c) === key,
|
|
2344
|
-
);
|
|
2325
|
+
const conn = conns.find((c) => this.#runtime.connId(c) === key);
|
|
2345
2326
|
if (!conn) return undefined;
|
|
2346
2327
|
return this.#connToAdapter(conn);
|
|
2347
2328
|
}
|
|
@@ -2353,23 +2334,39 @@ class NativeConnectionMap implements ReadonlyMap<string, NativeConnAdapter> {
|
|
|
2353
2334
|
|
|
2354
2335
|
keys(): MapIterator<string> {
|
|
2355
2336
|
const conns = callNativeSync(() => this.#runtime.actorConns(this.#ctx));
|
|
2356
|
-
return conns
|
|
2337
|
+
return conns
|
|
2338
|
+
.map((c) => this.#runtime.connId(c))
|
|
2339
|
+
[Symbol.iterator]() satisfies MapIterator<string>;
|
|
2357
2340
|
}
|
|
2358
2341
|
|
|
2359
2342
|
values(): MapIterator<NativeConnAdapter> {
|
|
2360
2343
|
const conns = callNativeSync(() => this.#runtime.actorConns(this.#ctx));
|
|
2361
|
-
return conns
|
|
2344
|
+
return conns
|
|
2345
|
+
.map((c) => this.#connToAdapter(c))
|
|
2346
|
+
[Symbol.iterator]() satisfies MapIterator<NativeConnAdapter>;
|
|
2362
2347
|
}
|
|
2363
2348
|
|
|
2364
2349
|
entries(): MapIterator<[string, NativeConnAdapter]> {
|
|
2365
2350
|
const conns = callNativeSync(() => this.#runtime.actorConns(this.#ctx));
|
|
2366
|
-
return conns
|
|
2367
|
-
|
|
2368
|
-
|
|
2351
|
+
return conns
|
|
2352
|
+
.map(
|
|
2353
|
+
(c) =>
|
|
2354
|
+
[this.#runtime.connId(c), this.#connToAdapter(c)] as [
|
|
2355
|
+
string,
|
|
2356
|
+
NativeConnAdapter,
|
|
2357
|
+
],
|
|
2358
|
+
)
|
|
2359
|
+
[Symbol.iterator]() satisfies MapIterator<
|
|
2360
|
+
[string, NativeConnAdapter]
|
|
2361
|
+
>;
|
|
2369
2362
|
}
|
|
2370
2363
|
|
|
2371
2364
|
forEach(
|
|
2372
|
-
callback: (
|
|
2365
|
+
callback: (
|
|
2366
|
+
value: NativeConnAdapter,
|
|
2367
|
+
key: string,
|
|
2368
|
+
map: ReadonlyMap<string, NativeConnAdapter>,
|
|
2369
|
+
) => void,
|
|
2373
2370
|
thisArg?: unknown,
|
|
2374
2371
|
): void {
|
|
2375
2372
|
const conns = callNativeSync(() => this.#runtime.actorConns(this.#ctx));
|
|
@@ -2484,17 +2481,29 @@ export class ActorContextHandleAdapter {
|
|
|
2484
2481
|
if (!this.#stateEnabled) {
|
|
2485
2482
|
throw stateNotEnabledError();
|
|
2486
2483
|
}
|
|
2484
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
2487
2485
|
const nextState = this.#readState();
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2486
|
+
// Reading `c.state` rebuilds the deep write-through proxy, which
|
|
2487
|
+
// allocates fresh on-change caches and rewraps the whole tree. Memoize
|
|
2488
|
+
// the proxy keyed on the underlying state object so repeated reads and
|
|
2489
|
+
// deep read cascades reuse a single proxy.
|
|
2490
|
+
if (
|
|
2491
|
+
actorState.stateProxy === undefined ||
|
|
2492
|
+
actorState.stateProxyTarget !== nextState
|
|
2493
|
+
) {
|
|
2494
|
+
actorState.stateProxyTarget = nextState;
|
|
2495
|
+
actorState.stateProxy = createWriteThroughProxy(
|
|
2496
|
+
nextState,
|
|
2497
|
+
(nextValue) => {
|
|
2498
|
+
this.#writeState(nextValue, { scheduleSave: true });
|
|
2499
|
+
},
|
|
2500
|
+
(newValue) => {
|
|
2501
|
+
this.#assertCanMutateState();
|
|
2502
|
+
assertJsonCompatValue(newValue);
|
|
2503
|
+
},
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2506
|
+
return actorState.stateProxy;
|
|
2498
2507
|
}
|
|
2499
2508
|
|
|
2500
2509
|
set state(value: unknown) {
|
|
@@ -2571,7 +2580,11 @@ export class ActorContextHandleAdapter {
|
|
|
2571
2580
|
|
|
2572
2581
|
get conns(): ReadonlyMap<string, NativeConnAdapter> {
|
|
2573
2582
|
if (!this.#connMap) {
|
|
2574
|
-
this.#connMap = new NativeConnectionMap(
|
|
2583
|
+
this.#connMap = new NativeConnectionMap(
|
|
2584
|
+
this.#runtime,
|
|
2585
|
+
this.#ctx,
|
|
2586
|
+
this.#schemas,
|
|
2587
|
+
);
|
|
2575
2588
|
}
|
|
2576
2589
|
return this.#connMap;
|
|
2577
2590
|
}
|
|
@@ -2804,41 +2817,22 @@ export class ActorContextHandleAdapter {
|
|
|
2804
2817
|
}
|
|
2805
2818
|
|
|
2806
2819
|
keepAwake<T>(promise: Promise<T>): Promise<T> {
|
|
2807
|
-
const startedAt = Date.now();
|
|
2808
|
-
logger().debug({
|
|
2809
|
-
msg: "keepAwake registered",
|
|
2810
|
-
at: startedAt,
|
|
2811
|
-
});
|
|
2812
2820
|
const trackedPromise = Promise.resolve(promise)
|
|
2813
|
-
.
|
|
2814
|
-
()
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
},
|
|
2820
|
-
(error) => {
|
|
2821
|
-
logger().warn({
|
|
2822
|
-
msg: "keepAwake promise rejected",
|
|
2823
|
-
durationMs: Date.now() - startedAt,
|
|
2824
|
-
error: stringifyError(error),
|
|
2825
|
-
});
|
|
2826
|
-
},
|
|
2827
|
-
)
|
|
2821
|
+
.catch((error) => {
|
|
2822
|
+
logger().warn({
|
|
2823
|
+
msg: "keepAwake promise rejected",
|
|
2824
|
+
error: stringifyError(error),
|
|
2825
|
+
});
|
|
2826
|
+
})
|
|
2828
2827
|
.then(() => null);
|
|
2829
2828
|
try {
|
|
2830
2829
|
callNativeSync(() =>
|
|
2831
2830
|
this.#runtime.actorKeepAwake(this.#ctx, trackedPromise),
|
|
2832
2831
|
);
|
|
2833
2832
|
} catch (error) {
|
|
2834
|
-
if (isClosedTaskRegistrationError(error)) {
|
|
2835
|
-
|
|
2836
|
-
msg: "keepAwake registration dropped (teardown already started); promise will not delay grace",
|
|
2837
|
-
error: stringifyError(error),
|
|
2838
|
-
});
|
|
2839
|
-
return promise;
|
|
2833
|
+
if (!isClosedTaskRegistrationError(error)) {
|
|
2834
|
+
throw error;
|
|
2840
2835
|
}
|
|
2841
|
-
throw error;
|
|
2842
2836
|
}
|
|
2843
2837
|
return promise;
|
|
2844
2838
|
}
|
|
@@ -2911,6 +2905,7 @@ export class ActorContextHandleAdapter {
|
|
|
2911
2905
|
}
|
|
2912
2906
|
|
|
2913
2907
|
sleep(): void {
|
|
2908
|
+
this.#flushStateChange();
|
|
2914
2909
|
callNativeSync(() => this.#runtime.actorSleep(this.#ctx));
|
|
2915
2910
|
}
|
|
2916
2911
|
|
|
@@ -2934,6 +2929,9 @@ export class ActorContextHandleAdapter {
|
|
|
2934
2929
|
}
|
|
2935
2930
|
|
|
2936
2931
|
async dispose(): Promise<void> {
|
|
2932
|
+
// Flush any save coalesced for this tick before the context is torn
|
|
2933
|
+
// down so the request-save and onStateChange always run.
|
|
2934
|
+
this.#flushStateChange();
|
|
2937
2935
|
this.#abortSignalCleanup?.();
|
|
2938
2936
|
this.#sql = undefined;
|
|
2939
2937
|
}
|
|
@@ -2969,13 +2967,12 @@ export class ActorContextHandleAdapter {
|
|
|
2969
2967
|
scheduleSave: boolean;
|
|
2970
2968
|
},
|
|
2971
2969
|
): void {
|
|
2972
|
-
encodeValue(value);
|
|
2973
2970
|
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
2974
2971
|
actorState.state = value;
|
|
2975
2972
|
if (!options.scheduleSave) {
|
|
2976
2973
|
return;
|
|
2977
2974
|
}
|
|
2978
|
-
this.#
|
|
2975
|
+
this.#scheduleSave();
|
|
2979
2976
|
}
|
|
2980
2977
|
|
|
2981
2978
|
#assertCanMutateState(): void {
|
|
@@ -2985,9 +2982,33 @@ export class ActorContextHandleAdapter {
|
|
|
2985
2982
|
}
|
|
2986
2983
|
}
|
|
2987
2984
|
|
|
2988
|
-
|
|
2985
|
+
// Coalesce the request-save and onStateChange work to once per event loop
|
|
2986
|
+
// tick. A synchronous burst of mutations (for example
|
|
2987
|
+
// `Object.assign(c.state, ...)`) would otherwise cross the NAPI boundary and
|
|
2988
|
+
// run onStateChange once per field, re-serializing the whole state each time
|
|
2989
|
+
// and pinning the event loop on large state.
|
|
2990
|
+
#scheduleSave(): void {
|
|
2989
2991
|
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
2990
|
-
|
|
2992
|
+
if (actorState.saveScheduled) {
|
|
2993
|
+
return;
|
|
2994
|
+
}
|
|
2995
|
+
actorState.saveScheduled = true;
|
|
2996
|
+
actorState.pendingSaveHandle = setImmediate(() => {
|
|
2997
|
+
this.#flushStateChange();
|
|
2998
|
+
});
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
#flushStateChange(): void {
|
|
3002
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
3003
|
+
if (!actorState.saveScheduled) {
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
actorState.saveScheduled = false;
|
|
3007
|
+
if (actorState.pendingSaveHandle !== undefined) {
|
|
3008
|
+
clearImmediate(actorState.pendingSaveHandle);
|
|
3009
|
+
actorState.pendingSaveHandle = undefined;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
2991
3012
|
callNativeSync(() =>
|
|
2992
3013
|
this.#runtime.actorRequestSave(this.#ctx, { immediate: false }),
|
|
2993
3014
|
);
|
|
@@ -3309,9 +3330,87 @@ function buildActorConfig(
|
|
|
3309
3330
|
actions: Object.keys((config.actions ?? {}) as Record<string, unknown>)
|
|
3310
3331
|
.sort()
|
|
3311
3332
|
.map((name) => ({ name })),
|
|
3333
|
+
inspectorTabs: buildInspectorTabs(config.inspector),
|
|
3312
3334
|
};
|
|
3313
3335
|
}
|
|
3314
3336
|
|
|
3337
|
+
function buildInspectorTabs(
|
|
3338
|
+
inspector: unknown,
|
|
3339
|
+
): Array<RuntimeInspectorTabEntry> | undefined {
|
|
3340
|
+
if (!inspector || typeof inspector !== "object") return undefined;
|
|
3341
|
+
const tabs = (inspector as { tabs?: unknown }).tabs;
|
|
3342
|
+
if (!Array.isArray(tabs) || tabs.length === 0) return undefined;
|
|
3343
|
+
return tabs.map((raw) => {
|
|
3344
|
+
const entry = raw as {
|
|
3345
|
+
id: string;
|
|
3346
|
+
label?: string;
|
|
3347
|
+
source?: string;
|
|
3348
|
+
icon?: string;
|
|
3349
|
+
hidden?: boolean;
|
|
3350
|
+
};
|
|
3351
|
+
if (entry.hidden === true) {
|
|
3352
|
+
return { id: entry.id, hidden: true };
|
|
3353
|
+
}
|
|
3354
|
+
// Resolve the author's source path against the current working
|
|
3355
|
+
// directory so the Rust runtime gets an absolute path. The author
|
|
3356
|
+
// runs the actor process from their project root by convention.
|
|
3357
|
+
const resolved =
|
|
3358
|
+
entry.source !== undefined
|
|
3359
|
+
? nodePath.resolve(entry.source)
|
|
3360
|
+
: undefined;
|
|
3361
|
+
if (resolved !== undefined) {
|
|
3362
|
+
validateInspectorTabSource(entry.id, resolved);
|
|
3363
|
+
}
|
|
3364
|
+
return {
|
|
3365
|
+
id: entry.id,
|
|
3366
|
+
label: entry.label,
|
|
3367
|
+
icon: entry.icon,
|
|
3368
|
+
source: resolved,
|
|
3369
|
+
};
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
function validateInspectorTabSource(tabId: string, resolved: string): void {
|
|
3374
|
+
// Catch obviously dangerous misconfigurations at registry construction
|
|
3375
|
+
// rather than silently exposing the wrong subtree over the unauthenticated
|
|
3376
|
+
// `/inspector/custom-tabs/<id>/*` route. Fail loudly so misconfigured
|
|
3377
|
+
// actors never start.
|
|
3378
|
+
if (resolved === nodePath.parse(resolved).root) {
|
|
3379
|
+
throw new Error(
|
|
3380
|
+
`inspector.tabs[id="${tabId}"].source resolves to the filesystem root (${resolved}). ` +
|
|
3381
|
+
"Point it at the tab's own static-asset directory instead.",
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3384
|
+
let stat: import("node:fs").Stats;
|
|
3385
|
+
try {
|
|
3386
|
+
stat = nodeFs.statSync(resolved);
|
|
3387
|
+
} catch (err) {
|
|
3388
|
+
const code = (err as NodeJS.ErrnoException)?.code;
|
|
3389
|
+
if (code === "ENOENT") {
|
|
3390
|
+
throw new Error(
|
|
3391
|
+
`inspector.tabs[id="${tabId}"].source (${resolved}) does not exist.`,
|
|
3392
|
+
);
|
|
3393
|
+
}
|
|
3394
|
+
if (code === "EACCES") {
|
|
3395
|
+
throw new Error(
|
|
3396
|
+
`inspector.tabs[id="${tabId}"].source (${resolved}) is not readable (EACCES).`,
|
|
3397
|
+
);
|
|
3398
|
+
}
|
|
3399
|
+
throw new Error(
|
|
3400
|
+
`inspector.tabs[id="${tabId}"].source (${resolved}) could not be stat'd: ${
|
|
3401
|
+
(err as Error)?.message ?? err
|
|
3402
|
+
}`,
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
if (!stat.isDirectory()) {
|
|
3406
|
+
throw new Error(
|
|
3407
|
+
`inspector.tabs[id="${tabId}"].source (${resolved}) must be a directory, got ${
|
|
3408
|
+
stat.isFile() ? "file" : "non-directory"
|
|
3409
|
+
}.`,
|
|
3410
|
+
);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3315
3414
|
export function buildNativeFactory(
|
|
3316
3415
|
runtime: CoreRuntime,
|
|
3317
3416
|
registryConfig: RegistryConfig,
|
|
@@ -3742,14 +3841,38 @@ export function buildNativeFactory(
|
|
|
3742
3841
|
404,
|
|
3743
3842
|
);
|
|
3744
3843
|
}
|
|
3745
|
-
const body = (await jsRequest.json()) as {
|
|
3844
|
+
const body = (await jsRequest.json()) as {
|
|
3845
|
+
args?: unknown;
|
|
3846
|
+
properties?: unknown;
|
|
3847
|
+
};
|
|
3848
|
+
if (body.args !== undefined && body.properties !== undefined) {
|
|
3849
|
+
return jsonResponse(
|
|
3850
|
+
{ error: "use either args or properties, not both" },
|
|
3851
|
+
{ status: 400 },
|
|
3852
|
+
);
|
|
3853
|
+
}
|
|
3854
|
+
if (
|
|
3855
|
+
body.properties !== undefined &&
|
|
3856
|
+
(body.properties === null ||
|
|
3857
|
+
typeof body.properties !== "object" ||
|
|
3858
|
+
Array.isArray(body.properties))
|
|
3859
|
+
) {
|
|
3860
|
+
return jsonResponse(
|
|
3861
|
+
{ error: "properties must be an object" },
|
|
3862
|
+
{ status: 400 },
|
|
3863
|
+
);
|
|
3864
|
+
}
|
|
3865
|
+
const args =
|
|
3866
|
+
body.properties !== undefined
|
|
3867
|
+
? [body.properties]
|
|
3868
|
+
: normalizeArgs(body.args);
|
|
3746
3869
|
try {
|
|
3747
3870
|
const output = await action(
|
|
3748
3871
|
actorCtx,
|
|
3749
3872
|
...validateActionArgs(
|
|
3750
3873
|
schemaConfig.actionInputSchemas,
|
|
3751
3874
|
actionName,
|
|
3752
|
-
|
|
3875
|
+
args,
|
|
3753
3876
|
),
|
|
3754
3877
|
);
|
|
3755
3878
|
return jsonResponse({ output });
|
|
@@ -3948,26 +4071,35 @@ export function buildNativeFactory(
|
|
|
3948
4071
|
async (error: unknown, payload: { ctx: ActorContextHandle }) => {
|
|
3949
4072
|
const { ctx } = unwrapTsfnPayload(error, payload);
|
|
3950
4073
|
const actorCtx = makeActorCtx(ctx);
|
|
4074
|
+
// TODO: Move this save hook into cleanupNativeSleepRuntimeState
|
|
4075
|
+
// so immediate and deferred sleep cleanup share one save-state
|
|
4076
|
+
// path instead of passing a callback through cleanup.
|
|
4077
|
+
const saveActorState = async () => {
|
|
4078
|
+
if (runtime.kind === "wasm") {
|
|
4079
|
+
// Wasm cannot use the native context save helper here because
|
|
4080
|
+
// the runtime owns the serialized state handoff.
|
|
4081
|
+
await runtime.actorSaveState(
|
|
4082
|
+
ctx,
|
|
4083
|
+
actorCtx.serializeForTick("save"),
|
|
4084
|
+
);
|
|
4085
|
+
} else {
|
|
4086
|
+
await actorCtx.saveState({
|
|
4087
|
+
immediate: true,
|
|
4088
|
+
});
|
|
4089
|
+
}
|
|
4090
|
+
};
|
|
3951
4091
|
try {
|
|
3952
4092
|
if (onSleep) {
|
|
3953
|
-
|
|
3954
|
-
await onSleep(actorCtx);
|
|
3955
|
-
} finally {
|
|
3956
|
-
if (runtime.kind === "wasm") {
|
|
3957
|
-
// Wasm cannot use the native context save helper here because
|
|
3958
|
-
// the runtime owns the serialized state handoff.
|
|
3959
|
-
await runtime.actorSaveState(
|
|
3960
|
-
ctx,
|
|
3961
|
-
actorCtx.serializeForTick("save"),
|
|
3962
|
-
);
|
|
3963
|
-
} else {
|
|
3964
|
-
await actorCtx.saveState({ immediate: true });
|
|
3965
|
-
}
|
|
3966
|
-
}
|
|
4093
|
+
await onSleep(actorCtx);
|
|
3967
4094
|
}
|
|
4095
|
+
await saveActorState();
|
|
3968
4096
|
} finally {
|
|
3969
4097
|
try {
|
|
3970
|
-
await cleanupNativeSleepRuntimeState(
|
|
4098
|
+
await cleanupNativeSleepRuntimeState(
|
|
4099
|
+
runtime,
|
|
4100
|
+
ctx,
|
|
4101
|
+
saveActorState,
|
|
4102
|
+
);
|
|
3971
4103
|
} finally {
|
|
3972
4104
|
await actorCtx.dispose();
|
|
3973
4105
|
}
|
|
@@ -4624,10 +4756,29 @@ export async function buildServeConfig(
|
|
|
4624
4756
|
serverlessMaxStartPayloadBytes: config.serverless.maxStartPayloadBytes,
|
|
4625
4757
|
};
|
|
4626
4758
|
|
|
4627
|
-
|
|
4759
|
+
// Always best-effort resolve the npm-installed engine binary and hand its
|
|
4760
|
+
// path to the core. The core alone decides whether to actually spawn a local
|
|
4761
|
+
// engine (its `should_manage_engine`, based on the endpoint + spawn mode), so
|
|
4762
|
+
// JS must not duplicate that decision here. Only JS knows the npm
|
|
4763
|
+
// `node_modules` layout, so it resolves the path; if no binary is available
|
|
4764
|
+
// (remote-only install, unsupported platform, optional deps skipped), leave
|
|
4765
|
+
// it unset and let the core report `engine.binary_unavailable` if it actually
|
|
4766
|
+
// needs one.
|
|
4767
|
+
try {
|
|
4628
4768
|
const { getEnginePath } = await loadEngineCli();
|
|
4629
4769
|
serveConfig.engineBinaryPath = getEnginePath();
|
|
4770
|
+
} catch (error) {
|
|
4771
|
+
// The npm-installed engine binary could not be resolved. The core still
|
|
4772
|
+
// decides whether it needs to spawn a local engine; if it does, it will
|
|
4773
|
+
// fail with engine.binary_unavailable (auto-download is off in the napi
|
|
4774
|
+
// runtime). Warn so the cause is actionable.
|
|
4775
|
+
logger().warn({
|
|
4776
|
+
msg: "could not resolve a local engine binary; if a local engine must be spawned it will fail with engine.binary_unavailable — set RIVET_ENGINE_BINARY_PATH or install the @rivetkit/engine-cli platform package",
|
|
4777
|
+
error: stringifyError(error),
|
|
4778
|
+
});
|
|
4630
4779
|
}
|
|
4780
|
+
serveConfig.engineHost = config.engineHost;
|
|
4781
|
+
serveConfig.enginePort = config.enginePort;
|
|
4631
4782
|
if (config.test?.enabled) {
|
|
4632
4783
|
serveConfig.inspectorTestToken =
|
|
4633
4784
|
getEnvUniversal("_RIVET_TEST_INSPECTOR_TOKEN") ?? "token";
|
|
@@ -4686,14 +4837,6 @@ export async function buildConfiguredRegistry(config: RegistryConfig): Promise<{
|
|
|
4686
4837
|
serveConfig: RuntimeServeConfig;
|
|
4687
4838
|
}> {
|
|
4688
4839
|
const runtime = await loadConfiguredRuntime(config);
|
|
4689
|
-
if (runtime.kind === "napi") {
|
|
4690
|
-
// Start Node.js runtime health metrics collection (event loop lag,
|
|
4691
|
-
// GC, heap, CPU, libuv handles). Only available on the native NAPI
|
|
4692
|
-
// runtime; wasm/edge hosts do not expose perf_hooks/v8 the same
|
|
4693
|
-
// way and have no Rust-side prometheus collectors loaded.
|
|
4694
|
-
const { startProcessMetrics } = await import("./process-metrics");
|
|
4695
|
-
startProcessMetrics();
|
|
4696
|
-
}
|
|
4697
4840
|
return buildRegistryWithRuntime(
|
|
4698
4841
|
normalizeRuntimeConfig(config, runtime),
|
|
4699
4842
|
runtime,
|