rivetkit 2.3.11-rc.8 → 2.3.11-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/browser/client.js +1 -1
  2. package/dist/browser/client.js.map +1 -1
  3. package/dist/browser/inspector/client.js +1 -1
  4. package/dist/browser/inspector/client.js.map +1 -1
  5. package/dist/tsup/agent-os/index.cjs +1 -1
  6. package/dist/tsup/agent-os/index.cjs.map +1 -1
  7. package/dist/tsup/agent-os/index.js +1 -1
  8. package/dist/tsup/agent-os/index.js.map +1 -1
  9. package/dist/tsup/{chunk-E6WB2BLO.cjs → chunk-6SBPWTXJ.cjs} +9 -9
  10. package/dist/tsup/{chunk-E6WB2BLO.cjs.map → chunk-6SBPWTXJ.cjs.map} +1 -1
  11. package/dist/tsup/{chunk-A55CGZIV.cjs → chunk-BZWKY6WO.cjs} +139 -139
  12. package/dist/tsup/{chunk-A55CGZIV.cjs.map → chunk-BZWKY6WO.cjs.map} +1 -1
  13. package/dist/tsup/{chunk-4EDGOGCB.js → chunk-C5TORAVP.js} +2 -2
  14. package/dist/tsup/{chunk-JBQ63WSM.js → chunk-HOZ3ZLZ2.js} +6 -6
  15. package/dist/tsup/{chunk-N6MBTYGX.cjs → chunk-IMRDHZM2.cjs} +4 -4
  16. package/dist/tsup/{chunk-N6MBTYGX.cjs.map → chunk-IMRDHZM2.cjs.map} +1 -1
  17. package/dist/tsup/{chunk-WRRB24UY.js → chunk-IOLZ3ONU.js} +3 -3
  18. package/dist/tsup/{chunk-AGNBLCNJ.js → chunk-LZ4TSBIP.js} +2 -2
  19. package/dist/tsup/{chunk-AGNBLCNJ.js.map → chunk-LZ4TSBIP.js.map} +1 -1
  20. package/dist/tsup/{chunk-KVD6ZWNM.js → chunk-QVCO6DTZ.js} +3 -3
  21. package/dist/tsup/{chunk-2ROUKH7W.cjs → chunk-T56IZ42M.cjs} +2 -2
  22. package/dist/tsup/{chunk-2ROUKH7W.cjs.map → chunk-T56IZ42M.cjs.map} +1 -1
  23. package/dist/tsup/{chunk-CCUO5OKD.cjs → chunk-T5XVTVQY.cjs} +3 -3
  24. package/dist/tsup/{chunk-CCUO5OKD.cjs.map → chunk-T5XVTVQY.cjs.map} +1 -1
  25. package/dist/tsup/{chunk-ZVM35ICH.cjs → chunk-V5CVATRU.cjs} +7 -7
  26. package/dist/tsup/{chunk-ZVM35ICH.cjs.map → chunk-V5CVATRU.cjs.map} +1 -1
  27. package/dist/tsup/{chunk-V6ANE6JM.js → chunk-XOOESJY7.js} +2 -2
  28. package/dist/tsup/client/mod.cjs +5 -5
  29. package/dist/tsup/client/mod.js +4 -4
  30. package/dist/tsup/common/log.cjs +2 -2
  31. package/dist/tsup/common/log.js +1 -1
  32. package/dist/tsup/common/websocket.cjs +3 -3
  33. package/dist/tsup/common/websocket.js +2 -2
  34. package/dist/tsup/db/drizzle.cjs +2 -2
  35. package/dist/tsup/db/drizzle.js +1 -1
  36. package/dist/tsup/db/mod.cjs +2 -2
  37. package/dist/tsup/db/mod.js +1 -1
  38. package/dist/tsup/inspector/mod.cjs +4 -4
  39. package/dist/tsup/inspector/mod.js +3 -3
  40. package/dist/tsup/mod.cjs +170 -72
  41. package/dist/tsup/mod.cjs.map +1 -1
  42. package/dist/tsup/mod.js +109 -11
  43. package/dist/tsup/mod.js.map +1 -1
  44. package/dist/tsup/test/mod.cjs +6 -6
  45. package/dist/tsup/test/mod.js +4 -4
  46. package/dist/tsup/utils.cjs +2 -2
  47. package/dist/tsup/utils.js +1 -1
  48. package/dist/tsup/workflow/mod.cjs +6 -6
  49. package/dist/tsup/workflow/mod.js +3 -3
  50. package/package.json +8 -8
  51. package/src/registry/native.ts +45 -8
  52. package/src/registry/write-through-proxy.ts +101 -0
  53. /package/dist/tsup/{chunk-4EDGOGCB.js.map → chunk-C5TORAVP.js.map} +0 -0
  54. /package/dist/tsup/{chunk-JBQ63WSM.js.map → chunk-HOZ3ZLZ2.js.map} +0 -0
  55. /package/dist/tsup/{chunk-WRRB24UY.js.map → chunk-IOLZ3ONU.js.map} +0 -0
  56. /package/dist/tsup/{chunk-KVD6ZWNM.js.map → chunk-QVCO6DTZ.js.map} +0 -0
  57. /package/dist/tsup/{chunk-V6ANE6JM.js.map → chunk-XOOESJY7.js.map} +0 -0
