rivetkit 2.3.11-rc.9 → 2.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-HOZ3ZLZ2.js → chunk-3ZR542SK.js} +6 -6
- 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-XOOESJY7.js → chunk-FWROC5LA.js} +2 -2
- package/dist/tsup/{chunk-V5CVATRU.cjs → chunk-GLIPKKEC.cjs} +7 -7
- package/dist/tsup/{chunk-V5CVATRU.cjs.map → chunk-GLIPKKEC.cjs.map} +1 -1
- package/dist/tsup/{chunk-T5XVTVQY.cjs → chunk-GN2EUMEW.cjs} +3 -3
- package/dist/tsup/{chunk-T5XVTVQY.cjs.map → chunk-GN2EUMEW.cjs.map} +1 -1
- package/dist/tsup/{chunk-T56IZ42M.cjs → chunk-GU7ML3KE.cjs} +13 -3
- package/dist/tsup/chunk-GU7ML3KE.cjs.map +1 -0
- package/dist/tsup/{chunk-IOLZ3ONU.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-QVCO6DTZ.js → chunk-MO2SSUOR.js} +8 -7
- package/dist/tsup/chunk-MO2SSUOR.js.map +1 -0
- package/dist/tsup/{chunk-LZ4TSBIP.js → chunk-OUENKQHB.js} +13 -3
- package/dist/tsup/chunk-OUENKQHB.js.map +1 -0
- package/dist/tsup/{chunk-6SBPWTXJ.cjs → chunk-PUZY2XCD.cjs} +10 -9
- package/dist/tsup/chunk-PUZY2XCD.cjs.map +1 -0
- package/dist/tsup/{chunk-C5TORAVP.js → chunk-QFJ24MBU.js} +27 -2
- package/dist/tsup/{chunk-C5TORAVP.js.map → chunk-QFJ24MBU.js.map} +1 -1
- package/dist/tsup/{chunk-BZWKY6WO.cjs → chunk-RXXDNJXB.cjs} +140 -140
- package/dist/tsup/{chunk-BZWKY6WO.cjs.map → chunk-RXXDNJXB.cjs.map} +1 -1
- package/dist/tsup/{chunk-IMRDHZM2.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 +743 -247
- 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 +670 -174
- 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 +474 -161
- 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/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-6SBPWTXJ.cjs.map +0 -1
- package/dist/tsup/chunk-7AFZMFPQ.js.map +0 -1
- package/dist/tsup/chunk-HNHLBM3Y.cjs.map +0 -1
- package/dist/tsup/chunk-HOZ3ZLZ2.js.map +0 -1
- package/dist/tsup/chunk-IMRDHZM2.cjs.map +0 -1
- package/dist/tsup/chunk-LZ4TSBIP.js.map +0 -1
- package/dist/tsup/chunk-QVCO6DTZ.js.map +0 -1
- package/dist/tsup/chunk-QWLJCP3X.js.map +0 -1
- package/dist/tsup/chunk-T56IZ42M.cjs.map +0 -1
- package/dist/tsup/chunk-UEULFXUI.cjs.map +0 -1
- /package/dist/tsup/{chunk-XOOESJY7.js.map → chunk-FWROC5LA.js.map} +0 -0
- /package/dist/tsup/{chunk-IOLZ3ONU.js.map → chunk-JQ2AZ5FN.js.map} +0 -0
package/src/registry/native.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
disposeRunInspector,
|
|
16
16
|
getRunFunction,
|
|
17
17
|
getRunInspectorConfig,
|
|
18
|
+
getRunInspectorKind,
|
|
19
|
+
hasRunInspectorConfig,
|
|
18
20
|
RAW_STATE_SYMBOL,
|
|
19
21
|
type ScheduledEventInfo,
|
|
20
22
|
type WorkflowInspectorConfig,
|
|
@@ -44,7 +46,15 @@ import {
|
|
|
44
46
|
} from "@/client/client";
|
|
45
47
|
import { convertRegistryConfigToClientConfig } from "@/client/config";
|
|
46
48
|
import { HEADER_CONN_PARAMS } from "@/common/actor-router-consts";
|
|
47
|
-
import type {
|
|
49
|
+
import type {
|
|
50
|
+
AnyDatabaseProvider,
|
|
51
|
+
SqliteProfilingOptions,
|
|
52
|
+
} from "@/common/database/config";
|
|
53
|
+
import {
|
|
54
|
+
bindNativeStateTransactionContext,
|
|
55
|
+
db as defaultDb,
|
|
56
|
+
registerNativeStateTransactionOpener,
|
|
57
|
+
} from "@/common/database/mod";
|
|
48
58
|
import { wrapJsNativeDatabase } from "@/common/database/native-database";
|
|
49
59
|
import { assertJsonCompatValue, type JsonCompatValue } from "@/common/encoding";
|
|
50
60
|
import { isResponseLike, type ResponseLike } from "@/common/fetch-like";
|
|
@@ -83,6 +93,7 @@ import {
|
|
|
83
93
|
validateQueueBody,
|
|
84
94
|
validateQueueComplete,
|
|
85
95
|
} from "./native-validation";
|
|
96
|
+
import { RunHandlerCoordinator } from "./run-handler-coordinator";
|
|
86
97
|
import type {
|
|
87
98
|
ActorContextHandle,
|
|
88
99
|
ActorFactoryHandle,
|
|
@@ -129,10 +140,14 @@ type NativeOnStateChangeHandler = (
|
|
|
129
140
|
) => void | Promise<void>;
|
|
130
141
|
type NativePersistConnState = {
|
|
131
142
|
state: unknown;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
type NativeStateTransactionScope = {
|
|
146
|
+
actorContext: ActorContextHandleAdapter;
|
|
147
|
+
actorStateBaseline: unknown;
|
|
148
|
+
connectionStateBaselines: Map<string, Uint8Array>;
|
|
149
|
+
committed: boolean;
|
|
150
|
+
release: () => void;
|
|
136
151
|
};
|
|
137
152
|
|
|
138
153
|
const defaultRuntimeLoaders: RuntimeLoaders = {
|
|
@@ -278,14 +293,14 @@ type NativePersistActorState = {
|
|
|
278
293
|
state: unknown;
|
|
279
294
|
isInOnStateChange: boolean;
|
|
280
295
|
connStates: Map<string, NativePersistConnState>;
|
|
281
|
-
// Memoized deep write-through proxy and the state object it wraps. Rebuilt
|
|
282
|
-
// only when the underlying state object identity changes.
|
|
283
|
-
stateProxy?: unknown;
|
|
284
|
-
stateProxyTarget?: unknown;
|
|
285
296
|
// Set when a coalesced save and onStateChange flush is pending for the
|
|
286
297
|
// current event loop tick.
|
|
287
298
|
saveScheduled?: boolean;
|
|
288
299
|
pendingSaveHandle?: ReturnType<typeof setImmediate>;
|
|
300
|
+
activeStateTransactionOwner?: symbol;
|
|
301
|
+
pendingStateTransactionOwners?: Set<symbol>;
|
|
302
|
+
stateTransactionTail?: Promise<void>;
|
|
303
|
+
stateTransactionSaveDeferred?: boolean;
|
|
289
304
|
};
|
|
290
305
|
type NativeDestroyGate = {
|
|
291
306
|
destroyCompletion?: Promise<void>;
|
|
@@ -371,6 +386,15 @@ function stateMutationReentrantError(): RivetError {
|
|
|
371
386
|
);
|
|
372
387
|
}
|
|
373
388
|
|
|
389
|
+
function stateTransactionConflictError(): RivetError {
|
|
390
|
+
return new RivetError(
|
|
391
|
+
"actor",
|
|
392
|
+
"state_transaction_conflict",
|
|
393
|
+
"State cannot be mutated by another action while an experimental state-aware database transaction is active.",
|
|
394
|
+
{ public: true, statusCode: 409 },
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
374
398
|
function databaseNotConfiguredError(): RivetError {
|
|
375
399
|
return new RivetError(
|
|
376
400
|
"actor",
|
|
@@ -388,6 +412,14 @@ function databaseClientNotReadyError(): RivetError {
|
|
|
388
412
|
{ public: true },
|
|
389
413
|
);
|
|
390
414
|
}
|
|
415
|
+
function runHandlerNotConfiguredError(): RivetError {
|
|
416
|
+
return new RivetError(
|
|
417
|
+
"actor",
|
|
418
|
+
"run_handler_unavailable",
|
|
419
|
+
"Cannot schedule a run wake because this actor does not define a run handler.",
|
|
420
|
+
{ public: true, statusCode: 409 },
|
|
421
|
+
);
|
|
422
|
+
}
|
|
391
423
|
|
|
392
424
|
function stateNotEnabledError(): RivetError {
|
|
393
425
|
return new RivetError(
|
|
@@ -540,10 +572,11 @@ function getOrCreateNativeSqlDatabase(
|
|
|
540
572
|
execute: (sql, params) => runtime.actorSqlExecute(ctx, sql, params),
|
|
541
573
|
executeBatch: (statements) =>
|
|
542
574
|
runtime.actorSqlExecuteBatch(ctx, statements),
|
|
543
|
-
beginTransaction: async (timeoutMs) => {
|
|
575
|
+
beginTransaction: async (timeoutMs, name) => {
|
|
544
576
|
const transaction = await runtime.actorSqlBeginTransaction(
|
|
545
577
|
ctx,
|
|
546
578
|
timeoutMs,
|
|
579
|
+
name,
|
|
547
580
|
);
|
|
548
581
|
return {
|
|
549
582
|
exec: (sql) =>
|
|
@@ -559,6 +592,40 @@ function getOrCreateNativeSqlDatabase(
|
|
|
559
592
|
runtime.actorSqlTransactionRollback(transaction),
|
|
560
593
|
};
|
|
561
594
|
},
|
|
595
|
+
beginStateTransaction: async (timeoutMs, context) => {
|
|
596
|
+
const scope = context as NativeStateTransactionScope | undefined;
|
|
597
|
+
if (!scope) {
|
|
598
|
+
throw new Error(
|
|
599
|
+
"actor state transaction context is not configured",
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
const transaction = await runtime.actorBeginStateTransaction(
|
|
603
|
+
ctx,
|
|
604
|
+
timeoutMs,
|
|
605
|
+
);
|
|
606
|
+
return {
|
|
607
|
+
exec: async () => {
|
|
608
|
+
throw new Error(
|
|
609
|
+
"actor state transactions only support single-statement execute calls",
|
|
610
|
+
);
|
|
611
|
+
},
|
|
612
|
+
execute: (sql, params) =>
|
|
613
|
+
runtime.actorStateTransactionExecute(
|
|
614
|
+
transaction,
|
|
615
|
+
sql,
|
|
616
|
+
params,
|
|
617
|
+
),
|
|
618
|
+
commit: async () => {
|
|
619
|
+
await runtime.actorStateTransactionCommit(
|
|
620
|
+
transaction,
|
|
621
|
+
scope.actorContext.serializeForTick("save"),
|
|
622
|
+
);
|
|
623
|
+
scope.committed = true;
|
|
624
|
+
},
|
|
625
|
+
rollback: () =>
|
|
626
|
+
runtime.actorStateTransactionRollback(transaction),
|
|
627
|
+
};
|
|
628
|
+
},
|
|
562
629
|
query: (sql, params) => runtime.actorSqlQuery(ctx, sql, params),
|
|
563
630
|
run: (sql, params) => runtime.actorSqlRun(ctx, sql, params),
|
|
564
631
|
metrics: () => runtime.actorSqlMetrics(ctx),
|
|
@@ -1234,6 +1301,9 @@ class NativeConnAdapter {
|
|
|
1234
1301
|
#schemas: NativeValidationConfig;
|
|
1235
1302
|
#ctx?: ActorContextHandle;
|
|
1236
1303
|
#queueHibernationRemoval?: (connId: string) => void;
|
|
1304
|
+
#assertCanMutateState?: () => void;
|
|
1305
|
+
#stateProxy?: unknown;
|
|
1306
|
+
#stateProxyTarget?: unknown;
|
|
1237
1307
|
|
|
1238
1308
|
constructor(
|
|
1239
1309
|
runtime: CoreRuntime,
|
|
@@ -1241,12 +1311,14 @@ class NativeConnAdapter {
|
|
|
1241
1311
|
schemas: NativeValidationConfig = {},
|
|
1242
1312
|
ctx?: ActorContextHandle,
|
|
1243
1313
|
queueHibernationRemoval?: (connId: string) => void,
|
|
1314
|
+
assertCanMutateState?: () => void,
|
|
1244
1315
|
) {
|
|
1245
1316
|
this.#runtime = runtime;
|
|
1246
1317
|
this.#conn = conn;
|
|
1247
1318
|
this.#schemas = schemas;
|
|
1248
1319
|
this.#ctx = ctx;
|
|
1249
1320
|
this.#queueHibernationRemoval = queueHibernationRemoval;
|
|
1321
|
+
this.#assertCanMutateState = assertCanMutateState;
|
|
1250
1322
|
(
|
|
1251
1323
|
this as NativeConnAdapter & {
|
|
1252
1324
|
[CONN_STATE_MANAGER_SYMBOL]?: unknown;
|
|
@@ -1285,34 +1357,38 @@ class NativeConnAdapter {
|
|
|
1285
1357
|
// allocates fresh on-change caches and rewraps the whole tree. Memoize
|
|
1286
1358
|
// the proxy keyed on the underlying state object so repeated reads and
|
|
1287
1359
|
// deep read cascades reuse a single proxy.
|
|
1288
|
-
const connState = getNativeConnPersistState(
|
|
1289
|
-
this.#runtime,
|
|
1290
|
-
this.#ctx,
|
|
1291
|
-
this.#conn,
|
|
1292
|
-
);
|
|
1293
1360
|
if (
|
|
1294
|
-
|
|
1295
|
-
|
|
1361
|
+
this.#stateProxy === undefined ||
|
|
1362
|
+
this.#stateProxyTarget !== nextState
|
|
1296
1363
|
) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1364
|
+
this.#stateProxyTarget = nextState;
|
|
1365
|
+
this.#stateProxy = this.#createStateProxy(nextState);
|
|
1299
1366
|
}
|
|
1300
|
-
return
|
|
1367
|
+
return this.#stateProxy;
|
|
1301
1368
|
}
|
|
1302
1369
|
|
|
1303
1370
|
#createStateProxy(state: unknown): unknown {
|
|
1371
|
+
const assertCurrentState = () => {
|
|
1372
|
+
this.#assertCanMutateState?.();
|
|
1373
|
+
if (this.#ctx && this.#readState() !== state) {
|
|
1374
|
+
throw stateTransactionConflictError();
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1304
1377
|
return createWriteThroughProxy(
|
|
1305
1378
|
state,
|
|
1306
1379
|
(nextValue) => {
|
|
1380
|
+
assertCurrentState();
|
|
1307
1381
|
this.#writeState(nextValue, { writeNative: true });
|
|
1308
1382
|
},
|
|
1309
1383
|
(newValue) => {
|
|
1384
|
+
assertCurrentState();
|
|
1310
1385
|
assertJsonCompatValue(newValue);
|
|
1311
1386
|
},
|
|
1312
1387
|
);
|
|
1313
1388
|
}
|
|
1314
1389
|
|
|
1315
1390
|
set state(value: unknown) {
|
|
1391
|
+
this.#assertCanMutateState?.();
|
|
1316
1392
|
const nextValue = unwrapWriteThroughProxy(value);
|
|
1317
1393
|
assertJsonCompatValue(nextValue);
|
|
1318
1394
|
this.#writeState(nextValue, { writeNative: true });
|
|
@@ -1734,8 +1810,9 @@ function wrapQueueMessage(
|
|
|
1734
1810
|
schemas: NativeValidationConfig["queues"],
|
|
1735
1811
|
) {
|
|
1736
1812
|
const name = callNativeSync(() => message.name());
|
|
1813
|
+
const id = callNativeSync(() => message.id());
|
|
1737
1814
|
return {
|
|
1738
|
-
id: Number(
|
|
1815
|
+
id: id <= BigInt(Number.MAX_SAFE_INTEGER) ? Number(id) : id,
|
|
1739
1816
|
name,
|
|
1740
1817
|
body: validateQueueBody(
|
|
1741
1818
|
schemas,
|
|
@@ -1919,6 +1996,32 @@ class NativeQueueAdapter {
|
|
|
1919
1996
|
cleanup?.();
|
|
1920
1997
|
}
|
|
1921
1998
|
}
|
|
1999
|
+
async waitForAvailable(
|
|
2000
|
+
names?: readonly string[],
|
|
2001
|
+
options?: { timeout?: number; signal?: AbortSignal },
|
|
2002
|
+
): Promise<void> {
|
|
2003
|
+
await this.waitForNamesAvailable(names ?? [], options);
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
async complete(
|
|
2007
|
+
message: { id: number | bigint; name: string },
|
|
2008
|
+
response?: unknown,
|
|
2009
|
+
): Promise<void> {
|
|
2010
|
+
const validatedResponse = validateQueueComplete(
|
|
2011
|
+
this.#schemas,
|
|
2012
|
+
message.name,
|
|
2013
|
+
response,
|
|
2014
|
+
);
|
|
2015
|
+
await callNative(() =>
|
|
2016
|
+
this.#runtime.actorQueueCompletePersisted(
|
|
2017
|
+
this.#ctx,
|
|
2018
|
+
BigInt(message.id),
|
|
2019
|
+
message.name,
|
|
2020
|
+
encodeValue(validatedResponse),
|
|
2021
|
+
),
|
|
2022
|
+
);
|
|
2023
|
+
this.#pendingCompletableMessageIds.delete(message.id.toString());
|
|
2024
|
+
}
|
|
1922
2025
|
|
|
1923
2026
|
async enqueueAndWait(
|
|
1924
2027
|
name: string,
|
|
@@ -2498,15 +2601,18 @@ class NativeConnectionMap implements ReadonlyMap<string, NativeConnAdapter> {
|
|
|
2498
2601
|
#runtime: CoreRuntime;
|
|
2499
2602
|
#ctx: ActorContextHandle;
|
|
2500
2603
|
#schemas: NativeValidationConfig;
|
|
2604
|
+
#assertCanMutateState: () => void;
|
|
2501
2605
|
|
|
2502
2606
|
constructor(
|
|
2503
2607
|
runtime: CoreRuntime,
|
|
2504
2608
|
ctx: ActorContextHandle,
|
|
2505
2609
|
schemas: NativeValidationConfig,
|
|
2610
|
+
assertCanMutateState: () => void,
|
|
2506
2611
|
) {
|
|
2507
2612
|
this.#runtime = runtime;
|
|
2508
2613
|
this.#ctx = ctx;
|
|
2509
2614
|
this.#schemas = schemas;
|
|
2615
|
+
this.#assertCanMutateState = assertCanMutateState;
|
|
2510
2616
|
}
|
|
2511
2617
|
|
|
2512
2618
|
#connToAdapter(conn: ConnHandle): NativeConnAdapter {
|
|
@@ -2522,6 +2628,7 @@ class NativeConnectionMap implements ReadonlyMap<string, NativeConnAdapter> {
|
|
|
2522
2628
|
connId,
|
|
2523
2629
|
),
|
|
2524
2630
|
),
|
|
2631
|
+
this.#assertCanMutateState,
|
|
2525
2632
|
);
|
|
2526
2633
|
}
|
|
2527
2634
|
|
|
@@ -2609,10 +2716,13 @@ export class ActorContextHandleAdapter {
|
|
|
2609
2716
|
#queue?: NativeQueueAdapter;
|
|
2610
2717
|
#request?: Request;
|
|
2611
2718
|
#schedule?: NativeScheduleAdapter;
|
|
2612
|
-
#
|
|
2613
|
-
#
|
|
2719
|
+
#run?: { setWakeAt(timestamp: number | null): Promise<void> };
|
|
2720
|
+
#runHandlerConfigured: boolean;
|
|
2614
2721
|
#onStateChange?: NativeOnStateChangeHandler;
|
|
2615
2722
|
#stateEnabled: boolean;
|
|
2723
|
+
#stateProxy?: unknown;
|
|
2724
|
+
#stateProxyTarget?: unknown;
|
|
2725
|
+
readonly #stateTransactionOwner = Symbol("actor-state-transaction-owner");
|
|
2616
2726
|
|
|
2617
2727
|
constructor(
|
|
2618
2728
|
runtime: CoreRuntime,
|
|
@@ -2622,16 +2732,16 @@ export class ActorContextHandleAdapter {
|
|
|
2622
2732
|
databaseProvider?: AnyDatabaseProvider,
|
|
2623
2733
|
request?: Request,
|
|
2624
2734
|
stateEnabled = true,
|
|
2625
|
-
runHandlerActiveProvider?: () => boolean,
|
|
2626
2735
|
onStateChange?: NativeOnStateChangeHandler,
|
|
2627
2736
|
dispatchCancelToken?: CancellationTokenHandle,
|
|
2737
|
+
runHandlerConfigured = false,
|
|
2628
2738
|
) {
|
|
2629
2739
|
this.#runtime = runtime;
|
|
2630
2740
|
this.#ctx = ctx;
|
|
2631
2741
|
this.#clientFactory = clientFactory;
|
|
2632
2742
|
this.#schemas = schemas;
|
|
2633
2743
|
this.#dispatchCancelToken = dispatchCancelToken;
|
|
2634
|
-
this.#
|
|
2744
|
+
this.#runHandlerConfigured = runHandlerConfigured;
|
|
2635
2745
|
this.#onStateChange = onStateChange;
|
|
2636
2746
|
this.#stateEnabled = stateEnabled;
|
|
2637
2747
|
if (databaseProvider) {
|
|
@@ -2654,13 +2764,6 @@ export class ActorContextHandleAdapter {
|
|
|
2654
2764
|
return this.#kv;
|
|
2655
2765
|
}
|
|
2656
2766
|
|
|
2657
|
-
get sql() {
|
|
2658
|
-
if (!this.#sql) {
|
|
2659
|
-
this.#sql = getOrCreateNativeSqlDatabase(this.#runtime, this.#ctx);
|
|
2660
|
-
}
|
|
2661
|
-
return this.#sql;
|
|
2662
|
-
}
|
|
2663
|
-
|
|
2664
2767
|
async actorRuntimeSocket(): Promise<{ path: string }> {
|
|
2665
2768
|
return await callNative(() =>
|
|
2666
2769
|
this.#runtime.actorRuntimeSocketProvision(this.#ctx),
|
|
@@ -2679,7 +2782,7 @@ export class ActorContextHandleAdapter {
|
|
|
2679
2782
|
const runtimeState = getNativeRuntimeState(this.#runtime, this.#ctx);
|
|
2680
2783
|
const cachedClient = runtimeState.databaseClient;
|
|
2681
2784
|
if (cachedClient) {
|
|
2682
|
-
this.#db = cachedClient.client;
|
|
2785
|
+
this.#db = this.#bindDatabaseClient(cachedClient.client);
|
|
2683
2786
|
return this.#db;
|
|
2684
2787
|
}
|
|
2685
2788
|
|
|
@@ -2697,29 +2800,35 @@ export class ActorContextHandleAdapter {
|
|
|
2697
2800
|
if (!this.#stateEnabled) {
|
|
2698
2801
|
throw stateNotEnabledError();
|
|
2699
2802
|
}
|
|
2700
|
-
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
2701
2803
|
const nextState = this.#readState();
|
|
2702
2804
|
// Reading `c.state` rebuilds the deep write-through proxy, which
|
|
2703
2805
|
// allocates fresh on-change caches and rewraps the whole tree. Memoize
|
|
2704
2806
|
// the proxy keyed on the underlying state object so repeated reads and
|
|
2705
2807
|
// deep read cascades reuse a single proxy.
|
|
2706
2808
|
if (
|
|
2707
|
-
|
|
2708
|
-
|
|
2809
|
+
this.#stateProxy === undefined ||
|
|
2810
|
+
this.#stateProxyTarget !== nextState
|
|
2709
2811
|
) {
|
|
2710
|
-
|
|
2711
|
-
|
|
2812
|
+
const assertCurrentState = () => {
|
|
2813
|
+
this.#assertCanMutateState();
|
|
2814
|
+
if (this.#readState() !== nextState) {
|
|
2815
|
+
throw stateTransactionConflictError();
|
|
2816
|
+
}
|
|
2817
|
+
};
|
|
2818
|
+
this.#stateProxyTarget = nextState;
|
|
2819
|
+
this.#stateProxy = createWriteThroughProxy(
|
|
2712
2820
|
nextState,
|
|
2713
2821
|
(nextValue) => {
|
|
2822
|
+
assertCurrentState();
|
|
2714
2823
|
this.#writeState(nextValue, { scheduleSave: true });
|
|
2715
2824
|
},
|
|
2716
2825
|
(newValue) => {
|
|
2717
|
-
|
|
2826
|
+
assertCurrentState();
|
|
2718
2827
|
assertJsonCompatValue(newValue);
|
|
2719
2828
|
},
|
|
2720
2829
|
);
|
|
2721
2830
|
}
|
|
2722
|
-
return
|
|
2831
|
+
return this.#stateProxy;
|
|
2723
2832
|
}
|
|
2724
2833
|
|
|
2725
2834
|
set state(value: unknown) {
|
|
@@ -2769,6 +2878,30 @@ export class ActorContextHandleAdapter {
|
|
|
2769
2878
|
return this.#queue;
|
|
2770
2879
|
}
|
|
2771
2880
|
|
|
2881
|
+
get run() {
|
|
2882
|
+
if (!this.#run) {
|
|
2883
|
+
this.#run = {
|
|
2884
|
+
setWakeAt: async (timestamp: number | null) => {
|
|
2885
|
+
if (timestamp !== null && !this.#runHandlerConfigured) {
|
|
2886
|
+
throw runHandlerNotConfiguredError();
|
|
2887
|
+
}
|
|
2888
|
+
if (
|
|
2889
|
+
timestamp !== null &&
|
|
2890
|
+
(!Number.isSafeInteger(timestamp) || timestamp < 0)
|
|
2891
|
+
) {
|
|
2892
|
+
throw new TypeError(
|
|
2893
|
+
"Run wake timestamp must be a non-negative safe integer or null",
|
|
2894
|
+
);
|
|
2895
|
+
}
|
|
2896
|
+
await callNative(() =>
|
|
2897
|
+
this.#runtime.actorSetRunWakeAt(this.#ctx, timestamp),
|
|
2898
|
+
);
|
|
2899
|
+
},
|
|
2900
|
+
};
|
|
2901
|
+
}
|
|
2902
|
+
return this.#run;
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2772
2905
|
get schedule(): NativeScheduleAdapter {
|
|
2773
2906
|
if (!this.#schedule) {
|
|
2774
2907
|
this.#schedule = new NativeScheduleAdapter(
|
|
@@ -2810,6 +2943,7 @@ export class ActorContextHandleAdapter {
|
|
|
2810
2943
|
this.#runtime,
|
|
2811
2944
|
this.#ctx,
|
|
2812
2945
|
this.#schemas,
|
|
2946
|
+
() => this.#assertCanMutateState(),
|
|
2813
2947
|
);
|
|
2814
2948
|
}
|
|
2815
2949
|
return this.#connMap;
|
|
@@ -2890,12 +3024,12 @@ export class ActorContextHandleAdapter {
|
|
|
2890
3024
|
const runtimeState = getNativeRuntimeState(this.#runtime, this.#ctx);
|
|
2891
3025
|
const cachedClient = runtimeState.databaseClient;
|
|
2892
3026
|
if (cachedClient) {
|
|
2893
|
-
this.#db = cachedClient.client;
|
|
3027
|
+
this.#db = this.#bindDatabaseClient(cachedClient.client);
|
|
2894
3028
|
return this.#db;
|
|
2895
3029
|
}
|
|
2896
3030
|
|
|
2897
3031
|
const actorId = this.actorId;
|
|
2898
|
-
const
|
|
3032
|
+
const createdClient = await this.#databaseProvider.createClient({
|
|
2899
3033
|
actorId,
|
|
2900
3034
|
kv: {
|
|
2901
3035
|
batchPut: async (entries) => {
|
|
@@ -2916,21 +3050,147 @@ export class ActorContextHandleAdapter {
|
|
|
2916
3050
|
log: {
|
|
2917
3051
|
debug: (obj) => logger().debug(obj),
|
|
2918
3052
|
},
|
|
2919
|
-
nativeDatabaseProvider:
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3053
|
+
nativeDatabaseProvider: registerNativeStateTransactionOpener(
|
|
3054
|
+
{
|
|
3055
|
+
open: async (requestedActorId) => {
|
|
3056
|
+
void requestedActorId;
|
|
3057
|
+
return getOrCreateNativeSqlDatabase(
|
|
3058
|
+
this.#runtime,
|
|
3059
|
+
this.#ctx,
|
|
3060
|
+
);
|
|
3061
|
+
},
|
|
3062
|
+
},
|
|
3063
|
+
async (timeoutMs, context) =>
|
|
3064
|
+
await getOrCreateNativeSqlDatabase(
|
|
2923
3065
|
this.#runtime,
|
|
2924
3066
|
this.#ctx,
|
|
2925
|
-
)
|
|
2926
|
-
|
|
2927
|
-
},
|
|
3067
|
+
).beginStateTransaction(timeoutMs, context),
|
|
3068
|
+
),
|
|
2928
3069
|
});
|
|
2929
3070
|
runtimeState.databaseClient = {
|
|
2930
|
-
client,
|
|
3071
|
+
client: createdClient,
|
|
2931
3072
|
};
|
|
2932
|
-
this.#db =
|
|
2933
|
-
return
|
|
3073
|
+
this.#db = this.#bindDatabaseClient(createdClient);
|
|
3074
|
+
return this.#db;
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
#bindDatabaseClient(client: unknown): unknown {
|
|
3078
|
+
return bindNativeStateTransactionContext(client, {
|
|
3079
|
+
enter: async () => await this.#enterStateTransaction(),
|
|
3080
|
+
exit: (scope) =>
|
|
3081
|
+
this.#exitStateTransaction(
|
|
3082
|
+
scope as NativeStateTransactionScope,
|
|
3083
|
+
),
|
|
3084
|
+
});
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
async #enterStateTransaction(): Promise<NativeStateTransactionScope> {
|
|
3088
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
3089
|
+
let pendingOwners = actorState.pendingStateTransactionOwners;
|
|
3090
|
+
if (!pendingOwners) {
|
|
3091
|
+
pendingOwners = new Set();
|
|
3092
|
+
actorState.pendingStateTransactionOwners = pendingOwners;
|
|
3093
|
+
}
|
|
3094
|
+
if (
|
|
3095
|
+
actorState.activeStateTransactionOwner ===
|
|
3096
|
+
this.#stateTransactionOwner ||
|
|
3097
|
+
pendingOwners.has(this.#stateTransactionOwner)
|
|
3098
|
+
) {
|
|
3099
|
+
throw stateTransactionConflictError();
|
|
3100
|
+
}
|
|
3101
|
+
pendingOwners.add(this.#stateTransactionOwner);
|
|
3102
|
+
const predecessor =
|
|
3103
|
+
actorState.stateTransactionTail ?? Promise.resolve();
|
|
3104
|
+
let release!: () => void;
|
|
3105
|
+
const hold = new Promise<void>((resolve) => {
|
|
3106
|
+
release = resolve;
|
|
3107
|
+
});
|
|
3108
|
+
actorState.stateTransactionTail = predecessor.then(
|
|
3109
|
+
async () => await hold,
|
|
3110
|
+
);
|
|
3111
|
+
try {
|
|
3112
|
+
await predecessor;
|
|
3113
|
+
pendingOwners.delete(this.#stateTransactionOwner);
|
|
3114
|
+
actorState.activeStateTransactionOwner =
|
|
3115
|
+
this.#stateTransactionOwner;
|
|
3116
|
+
return {
|
|
3117
|
+
actorContext: this,
|
|
3118
|
+
actorStateBaseline: this.#stateEnabled
|
|
3119
|
+
? structuredClone(this.#readState())
|
|
3120
|
+
: undefined,
|
|
3121
|
+
connectionStateBaselines: new Map(
|
|
3122
|
+
callNativeSync(() =>
|
|
3123
|
+
this.#runtime.actorConns(this.#ctx),
|
|
3124
|
+
).map((conn) => [
|
|
3125
|
+
callNativeSync(() => this.#runtime.connId(conn)),
|
|
3126
|
+
new Uint8Array(
|
|
3127
|
+
callNativeSync(() => this.#runtime.connState(conn)),
|
|
3128
|
+
),
|
|
3129
|
+
]),
|
|
3130
|
+
),
|
|
3131
|
+
committed: false,
|
|
3132
|
+
release,
|
|
3133
|
+
};
|
|
3134
|
+
} catch (error) {
|
|
3135
|
+
pendingOwners.delete(this.#stateTransactionOwner);
|
|
3136
|
+
if (
|
|
3137
|
+
actorState.activeStateTransactionOwner ===
|
|
3138
|
+
this.#stateTransactionOwner
|
|
3139
|
+
) {
|
|
3140
|
+
actorState.activeStateTransactionOwner = undefined;
|
|
3141
|
+
}
|
|
3142
|
+
release();
|
|
3143
|
+
throw error;
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
#exitStateTransaction(scope: NativeStateTransactionScope): void {
|
|
3148
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
3149
|
+
try {
|
|
3150
|
+
if (!scope.committed) {
|
|
3151
|
+
this.#restoreStateTransactionBaseline(scope);
|
|
3152
|
+
}
|
|
3153
|
+
} finally {
|
|
3154
|
+
if (
|
|
3155
|
+
actorState.activeStateTransactionOwner ===
|
|
3156
|
+
this.#stateTransactionOwner
|
|
3157
|
+
) {
|
|
3158
|
+
actorState.activeStateTransactionOwner = undefined;
|
|
3159
|
+
}
|
|
3160
|
+
if (actorState.stateTransactionSaveDeferred) {
|
|
3161
|
+
actorState.stateTransactionSaveDeferred = false;
|
|
3162
|
+
this.#scheduleSave();
|
|
3163
|
+
}
|
|
3164
|
+
scope.release();
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
#restoreStateTransactionBaseline(scope: NativeStateTransactionScope): void {
|
|
3169
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
3170
|
+
if (actorState.pendingSaveHandle !== undefined) {
|
|
3171
|
+
clearImmediate(actorState.pendingSaveHandle);
|
|
3172
|
+
}
|
|
3173
|
+
actorState.pendingSaveHandle = undefined;
|
|
3174
|
+
actorState.saveScheduled = false;
|
|
3175
|
+
|
|
3176
|
+
if (this.#stateEnabled) {
|
|
3177
|
+
actorState.state = structuredClone(scope.actorStateBaseline);
|
|
3178
|
+
}
|
|
3179
|
+
for (const conn of callNativeSync(() =>
|
|
3180
|
+
this.#runtime.actorConns(this.#ctx),
|
|
3181
|
+
)) {
|
|
3182
|
+
const connId = callNativeSync(() => this.#runtime.connId(conn));
|
|
3183
|
+
const baseline = scope.connectionStateBaselines.get(connId);
|
|
3184
|
+
if (baseline === undefined) {
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
callNativeSync(() => this.#runtime.connSetState(conn, baseline));
|
|
3188
|
+
const cached = actorState.connStates.get(connId);
|
|
3189
|
+
if (cached) {
|
|
3190
|
+
cached.state = decodeValue(baseline);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
this.#scheduleSave();
|
|
2934
3194
|
}
|
|
2935
3195
|
|
|
2936
3196
|
async prepare(): Promise<void> {
|
|
@@ -2953,7 +3213,6 @@ export class ActorContextHandleAdapter {
|
|
|
2953
3213
|
|
|
2954
3214
|
async closeDatabase(): Promise<void> {
|
|
2955
3215
|
this.#db = undefined;
|
|
2956
|
-
this.#sql = undefined;
|
|
2957
3216
|
await closeNativeDatabaseClient(this.#runtime, this.#ctx);
|
|
2958
3217
|
await closeNativeSqlDatabase(this.#runtime, this.#ctx);
|
|
2959
3218
|
}
|
|
@@ -2978,6 +3237,7 @@ export class ActorContextHandleAdapter {
|
|
|
2978
3237
|
maxWait?: number;
|
|
2979
3238
|
}): Promise<void> {
|
|
2980
3239
|
if (opts?.immediate) {
|
|
3240
|
+
this.#assertCanAwaitStateSave();
|
|
2981
3241
|
await callNative(() =>
|
|
2982
3242
|
this.#runtime.actorRequestSaveAndWait(this.#ctx, {
|
|
2983
3243
|
immediate: true,
|
|
@@ -3003,11 +3263,22 @@ export class ActorContextHandleAdapter {
|
|
|
3003
3263
|
async saveStateAndWorkflowBatch(
|
|
3004
3264
|
writes: Array<{ key: Uint8Array; value: Uint8Array }>,
|
|
3005
3265
|
): Promise<void> {
|
|
3266
|
+
this.#assertCanAwaitStateSave();
|
|
3006
3267
|
await callNative(() =>
|
|
3007
3268
|
this.#runtime.actorSaveStateAndWorkflowBatch(this.#ctx, writes),
|
|
3008
3269
|
);
|
|
3009
3270
|
}
|
|
3010
3271
|
|
|
3272
|
+
#assertCanAwaitStateSave(): void {
|
|
3273
|
+
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
3274
|
+
if (
|
|
3275
|
+
actorState.activeStateTransactionOwner ===
|
|
3276
|
+
this.#stateTransactionOwner
|
|
3277
|
+
) {
|
|
3278
|
+
throw stateTransactionConflictError();
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3011
3282
|
serializeForTick(reason: SerializeStateReason): RuntimeStateDeltaPayload {
|
|
3012
3283
|
void reason;
|
|
3013
3284
|
const actorState = getNativePersistState(this.#runtime, this.#ctx);
|
|
@@ -3071,10 +3342,6 @@ export class ActorContextHandleAdapter {
|
|
|
3071
3342
|
return promise;
|
|
3072
3343
|
}
|
|
3073
3344
|
|
|
3074
|
-
runHandlerActive(): boolean {
|
|
3075
|
-
return this.#runHandlerActiveProvider?.() ?? false;
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
3345
|
internalKeepAwake<T>(run: Promise<T> | (() => Promise<T>)): Promise<T> {
|
|
3079
3346
|
const promise = typeof run === "function" ? run() : run;
|
|
3080
3347
|
// Track only completion, swallowing the outcome. The real result/error
|
|
@@ -3175,7 +3442,6 @@ export class ActorContextHandleAdapter {
|
|
|
3175
3442
|
// down so the request-save and onStateChange always run.
|
|
3176
3443
|
this.#flushStateChange();
|
|
3177
3444
|
this.#abortSignalCleanup?.();
|
|
3178
|
-
this.#sql = undefined;
|
|
3179
3445
|
}
|
|
3180
3446
|
|
|
3181
3447
|
#createActorAbortSignal(): AbortSignal {
|
|
@@ -3222,6 +3488,19 @@ export class ActorContextHandleAdapter {
|
|
|
3222
3488
|
if (actorState.isInOnStateChange) {
|
|
3223
3489
|
throw stateMutationReentrantError();
|
|
3224
3490
|
}
|
|
3491
|
+
if (
|
|
3492
|
+
actorState.activeStateTransactionOwner !==
|
|
3493
|
+
this.#stateTransactionOwner &&
|
|
3494
|
+
(actorState.activeStateTransactionOwner !== undefined ||
|
|
3495
|
+
(actorState.pendingStateTransactionOwners?.size ?? 0) > 0)
|
|
3496
|
+
) {
|
|
3497
|
+
throw stateTransactionConflictError();
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
|
|
3501
|
+
/** @internal */
|
|
3502
|
+
assertCanMutateState(): void {
|
|
3503
|
+
this.#assertCanMutateState();
|
|
3225
3504
|
}
|
|
3226
3505
|
|
|
3227
3506
|
// Coalesce the request-save and onStateChange work to once per event loop
|
|
@@ -3245,6 +3524,15 @@ export class ActorContextHandleAdapter {
|
|
|
3245
3524
|
if (!actorState.saveScheduled) {
|
|
3246
3525
|
return;
|
|
3247
3526
|
}
|
|
3527
|
+
if (actorState.activeStateTransactionOwner !== undefined) {
|
|
3528
|
+
actorState.saveScheduled = false;
|
|
3529
|
+
if (actorState.pendingSaveHandle !== undefined) {
|
|
3530
|
+
clearImmediate(actorState.pendingSaveHandle);
|
|
3531
|
+
actorState.pendingSaveHandle = undefined;
|
|
3532
|
+
}
|
|
3533
|
+
actorState.stateTransactionSaveDeferred = true;
|
|
3534
|
+
return;
|
|
3535
|
+
}
|
|
3248
3536
|
actorState.saveScheduled = false;
|
|
3249
3537
|
if (actorState.pendingSaveHandle !== undefined) {
|
|
3250
3538
|
clearImmediate(actorState.pendingSaveHandle);
|
|
@@ -3446,10 +3734,6 @@ class NativeWorkflowRuntimeAdapter {
|
|
|
3446
3734
|
};
|
|
3447
3735
|
}
|
|
3448
3736
|
|
|
3449
|
-
isRunHandlerActive(): boolean {
|
|
3450
|
-
return this.#ctx.runHandlerActive();
|
|
3451
|
-
}
|
|
3452
|
-
|
|
3453
3737
|
async restartRunHandler(): Promise<void> {
|
|
3454
3738
|
await this.#ctx.restartRunHandler();
|
|
3455
3739
|
}
|
|
@@ -3497,28 +3781,33 @@ function withConnContext(
|
|
|
3497
3781
|
stateEnabled = true,
|
|
3498
3782
|
onStateChange?: NativeOnStateChangeHandler,
|
|
3499
3783
|
dispatchCancelToken?: CancellationTokenHandle,
|
|
3784
|
+
runHandlerConfigured = false,
|
|
3500
3785
|
) {
|
|
3501
|
-
|
|
3502
|
-
|
|
3786
|
+
const actorContext = new ActorContextHandleAdapter(
|
|
3787
|
+
runtime,
|
|
3788
|
+
ctx,
|
|
3789
|
+
clientFactory,
|
|
3790
|
+
schemas,
|
|
3791
|
+
databaseProvider,
|
|
3792
|
+
request,
|
|
3793
|
+
stateEnabled,
|
|
3794
|
+
onStateChange,
|
|
3795
|
+
dispatchCancelToken,
|
|
3796
|
+
runHandlerConfigured,
|
|
3797
|
+
);
|
|
3798
|
+
return Object.assign(actorContext, {
|
|
3799
|
+
conn: new NativeConnAdapter(
|
|
3503
3800
|
runtime,
|
|
3504
|
-
|
|
3505
|
-
clientFactory,
|
|
3801
|
+
conn,
|
|
3506
3802
|
schemas,
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
stateEnabled,
|
|
3510
|
-
undefined,
|
|
3511
|
-
onStateChange,
|
|
3512
|
-
dispatchCancelToken,
|
|
3513
|
-
),
|
|
3514
|
-
{
|
|
3515
|
-
conn: new NativeConnAdapter(runtime, conn, schemas, ctx, (connId) =>
|
|
3803
|
+
ctx,
|
|
3804
|
+
(connId) =>
|
|
3516
3805
|
callNativeSync(() =>
|
|
3517
3806
|
runtime.actorQueueHibernationRemoval(ctx, connId),
|
|
3518
3807
|
),
|
|
3519
|
-
),
|
|
3520
|
-
|
|
3521
|
-
);
|
|
3808
|
+
() => actorContext.assertCanMutateState(),
|
|
3809
|
+
),
|
|
3810
|
+
});
|
|
3522
3811
|
}
|
|
3523
3812
|
|
|
3524
3813
|
function buildActorConfig(
|
|
@@ -3531,12 +3820,16 @@ function buildActorConfig(
|
|
|
3531
3820
|
const canHibernate = options.canHibernateWebSocket;
|
|
3532
3821
|
const usesRemoteSqlite =
|
|
3533
3822
|
sqliteBackendForConfig(registryConfig) === "remote";
|
|
3823
|
+
const sqliteProfiling = (
|
|
3824
|
+
config.db as { sqliteProfiling?: SqliteProfilingOptions } | undefined
|
|
3825
|
+
)?.sqliteProfiling;
|
|
3534
3826
|
|
|
3535
3827
|
return {
|
|
3536
3828
|
name: options.name as string | undefined,
|
|
3537
3829
|
icon: options.icon as string | undefined,
|
|
3538
|
-
hasDatabase:
|
|
3830
|
+
hasDatabase: true,
|
|
3539
3831
|
remoteSqlite: usesRemoteSqlite,
|
|
3832
|
+
sqliteProfiling,
|
|
3540
3833
|
enableActorRuntimeSocket: options.enableActorRuntimeSocket === true,
|
|
3541
3834
|
hasState:
|
|
3542
3835
|
config.state !== undefined ||
|
|
@@ -3681,7 +3974,10 @@ export function buildNativeFactory(
|
|
|
3681
3974
|
definition: AnyActorDefinition,
|
|
3682
3975
|
): ActorFactoryHandle {
|
|
3683
3976
|
const config = definition.config as Record<string, any>;
|
|
3684
|
-
const databaseProvider = config.db as
|
|
3977
|
+
const databaseProvider = (config.db ?? defaultDb()) as Exclude<
|
|
3978
|
+
AnyDatabaseProvider,
|
|
3979
|
+
undefined
|
|
3980
|
+
>;
|
|
3685
3981
|
const actionHandlers = flattenActionHandlers(config.actions);
|
|
3686
3982
|
const schemaConfig: NativeValidationConfig = {
|
|
3687
3983
|
actionInputSchemas: flattenActionInputSchemas(
|
|
@@ -3699,16 +3995,21 @@ export function buildNativeFactory(
|
|
|
3699
3995
|
),
|
|
3700
3996
|
{ encoding: "bare" },
|
|
3701
3997
|
);
|
|
3702
|
-
const
|
|
3703
|
-
const
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
const getNativeWorkflowInspector = (ctx: ActorContextHandle) =>
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
callNativeSync(() => runtime.
|
|
3711
|
-
|
|
3998
|
+
const run = getRunFunction(config.run);
|
|
3999
|
+
const runHandlerCoordinator =
|
|
4000
|
+
getRunInspectorKind(config.run) === "workflow"
|
|
4001
|
+
? new RunHandlerCoordinator(config.run)
|
|
4002
|
+
: undefined;
|
|
4003
|
+
const getNativeWorkflowInspector = (ctx: ActorContextHandle) => {
|
|
4004
|
+
const actorId = callNativeSync(() => runtime.actorId(ctx));
|
|
4005
|
+
const restart = () =>
|
|
4006
|
+
callNativeSync(() => runtime.actorRestartRunHandler(ctx));
|
|
4007
|
+
return (runHandlerCoordinator?.getInspector(actorId, restart)
|
|
4008
|
+
?.workflow ??
|
|
4009
|
+
getRunInspectorConfig(config.run, actorId)?.workflow) as
|
|
4010
|
+
| NativeWorkflowInspectorConfig
|
|
4011
|
+
| undefined;
|
|
4012
|
+
};
|
|
3712
4013
|
const onStateChange =
|
|
3713
4014
|
typeof config.onStateChange === "function"
|
|
3714
4015
|
? (actorCtx: ActorContextHandleAdapter, nextState: unknown) => {
|
|
@@ -3741,9 +4042,9 @@ export function buildNativeFactory(
|
|
|
3741
4042
|
databaseProvider,
|
|
3742
4043
|
request,
|
|
3743
4044
|
stateEnabled,
|
|
3744
|
-
() => isNativeRunHandlerActive(ctx),
|
|
3745
4045
|
onStateChange,
|
|
3746
4046
|
cancelToken,
|
|
4047
|
+
run !== undefined,
|
|
3747
4048
|
);
|
|
3748
4049
|
const makeConnCtx = (
|
|
3749
4050
|
ctx: ActorContextHandle,
|
|
@@ -3762,6 +4063,7 @@ export function buildNativeFactory(
|
|
|
3762
4063
|
stateEnabled,
|
|
3763
4064
|
onStateChange,
|
|
3764
4065
|
cancelToken,
|
|
4066
|
+
run !== undefined,
|
|
3765
4067
|
);
|
|
3766
4068
|
const maybeHandleNativeInspectorRequest = async (
|
|
3767
4069
|
ctx: ActorContextHandle,
|
|
@@ -3830,6 +4132,7 @@ export function buildNativeFactory(
|
|
|
3830
4132
|
const workflowState = async () =>
|
|
3831
4133
|
(await getNativeWorkflowInspector(ctx)?.getState?.()) ?? null;
|
|
3832
4134
|
const actorCtx = makeActorCtx(ctx, jsRequest);
|
|
4135
|
+
const sql = getOrCreateNativeSqlDatabase(runtime, ctx);
|
|
3833
4136
|
try {
|
|
3834
4137
|
if (
|
|
3835
4138
|
url.pathname === "/inspector/state" &&
|
|
@@ -4001,7 +4304,7 @@ export function buildNativeFactory(
|
|
|
4001
4304
|
url.pathname === "/inspector/database/schema" &&
|
|
4002
4305
|
jsRequest.method === "GET"
|
|
4003
4306
|
) {
|
|
4004
|
-
const db =
|
|
4307
|
+
const db = sql;
|
|
4005
4308
|
const tables = queryRows(
|
|
4006
4309
|
await db.query(
|
|
4007
4310
|
"SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '__drizzle_%' ORDER BY name",
|
|
@@ -4055,7 +4358,7 @@ export function buildNativeFactory(
|
|
|
4055
4358
|
);
|
|
4056
4359
|
const quoted = `"${table.replace(/"/g, '""')}"`;
|
|
4057
4360
|
const rows = queryRows(
|
|
4058
|
-
await
|
|
4361
|
+
await sql.query(
|
|
4059
4362
|
`SELECT * FROM ${quoted} LIMIT ? OFFSET ?`,
|
|
4060
4363
|
[
|
|
4061
4364
|
Math.max(0, Math.min(limit, 500)),
|
|
@@ -4098,15 +4401,11 @@ export function buildNativeFactory(
|
|
|
4098
4401
|
const bindings = normalizeSqlitePropertyBindings(
|
|
4099
4402
|
body.properties as Record<string, unknown>,
|
|
4100
4403
|
);
|
|
4101
|
-
const rows = queryRows(
|
|
4102
|
-
await actorCtx.sql.query(body.sql, bindings),
|
|
4103
|
-
);
|
|
4404
|
+
const rows = queryRows(await sql.query(body.sql, bindings));
|
|
4104
4405
|
return jsonResponse({ rows: jsonSafe(rows) });
|
|
4105
4406
|
}
|
|
4106
4407
|
const args = Array.isArray(body.args) ? body.args : [];
|
|
4107
|
-
const rows = queryRows(
|
|
4108
|
-
await actorCtx.sql.query(body.sql, args),
|
|
4109
|
-
);
|
|
4408
|
+
const rows = queryRows(await sql.query(body.sql, args));
|
|
4110
4409
|
return jsonResponse({ rows: jsonSafe(rows) });
|
|
4111
4410
|
}
|
|
4112
4411
|
if (
|
|
@@ -4135,7 +4434,7 @@ export function buildNativeFactory(
|
|
|
4135
4434
|
rpcs: Object.keys(actionHandlers).sort(),
|
|
4136
4435
|
queueSize: inspectorSnapshot.queueSize,
|
|
4137
4436
|
isStateEnabled: stateEnabled,
|
|
4138
|
-
isDatabaseEnabled:
|
|
4437
|
+
isDatabaseEnabled: true,
|
|
4139
4438
|
isWorkflowEnabled:
|
|
4140
4439
|
getNativeWorkflowInspector(ctx) !== undefined,
|
|
4141
4440
|
workflowState: await workflowState(),
|
|
@@ -4391,6 +4690,7 @@ export function buildNativeFactory(
|
|
|
4391
4690
|
async (error: unknown, payload: { ctx: ActorContextHandle }) => {
|
|
4392
4691
|
const { ctx } = unwrapTsfnPayload(error, payload);
|
|
4393
4692
|
const actorCtx = makeActorCtx(ctx);
|
|
4693
|
+
const actorId = callNativeSync(() => runtime.actorId(ctx));
|
|
4394
4694
|
// TODO: Move this save hook into cleanupNativeSleepRuntimeState
|
|
4395
4695
|
// so immediate and deferred sleep cleanup share one save-state
|
|
4396
4696
|
// path instead of passing a callback through cleanup.
|
|
@@ -4421,6 +4721,8 @@ export function buildNativeFactory(
|
|
|
4421
4721
|
saveActorState,
|
|
4422
4722
|
);
|
|
4423
4723
|
} finally {
|
|
4724
|
+
runHandlerCoordinator?.destroy(actorId);
|
|
4725
|
+
disposeRunInspector(config.run, actorId);
|
|
4424
4726
|
await actorCtx.dispose();
|
|
4425
4727
|
}
|
|
4426
4728
|
}
|
|
@@ -4430,16 +4732,16 @@ export function buildNativeFactory(
|
|
|
4430
4732
|
async (error: unknown, payload: { ctx: ActorContextHandle }) => {
|
|
4431
4733
|
const { ctx } = unwrapTsfnPayload(error, payload);
|
|
4432
4734
|
const actorCtx = makeActorCtx(ctx);
|
|
4735
|
+
const actorId = callNativeSync(() => runtime.actorId(ctx));
|
|
4736
|
+
// Close run control before user cleanup so replay cannot race actor
|
|
4737
|
+
// destruction. Recreating this actor id receives a fresh controller.
|
|
4738
|
+
runHandlerCoordinator?.destroy(actorId);
|
|
4739
|
+
disposeRunInspector(config.run, actorId);
|
|
4433
4740
|
try {
|
|
4434
4741
|
if (typeof config.onDestroy === "function") {
|
|
4435
4742
|
await config.onDestroy(actorCtx);
|
|
4436
4743
|
}
|
|
4437
4744
|
} finally {
|
|
4438
|
-
const actorId = callNativeSync(() => runtime.actorId(ctx));
|
|
4439
|
-
// Release actorId-keyed state so it does not accumulate per
|
|
4440
|
-
// destroyed actor.
|
|
4441
|
-
nativeRunHandlerActiveByActorId.delete(actorId);
|
|
4442
|
-
disposeRunInspector(config.run, actorId);
|
|
4443
4745
|
resolveNativeDestroy(runtime, ctx);
|
|
4444
4746
|
await actorCtx.closeDatabase();
|
|
4445
4747
|
clearNativeRuntimeState(runtime, ctx);
|
|
@@ -4520,6 +4822,7 @@ export function buildNativeFactory(
|
|
|
4520
4822
|
connId,
|
|
4521
4823
|
),
|
|
4522
4824
|
),
|
|
4825
|
+
() => actorCtx.assertCanMutateState(),
|
|
4523
4826
|
);
|
|
4524
4827
|
try {
|
|
4525
4828
|
const nextConnState = hasStaticConnState
|
|
@@ -4575,6 +4878,7 @@ export function buildNativeFactory(
|
|
|
4575
4878
|
connId,
|
|
4576
4879
|
),
|
|
4577
4880
|
),
|
|
4881
|
+
() => actorCtx.assertCanMutateState(),
|
|
4578
4882
|
);
|
|
4579
4883
|
try {
|
|
4580
4884
|
await config.onConnect(
|
|
@@ -4618,6 +4922,7 @@ export function buildNativeFactory(
|
|
|
4618
4922
|
connId,
|
|
4619
4923
|
),
|
|
4620
4924
|
),
|
|
4925
|
+
() => actorCtx.assertCanMutateState(),
|
|
4621
4926
|
),
|
|
4622
4927
|
);
|
|
4623
4928
|
}
|
|
@@ -4826,7 +5131,6 @@ export function buildNativeFactory(
|
|
|
4826
5131
|
)
|
|
4827
5132
|
: undefined,
|
|
4828
5133
|
run: (() => {
|
|
4829
|
-
const run = getRunFunction(config.run);
|
|
4830
5134
|
if (!run) {
|
|
4831
5135
|
return undefined;
|
|
4832
5136
|
}
|
|
@@ -4838,67 +5142,75 @@ export function buildNativeFactory(
|
|
|
4838
5142
|
) => {
|
|
4839
5143
|
const { ctx } = unwrapTsfnPayload(error, payload);
|
|
4840
5144
|
const actorId = callNativeSync(() => runtime.actorId(ctx));
|
|
4841
|
-
const
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
5145
|
+
const executeRun = async () => {
|
|
5146
|
+
const actorCtx = makeActorCtx(ctx);
|
|
5147
|
+
try {
|
|
5148
|
+
await run(actorCtx);
|
|
5149
|
+
} finally {
|
|
5150
|
+
await actorCtx.dispose();
|
|
5151
|
+
}
|
|
5152
|
+
};
|
|
5153
|
+
if (runHandlerCoordinator) {
|
|
5154
|
+
const outcome = await runHandlerCoordinator.run(
|
|
5155
|
+
actorId,
|
|
5156
|
+
() =>
|
|
5157
|
+
callNativeSync(() =>
|
|
5158
|
+
runtime.actorRestartRunHandler(ctx),
|
|
5159
|
+
),
|
|
5160
|
+
executeRun,
|
|
5161
|
+
);
|
|
5162
|
+
// Only a start suppressed behind a failed replay must preserve a
|
|
5163
|
+
// consumed durable wake. Closed actors must remain closed.
|
|
5164
|
+
return outcome !== "suppressed";
|
|
4852
5165
|
}
|
|
5166
|
+
await executeRun();
|
|
5167
|
+
return true;
|
|
4853
5168
|
},
|
|
4854
5169
|
);
|
|
4855
5170
|
})(),
|
|
4856
|
-
getWorkflowHistory:
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
5171
|
+
getWorkflowHistory: hasRunInspectorConfig(config.run)
|
|
5172
|
+
? wrapNativeCallback(
|
|
5173
|
+
async (
|
|
5174
|
+
error: unknown,
|
|
5175
|
+
payload: { ctx: ActorContextHandle },
|
|
5176
|
+
) => {
|
|
5177
|
+
const { ctx } = unwrapTsfnPayload(error, payload);
|
|
5178
|
+
const history =
|
|
5179
|
+
getNativeWorkflowInspector(ctx)?.getHistory();
|
|
5180
|
+
return history == null
|
|
5181
|
+
? undefined
|
|
5182
|
+
: toUint8Array(history);
|
|
5183
|
+
},
|
|
5184
|
+
)
|
|
5185
|
+
: undefined,
|
|
5186
|
+
replayWorkflow: hasRunInspectorConfig(config.run)
|
|
5187
|
+
? wrapNativeCallback(
|
|
5188
|
+
async (
|
|
5189
|
+
error: unknown,
|
|
5190
|
+
payload: {
|
|
5191
|
+
ctx: ActorContextHandle;
|
|
5192
|
+
entryId?: string;
|
|
4869
5193
|
},
|
|
4870
|
-
)
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
},
|
|
4881
|
-
) => {
|
|
4882
|
-
const { ctx, entryId } = unwrapTsfnPayload(
|
|
4883
|
-
error,
|
|
4884
|
-
payload,
|
|
4885
|
-
);
|
|
4886
|
-
const workflowInspector =
|
|
4887
|
-
getNativeWorkflowInspector(ctx);
|
|
4888
|
-
if (!workflowInspector?.replayFromStep) {
|
|
4889
|
-
return undefined;
|
|
4890
|
-
}
|
|
5194
|
+
) => {
|
|
5195
|
+
const { ctx, entryId } = unwrapTsfnPayload(
|
|
5196
|
+
error,
|
|
5197
|
+
payload,
|
|
5198
|
+
);
|
|
5199
|
+
const workflowInspector =
|
|
5200
|
+
getNativeWorkflowInspector(ctx);
|
|
5201
|
+
if (!workflowInspector?.replayFromStep) {
|
|
5202
|
+
return undefined;
|
|
5203
|
+
}
|
|
4891
5204
|
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
: undefined,
|
|
5205
|
+
const history =
|
|
5206
|
+
(await workflowInspector.replayFromStep(entryId)) ??
|
|
5207
|
+
null;
|
|
5208
|
+
return history == null
|
|
5209
|
+
? undefined
|
|
5210
|
+
: toUint8Array(history);
|
|
5211
|
+
},
|
|
5212
|
+
)
|
|
5213
|
+
: undefined,
|
|
4902
5214
|
actions: Object.fromEntries(
|
|
4903
5215
|
Object.entries(actionHandlers).map(([name, handler]) => [
|
|
4904
5216
|
name,
|
|
@@ -4980,6 +5292,7 @@ export function buildNativeFactory(
|
|
|
4980
5292
|
stateEnabled,
|
|
4981
5293
|
onStateChange,
|
|
4982
5294
|
cancelToken,
|
|
5295
|
+
run !== undefined,
|
|
4983
5296
|
);
|
|
4984
5297
|
try {
|
|
4985
5298
|
if (
|