@@ -1,13 +1,13 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
2
 
3
- var _chunkZVM35ICHcjs = require('../chunk-ZVM35ICH.cjs');
3
+ var _chunkV5CVATRUcjs = require('../chunk-V5CVATRU.cjs');
4
4
 
5
5
 
6
- var _chunkA55CGZIVcjs = require('../chunk-A55CGZIV.cjs');
6
+ var _chunkBZWKY6WOcjs = require('../chunk-BZWKY6WO.cjs');
7
7
  require('../chunk-MYWZCDML.cjs');
8
+ require('../chunk-T5XVTVQY.cjs');
8
9
  require('../chunk-UEULFXUI.cjs');
9
- require('../chunk-CCUO5OKD.cjs');
10
- require('../chunk-2ROUKH7W.cjs');
10
+ require('../chunk-T56IZ42M.cjs');
11
11
  require('../chunk-4FC7TVS6.cjs');
12
12
 
13
13
  // src/test/ready.ts
@@ -36,8 +36,8 @@ async function setupTest(c, registry) {
36
36
  registry.config.noWelcome = true;
37
37
  registry.start();
38
38
  await waitForRegistryReady(registry);
39
- const client = _chunkZVM35ICHcjs.createClient.call(void 0, {
40
- ..._chunkA55CGZIVcjs.convertRegistryConfigToClientConfig.call(void 0, registry.parseConfig()),
39
+ const client = _chunkV5CVATRUcjs.createClient.call(void 0, {
40
+ ..._chunkBZWKY6WOcjs.convertRegistryConfigToClientConfig.call(void 0, registry.parseConfig()),
41
41
  disableMetadataLookup: false
42
42
  });
43
43
  c.onTestFinished(async () => {
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  createClient
3
- } from "../chunk-WRRB24UY.js";
3
+ } from "../chunk-IOLZ3ONU.js";
4
4
  import {
5
5
  convertRegistryConfigToClientConfig
6
- } from "../chunk-JBQ63WSM.js";
6
+ } from "../chunk-HOZ3ZLZ2.js";
7
7
  import "../chunk-JI6GZ2C2.js";
8
+ import "../chunk-XOOESJY7.js";
8
9
  import "../chunk-7AFZMFPQ.js";
9
- import "../chunk-V6ANE6JM.js";
10
- import "../chunk-AGNBLCNJ.js";
10
+ import "../chunk-LZ4TSBIP.js";
11
11
  import "../chunk-ZZ3WBRPD.js";
12
12
 
13
13
  // src/test/ready.ts
@@ -19,7 +19,7 @@
19
19
 
20
20
 
21
21
 
22
- var _chunk2ROUKH7Wcjs = require('./chunk-2ROUKH7W.cjs');
22
+ var _chunkT56IZ42Mcjs = require('./chunk-T56IZ42M.cjs');
23
23
  require('./chunk-4FC7TVS6.cjs');
24
24
 
25
25
 
@@ -42,5 +42,5 @@ require('./chunk-4FC7TVS6.cjs');
42
42
 
43
43
 
44
44
 
45
- exports.EXTRA_ERROR_LOG = _chunk2ROUKH7Wcjs.EXTRA_ERROR_LOG; exports.SinglePromiseQueue = _chunk2ROUKH7Wcjs.SinglePromiseQueue; exports.VERSION = _chunk2ROUKH7Wcjs.VERSION; exports.arrayBuffersEqual = _chunk2ROUKH7Wcjs.arrayBuffersEqual; exports.assertUnreachable = _chunk2ROUKH7Wcjs.assertUnreachable; exports.bufferToArrayBuffer = _chunk2ROUKH7Wcjs.bufferToArrayBuffer; exports.combineUrlPath = _chunk2ROUKH7Wcjs.combineUrlPath; exports.dbg = _chunk2ROUKH7Wcjs.dbg; exports.detectRuntime = _chunk2ROUKH7Wcjs.detectRuntime; exports.getEnvUniversal = _chunk2ROUKH7Wcjs.getEnvUniversal; exports.httpUserAgent = _chunk2ROUKH7Wcjs.httpUserAgent; exports.interval = _chunk2ROUKH7Wcjs.interval; exports.joinSignals = _chunk2ROUKH7Wcjs.joinSignals; exports.jsonParseCompat = _chunk2ROUKH7Wcjs.jsonParseCompat; exports.jsonStringifyCompat = _chunk2ROUKH7Wcjs.jsonStringifyCompat; exports.promiseWithResolvers = _chunk2ROUKH7Wcjs.promiseWithResolvers; exports.setLongTimeout = _chunk2ROUKH7Wcjs.setLongTimeout; exports.sleep = _chunk2ROUKH7Wcjs.sleep; exports.stringifyError = _chunk2ROUKH7Wcjs.stringifyError; exports.toUint8Array = _chunk2ROUKH7Wcjs.toUint8Array;
45
+ exports.EXTRA_ERROR_LOG = _chunkT56IZ42Mcjs.EXTRA_ERROR_LOG; exports.SinglePromiseQueue = _chunkT56IZ42Mcjs.SinglePromiseQueue; exports.VERSION = _chunkT56IZ42Mcjs.VERSION; exports.arrayBuffersEqual = _chunkT56IZ42Mcjs.arrayBuffersEqual; exports.assertUnreachable = _chunkT56IZ42Mcjs.assertUnreachable; exports.bufferToArrayBuffer = _chunkT56IZ42Mcjs.bufferToArrayBuffer; exports.combineUrlPath = _chunkT56IZ42Mcjs.combineUrlPath; exports.dbg = _chunkT56IZ42Mcjs.dbg; exports.detectRuntime = _chunkT56IZ42Mcjs.detectRuntime; exports.getEnvUniversal = _chunkT56IZ42Mcjs.getEnvUniversal; exports.httpUserAgent = _chunkT56IZ42Mcjs.httpUserAgent; exports.interval = _chunkT56IZ42Mcjs.interval; exports.joinSignals = _chunkT56IZ42Mcjs.joinSignals; exports.jsonParseCompat = _chunkT56IZ42Mcjs.jsonParseCompat; exports.jsonStringifyCompat = _chunkT56IZ42Mcjs.jsonStringifyCompat; exports.promiseWithResolvers = _chunkT56IZ42Mcjs.promiseWithResolvers; exports.setLongTimeout = _chunkT56IZ42Mcjs.setLongTimeout; exports.sleep = _chunkT56IZ42Mcjs.sleep; exports.stringifyError = _chunkT56IZ42Mcjs.stringifyError; exports.toUint8Array = _chunkT56IZ42Mcjs.toUint8Array;
46
46
  //# sourceMappingURL=utils.cjs.map
@@ -19,7 +19,7 @@ import {
19
19
  sleep,
20
20
  stringifyError,
21
21
  toUint8Array
22
- } from "./chunk-AGNBLCNJ.js";
22
+ } from "./chunk-LZ4TSBIP.js";
23
23
  import "./chunk-ZZ3WBRPD.js";
24
24
  export {
25
25
  EXTRA_ERROR_LOG,
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } var _class; var _class2;
2
2
 
3
- var _chunkE6WB2BLOcjs = require('../chunk-E6WB2BLO.cjs');
4
- require('../chunk-N6MBTYGX.cjs');
3
+ var _chunk6SBPWTXJcjs = require('../chunk-6SBPWTXJ.cjs');
4
+ require('../chunk-IMRDHZM2.cjs');
5
5
 
6
6
 
7
7
 
@@ -12,7 +12,7 @@ var _chunkHNHLBM3Ycjs = require('../chunk-HNHLBM3Y.cjs');
12
12
  var _chunkMYWZCDMLcjs = require('../chunk-MYWZCDML.cjs');
13
13
 
14
14
 
15
- var _chunk2ROUKH7Wcjs = require('../chunk-2ROUKH7W.cjs');
15
+ var _chunkT56IZ42Mcjs = require('../chunk-T56IZ42M.cjs');
16
16
 
17
17
 
18
18
 
@@ -716,7 +716,7 @@ function workflow(fn, options = {}) {
716
716
  function getWorkflowInspector(actorId) {
717
717
  let workflowInspector = workflowInspectors.get(actorId);
718
718
  if (!workflowInspector) {
719
- workflowInspector = _chunkE6WB2BLOcjs.createWorkflowInspectorAdapter.call(void 0, );
719
+ workflowInspector = _chunk6SBPWTXJcjs.createWorkflowInspectorAdapter.call(void 0, );
720
720
  workflowInspectors.set(actorId, workflowInspector);
721
721
  }
722
722
  return workflowInspector;
@@ -784,13 +784,13 @@ function workflow(fn, options = {}) {
784
784
  if (shouldRethrowWorkflowError(error)) {
785
785
  runCtx.log.error({
786
786
  msg: "workflow run failed",
787
- error: _chunk2ROUKH7Wcjs.stringifyError.call(void 0, error)
787
+ error: _chunkT56IZ42Mcjs.stringifyError.call(void 0, error)
788
788
  });
789
789
  throw error;
790
790
  }
791
791
  runCtx.log.warn({
792
792
  msg: "workflow failed and will sleep until woken",
793
- error: _chunk2ROUKH7Wcjs.stringifyError.call(void 0, error)
793
+ error: _chunkT56IZ42Mcjs.stringifyError.call(void 0, error)
794
794
  });
795
795
  } finally {
796
796
  runCtx.abortSignal.removeEventListener("abort", onAbort);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createWorkflowInspectorAdapter
3
- } from "../chunk-KVD6ZWNM.js";
4
- import "../chunk-4EDGOGCB.js";
3
+ } from "../chunk-QVCO6DTZ.js";
4
+ import "../chunk-C5TORAVP.js";
5
5
  import {
6
6
  ACTOR_CONTEXT_INTERNAL_SYMBOL,
7
7
  RUN_FUNCTION_CONFIG_SYMBOL
@@ -12,7 +12,7 @@ import {
12
12
  } from "../chunk-JI6GZ2C2.js";
13
13
  import {
14
14
  stringifyError
15
- } from "../chunk-AGNBLCNJ.js";
15
+ } from "../chunk-LZ4TSBIP.js";
16
16
  import {
17
17
  RivetError,
18
18
  isActorAbortedError
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rivetkit",
3
- "version": "2.3.11-rc.8",
3
+ "version": "2.3.11-rc.9",
4
4
  "description": "Lightweight libraries for building stateful actors on edge platforms",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -201,14 +201,14 @@
201
201
  "@hono/zod-openapi": "^1.1.5",
202
202
  "@rivet-dev/agent-os-core": "^0.1.1",
203
203
  "@rivetkit/bare-ts": "^0.6.2",
204
- "@rivetkit/engine-cli": "2.3.11-rc.8",
205
- "@rivetkit/engine-envoy-protocol": "2.3.11-rc.8",
204
+ "@rivetkit/engine-cli": "2.3.11-rc.9",
205
+ "@rivetkit/engine-envoy-protocol": "2.3.11-rc.9",
206
206
  "@rivetkit/on-change": "6.0.1",
207
- "@rivetkit/rivetkit-napi": "2.3.11-rc.8",
208
- "@rivetkit/rivetkit-wasm": "2.3.11-rc.8",
209
- "@rivetkit/traces": "2.3.11-rc.8",
210
- "@rivetkit/virtual-websocket": "2.3.11-rc.8",
211
- "@rivetkit/workflow-engine": "2.3.11-rc.8",
207
+ "@rivetkit/rivetkit-napi": "2.3.11-rc.9",
208
+ "@rivetkit/rivetkit-wasm": "2.3.11-rc.9",
209
+ "@rivetkit/traces": "2.3.11-rc.9",
210
+ "@rivetkit/virtual-websocket": "2.3.11-rc.9",
211
+ "@rivetkit/workflow-engine": "2.3.11-rc.9",
212
212
  "cbor-x": "^1.6.0",
213
213
  "drizzle-orm": "^0.44.2",
214
214
  "hono": "^4.7.0",
@@ -104,7 +104,10 @@ import type {
104
104
  WebSocketHandle,
105
105
  } from "./runtime";
106
106
  import { loadWasmRuntime } from "./wasm-runtime";
107
- import { createWriteThroughProxy } from "./write-through-proxy";
107
+ import {
108
+ createWriteThroughProxy,
109
+ unwrapWriteThroughProxy,
110
+ } from "./write-through-proxy";
108
111
 
109
112
  const textEncoder = new TextEncoder();
110
113
  const textDecoder = new TextDecoder();
@@ -126,6 +129,10 @@ type NativeOnStateChangeHandler = (
126
129
  ) => void | Promise<void>;
127
130
  type NativePersistConnState = {
128
131
  state: unknown;
132
+ // Memoized deep write-through proxy and the state object it wraps. Rebuilt
133
+ // only when the underlying state object identity changes.
134
+ stateProxy?: unknown;
135
+ stateProxyTarget?: unknown;
129
136
  };
130
137
 
131
138
  const defaultRuntimeLoaders: RuntimeLoaders = {
@@ -1270,8 +1277,32 @@ class NativeConnAdapter {
1270
1277
 
1271
1278
  get state(): unknown {
1272
1279
  const nextState = this.#readState();
1280
+ if (!this.#ctx) {
1281
+ return this.#createStateProxy(nextState);
1282
+ }
1283
+
1284
+ // Reading `conn.state` rebuilds the deep write-through proxy, which
1285
+ // allocates fresh on-change caches and rewraps the whole tree. Memoize
1286
+ // the proxy keyed on the underlying state object so repeated reads and
1287
+ // deep read cascades reuse a single proxy.
1288
+ const connState = getNativeConnPersistState(
1289
+ this.#runtime,
1290
+ this.#ctx,
1291
+ this.#conn,
1292
+ );
1293
+ if (
1294
+ connState.stateProxy === undefined ||
1295
+ connState.stateProxyTarget !== nextState
1296
+ ) {
1297
+ connState.stateProxyTarget = nextState;
1298
+ connState.stateProxy = this.#createStateProxy(nextState);
1299
+ }
1300
+ return connState.stateProxy;
1301
+ }
1302
+
1303
+ #createStateProxy(state: unknown): unknown {
1273
1304
  return createWriteThroughProxy(
1274
- nextState,
1305
+ state,
1275
1306
  (nextValue) => {
1276
1307
  this.#writeState(nextValue, { writeNative: true });
1277
1308
  },
@@ -1282,12 +1313,15 @@ class NativeConnAdapter {
1282
1313
  }
1283
1314
 
1284
1315
  set state(value: unknown) {
1285
- assertJsonCompatValue(value);
1286
- this.#writeState(value, { writeNative: true });
1316
+ const nextValue = unwrapWriteThroughProxy(value);
1317
+ assertJsonCompatValue(nextValue);
1318
+ this.#writeState(nextValue, { writeNative: true });
1287
1319
  }
1288
1320
 
1289
1321
  initializeState(value: unknown): void {
1290
- this.#writeState(value, { writeNative: false });
1322
+ this.#writeState(unwrapWriteThroughProxy(value), {
1323
+ writeNative: false,
1324
+ });
1291
1325
  }
1292
1326
 
1293
1327
  get isHibernatable(): boolean {
@@ -2693,15 +2727,18 @@ export class ActorContextHandleAdapter {
2693
2727
  throw stateNotEnabledError();
2694
2728
  }
2695
2729
  this.#assertCanMutateState();
2696
- assertJsonCompatValue(value);
2697
- this.#writeState(value, { scheduleSave: true });
2730
+ const nextValue = unwrapWriteThroughProxy(value);
2731
+ assertJsonCompatValue(nextValue);
2732
+ this.#writeState(nextValue, { scheduleSave: true });
2698
2733
  }
2699
2734
 
2700
2735
  initializeState(value: unknown): void {
2701
2736
  if (!this.#stateEnabled) {
2702
2737
  return;
2703
2738
  }
2704
- this.#writeState(value, { scheduleSave: false });
2739
+ this.#writeState(unwrapWriteThroughProxy(value), {
2740
+ scheduleSave: false,
2741
+ });
2705
2742
  }
2706
2743
 
2707
2744
  get vars(): unknown {
@@ -38,3 +38,104 @@ export function createWriteThroughProxy<T>(
38
38
  },
39
39
  ) as T;
40
40
  }
41
+
42
+ /**
43
+ * Returns the raw target behind an `@rivetkit/on-change` proxy, following
44
+ * chains of proxies wrapping proxies until reaching a plain value.
45
+ */
46
+ function unwrapProxy(value: unknown): unknown {
47
+ let current = value;
48
+ while (current !== null && typeof current === "object") {
49
+ const target = onChange.target(current as Record<string, any>);
50
+ if (target === current) {
51
+ break;
52
+ }
53
+ current = target;
54
+ }
55
+ return current;
56
+ }
57
+
58
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
59
+ const proto = Object.getPrototypeOf(value as object);
60
+ return proto === Object.prototype || proto === null;
61
+ }
62
+
63
+ function unwrapDeep(value: unknown, seen: Set<object>): unknown {
64
+ const unwrapped = unwrapProxy(value);
65
+ if (!unwrapped || typeof unwrapped !== "object") {
66
+ return unwrapped;
67
+ }
68
+ if (seen.has(unwrapped)) {
69
+ return unwrapped;
70
+ }
71
+ seen.add(unwrapped);
72
+
73
+ if (Array.isArray(unwrapped)) {
74
+ for (let i = 0; i < unwrapped.length; i++) {
75
+ const child = unwrapDeep(unwrapped[i], seen);
76
+ if (child !== unwrapped[i]) {
77
+ unwrapped[i] = child;
78
+ }
79
+ }
80
+ return unwrapped;
81
+ }
82
+
83
+ if (unwrapped instanceof Map) {
84
+ const replacements: [unknown, unknown, unknown][] = [];
85
+ for (const [key, child] of unwrapped.entries()) {
86
+ const nextKey = unwrapDeep(key, seen);
87
+ const nextChild = unwrapDeep(child, seen);
88
+ if (nextKey !== key || nextChild !== child) {
89
+ replacements.push([key, nextKey, nextChild]);
90
+ }
91
+ }
92
+ for (const [key, nextKey, nextChild] of replacements) {
93
+ if (nextKey !== key) {
94
+ unwrapped.delete(key);
95
+ }
96
+ unwrapped.set(nextKey, nextChild);
97
+ }
98
+ return unwrapped;
99
+ }
100
+
101
+ if (unwrapped instanceof Set) {
102
+ const replacements: [unknown, unknown][] = [];
103
+ for (const child of unwrapped.values()) {
104
+ const next = unwrapDeep(child, seen);
105
+ if (next !== child) {
106
+ replacements.push([child, next]);
107
+ }
108
+ }
109
+ for (const [child, next] of replacements) {
110
+ unwrapped.delete(child);
111
+ unwrapped.add(next);
112
+ }
113
+ return unwrapped;
114
+ }
115
+
116
+ if (isPlainObject(unwrapped)) {
117
+ for (const key of Object.keys(unwrapped)) {
118
+ const child = unwrapDeep(unwrapped[key], seen);
119
+ if (child !== unwrapped[key]) {
120
+ unwrapped[key] = child;
121
+ }
122
+ }
123
+ }
124
+
125
+ return unwrapped;
126
+ }
127
+
128
+ /**
129
+ * Strips every `@rivetkit/on-change` proxy out of a value in place, including
130
+ * proxies nested inside plain objects, arrays, `Map`s, and `Set`s.
131
+ *
132
+ * A read of `c.state` or `conn.state` hands back a deep write-through proxy,
133
+ * so an update written as `c.state = { ...c.state, foo }` produces a plain root
134
+ * object whose children are still proxies. Persisting that value as-is makes
135
+ * the next read wrap proxies in another proxy layer, and each layer multiplies
136
+ * the work of traversing the state, so repeated spread updates degrade
137
+ * exponentially. Unwrapping before persisting keeps stored state proxy-free.
138
+ */
139
+ export function unwrapWriteThroughProxy<T>(value: T): T {
140
+ return unwrapDeep(value, new Set()) as T;
141
+ }