opshot 0.3.4 → 0.5.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.
Files changed (4) hide show
  1. package/README.md +43 -113
  2. package/dist/index.d.ts +156 -69
  3. package/dist/index.js +1105 -1789
  4. package/package.json +19 -12
package/dist/index.js CHANGED
@@ -1,355 +1,62 @@
1
- import { memo, useState, useReducer, useEffect, useRef, createElement } from 'react';
1
+ import * as React from 'react';
2
+ import { memo, useState, useReducer, useRef, useEffect, createElement, useLayoutEffect } from 'react';
2
3
 
3
- // node_modules/proxy-compare/dist/index.js
4
- var GET_ORIGINAL_SYMBOL = /* @__PURE__ */ Symbol();
5
- var HAS_KEY_PROPERTY = "h";
6
- var ALL_OWN_KEYS_PROPERTY = "w";
7
- var HAS_OWN_KEY_PROPERTY = "o";
8
- var KEYS_PROPERTY = "k";
9
- var getProto = Object.getPrototypeOf;
10
- var objectsToTrack = /* @__PURE__ */ new WeakMap();
11
- var isObjectToTrack = (obj) => obj && (objectsToTrack.has(obj) ? objectsToTrack.get(obj) : getProto(obj) === Object.prototype || getProto(obj) === Array.prototype);
12
- var isObject = (x) => typeof x === "object" && x !== null;
13
- var getOriginalObject = (obj) => (
14
- // unwrap proxy
15
- obj[GET_ORIGINAL_SYMBOL] || // otherwise
16
- obj
4
+ // src/utils/constructorName.ts
5
+ var constructorName = (candidate) => typeof candidate === "function" && candidate.name !== "" ? candidate.name : "Object";
6
+
7
+ // src/boundaryErrors.ts
8
+ var ignoreOption = "ignore(value) to store it by reference, untracked";
9
+ var unsafeTrackDataOption = "unsafeTrack(value) to track its data anyway";
10
+ var unsafeTrackPrivateOption = "unsafeTrack(value) tracks public fields while private methods stay untracked";
11
+ var unsafeTrackSlotOption = "unsafeTrack(value) tracks public fields while slot methods stay untracked";
12
+ var unsafeTrackLossyOption = "unsafeTrack(value) to track it lossily";
13
+ var locationClause = (path) => path === void 0 || path.length === 0 ? "" : ` at /${path.join("/")}`;
14
+ var boundaryError = (className, reason, options, path) => new Error(
15
+ `opshot: ${className}${locationClause(path)} cannot be tracked (${reason}). Options:
16
+ ${options.map((option) => `- ${option}`).join("\n")}`
17
17
  );
18
- var isAllOwnKeysChanged = (prevObj, nextObj) => {
19
- const prevKeys = Reflect.ownKeys(prevObj);
20
- const nextKeys = Reflect.ownKeys(nextObj);
21
- return prevKeys.length !== nextKeys.length || prevKeys.some((k, i) => k !== nextKeys[i]);
22
- };
23
- var isChanged = (prevObj, nextObj, affected, cache, isEqual = Object.is) => {
24
- if (isEqual(prevObj, nextObj)) {
25
- return false;
26
- }
27
- if (!isObject(prevObj) || !isObject(nextObj))
28
- return true;
29
- const used = affected.get(getOriginalObject(prevObj));
30
- if (!used)
31
- return true;
32
- if (cache) {
33
- const hit = cache.get(prevObj);
34
- if (hit === nextObj) {
35
- return false;
36
- }
37
- cache.set(prevObj, nextObj);
38
- }
39
- let changed = null;
40
- for (const key of used[HAS_KEY_PROPERTY] || []) {
41
- changed = Reflect.has(prevObj, key) !== Reflect.has(nextObj, key);
42
- if (changed)
43
- return changed;
44
- }
45
- if (used[ALL_OWN_KEYS_PROPERTY] === true) {
46
- changed = isAllOwnKeysChanged(prevObj, nextObj);
47
- if (changed)
48
- return changed;
49
- } else {
50
- for (const key of used[HAS_OWN_KEY_PROPERTY] || []) {
51
- const hasPrev = !!Reflect.getOwnPropertyDescriptor(prevObj, key);
52
- const hasNext = !!Reflect.getOwnPropertyDescriptor(nextObj, key);
53
- changed = hasPrev !== hasNext;
54
- if (changed)
55
- return changed;
56
- }
57
- }
58
- for (const key of used[KEYS_PROPERTY] || []) {
59
- changed = isChanged(prevObj[key], nextObj[key], affected, cache, isEqual);
60
- if (changed)
61
- return changed;
62
- }
63
- if (changed === null)
64
- throw new Error("invalid used");
65
- return changed;
18
+ var slotContainerError = (className, trackedName, path) => boundaryError(
19
+ className,
20
+ "its state lives in internal slots",
21
+ [`use ${trackedName} for a tracked equivalent`, unsafeTrackLossyOption, ignoreOption],
22
+ path
23
+ );
24
+ var arraySubclassError = (className, path) => boundaryError(className, "array subclasses are not plain arrays", [unsafeTrackDataOption, ignoreOption], path);
25
+ var cleanClassError = (className, path) => boundaryError(className, "arrow-method writes won't be tracked", [unsafeTrackDataOption, ignoreOption], path);
26
+ var privateClassError = (className, path) => boundaryError(className, "its state is hidden in private fields", [unsafeTrackPrivateOption, ignoreOption], path);
27
+ var nativeClassError = (className, path) => boundaryError(className, "its state is hidden in internal slots", [unsafeTrackSlotOption, ignoreOption], path);
28
+ var nonWritablePropertyError = (value, path) => boundaryError(
29
+ constructorName(value.constructor),
30
+ "a non-writable property's interior is silently mutable and untracked",
31
+ ["make the property writable", "ignore(value) to declare the escape"],
32
+ path
33
+ );
34
+ var inheritsFromPrototype = (value, prototype) => {
35
+ for (let current = Reflect.getPrototypeOf(value); current !== null; current = Reflect.getPrototypeOf(current))
36
+ if (current === prototype) return true;
37
+ return false;
66
38
  };
67
- var getUntracked = (obj) => {
68
- if (isObjectToTrack(obj)) {
69
- return obj[GET_ORIGINAL_SYMBOL] || null;
39
+ var rejectionError = (value, kind, path) => {
40
+ const className = constructorName(value.constructor);
41
+ if (inheritsFromPrototype(value, Map.prototype)) return slotContainerError(className, "TrackedMap", path);
42
+ if (inheritsFromPrototype(value, Set.prototype)) return slotContainerError(className, "TrackedSet", path);
43
+ if (inheritsFromPrototype(value, Date.prototype)) return slotContainerError(className, "TrackedDate", path);
44
+ switch (kind) {
45
+ case "arraySubclass":
46
+ return arraySubclassError(className, path);
47
+ case "cleanClass":
48
+ return cleanClassError(className, path);
49
+ case "privateClass":
50
+ return privateClassError(className, path);
51
+ case "nativeClass":
52
+ return nativeClassError(className, path);
70
53
  }
71
- return null;
72
- };
73
- var markToTrack = (obj, mark = true) => {
74
- objectsToTrack.set(obj, mark);
75
54
  };
76
55
 
77
- // node_modules/valtio/esm/vanilla.mjs
78
- var isObject2 = (x) => typeof x === "object" && x !== null;
79
- var canProxyDefault = (x) => isObject2(x) && !refSet.has(x) && (Array.isArray(x) || !(Symbol.iterator in x)) && !(x instanceof WeakMap) && !(x instanceof WeakSet) && !(x instanceof Error) && !(x instanceof Number) && !(x instanceof Date) && !(x instanceof String) && !(x instanceof RegExp) && !(x instanceof ArrayBuffer) && !(x instanceof Promise);
80
- var createSnapshotDefault = (target, version) => {
81
- const cache = snapCache.get(target);
82
- if ((cache == null ? void 0 : cache[0]) === version) {
83
- return cache[1];
84
- }
85
- const snap = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target));
86
- markToTrack(snap, true);
87
- snapCache.set(target, [version, snap]);
88
- Reflect.ownKeys(target).forEach((key) => {
89
- if (Object.getOwnPropertyDescriptor(snap, key)) {
90
- return;
91
- }
92
- const value = Reflect.get(target, key);
93
- const { enumerable } = Reflect.getOwnPropertyDescriptor(
94
- target,
95
- key
96
- );
97
- const desc = {
98
- value,
99
- enumerable,
100
- // This is intentional to avoid copying with proxy-compare.
101
- // It's still non-writable, so it avoids assigning a value.
102
- configurable: true
103
- };
104
- if (refSet.has(value)) {
105
- markToTrack(value, false);
106
- } else if (proxyStateMap.has(value)) {
107
- const [target2, ensureVersion] = proxyStateMap.get(
108
- value
109
- );
110
- desc.value = createSnapshotDefault(target2, ensureVersion());
111
- }
112
- Object.defineProperty(snap, key, desc);
113
- });
114
- return snap;
115
- };
116
- var createHandlerDefault = (isInitializing, addPropListener, removePropListener, notifyUpdate) => ({
117
- deleteProperty(target, prop) {
118
- Reflect.get(target, prop);
119
- removePropListener(prop);
120
- const deleted = Reflect.deleteProperty(target, prop);
121
- if (deleted) {
122
- notifyUpdate(void 0 );
123
- }
124
- return deleted;
125
- },
126
- set(target, prop, value, receiver) {
127
- const hasPrevValue = !isInitializing() && Reflect.has(target, prop);
128
- const prevValue = Reflect.get(target, prop, receiver);
129
- if (hasPrevValue && (objectIs(prevValue, value) || proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value)))) {
130
- return true;
131
- }
132
- removePropListener(prop);
133
- if (isObject2(value)) {
134
- value = getUntracked(value) || value;
135
- }
136
- const nextValue = !proxyStateMap.has(value) && canProxy(value) ? proxy(value) : value;
137
- addPropListener(prop, nextValue);
138
- Reflect.set(target, prop, nextValue, receiver);
139
- notifyUpdate(void 0 );
140
- return true;
141
- }
142
- });
143
- var proxyStateMap = /* @__PURE__ */ new WeakMap();
144
- var refSet = /* @__PURE__ */ new WeakSet();
145
- var snapCache = /* @__PURE__ */ new WeakMap();
146
- var versionHolder = [1];
147
- var proxyCache = /* @__PURE__ */ new WeakMap();
148
- var objectIs = Object.is;
149
- var newProxy = (target, handler) => new Proxy(target, handler);
150
- var canProxy = canProxyDefault;
151
- var createSnapshot = createSnapshotDefault;
152
- var createHandler = createHandlerDefault;
153
- function proxy(baseObject = {}) {
154
- if (!isObject2(baseObject)) {
155
- throw new Error("object required");
156
- }
157
- const found = proxyCache.get(baseObject);
158
- if (found) {
159
- return found;
160
- }
161
- let version = versionHolder[0];
162
- const listeners = /* @__PURE__ */ new Set();
163
- const notifyUpdate = (op, nextVersion = ++versionHolder[0]) => {
164
- if (version !== nextVersion) {
165
- checkVersion = version = nextVersion;
166
- listeners.forEach((listener) => listener(op, nextVersion));
167
- }
168
- };
169
- let checkVersion = version;
170
- const ensureVersion = (nextCheckVersion = versionHolder[0]) => {
171
- if (checkVersion !== nextCheckVersion) {
172
- checkVersion = nextCheckVersion;
173
- propProxyStates.forEach(([propProxyState]) => {
174
- const propVersion = propProxyState[1](nextCheckVersion);
175
- if (propVersion > version) {
176
- version = propVersion;
177
- }
178
- });
179
- }
180
- return version;
181
- };
182
- const createPropListener = (prop) => (op, nextVersion) => {
183
- let newOp;
184
- if (op) {
185
- newOp = [...op];
186
- newOp[1] = [prop, ...newOp[1]];
187
- }
188
- notifyUpdate(newOp, nextVersion);
189
- };
190
- const propProxyStates = /* @__PURE__ */ new Map();
191
- const addPropListener = (prop, propValue) => {
192
- const propProxyState = !refSet.has(propValue) && proxyStateMap.get(propValue);
193
- if (propProxyState) {
194
- if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && propProxyStates.has(prop)) {
195
- throw new Error("prop listener already exists");
196
- }
197
- if (listeners.size) {
198
- const remove = propProxyState[2](createPropListener(prop));
199
- propProxyStates.set(prop, [propProxyState, remove]);
200
- } else {
201
- propProxyStates.set(prop, [propProxyState]);
202
- }
203
- }
204
- };
205
- const removePropListener = (prop) => {
206
- var _a;
207
- const entry = propProxyStates.get(prop);
208
- if (entry) {
209
- propProxyStates.delete(prop);
210
- (_a = entry[1]) == null ? void 0 : _a.call(entry);
211
- }
212
- };
213
- const addListener = (listener) => {
214
- listeners.add(listener);
215
- if (listeners.size === 1) {
216
- propProxyStates.forEach(([propProxyState, prevRemove], prop) => {
217
- if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && prevRemove) {
218
- throw new Error("remove already exists");
219
- }
220
- const remove = propProxyState[2](createPropListener(prop));
221
- propProxyStates.set(prop, [propProxyState, remove]);
222
- });
223
- }
224
- const removeListener = () => {
225
- listeners.delete(listener);
226
- if (listeners.size === 0) {
227
- propProxyStates.forEach(([propProxyState, remove], prop) => {
228
- if (remove) {
229
- remove();
230
- propProxyStates.set(prop, [propProxyState]);
231
- }
232
- });
233
- }
234
- };
235
- return removeListener;
236
- };
237
- let initializing = true;
238
- const handler = createHandler(
239
- () => initializing,
240
- addPropListener,
241
- removePropListener,
242
- notifyUpdate
243
- );
244
- const proxyObject = newProxy(baseObject, handler);
245
- proxyCache.set(baseObject, proxyObject);
246
- const proxyState = [baseObject, ensureVersion, addListener];
247
- proxyStateMap.set(proxyObject, proxyState);
248
- Reflect.ownKeys(baseObject).forEach((key) => {
249
- const desc = Object.getOwnPropertyDescriptor(
250
- baseObject,
251
- key
252
- );
253
- if ("value" in desc && desc.writable) {
254
- proxyObject[key] = baseObject[key];
255
- }
256
- });
257
- initializing = false;
258
- return proxyObject;
259
- }
260
- function getVersion(proxyObject) {
261
- const proxyState = proxyStateMap.get(proxyObject);
262
- return proxyState == null ? void 0 : proxyState[1]();
263
- }
264
- function subscribe(proxyObject, callback, notifyInSync) {
265
- const proxyState = proxyStateMap.get(proxyObject);
266
- if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !proxyState) {
267
- console.warn("Please use proxy object");
268
- }
269
- let promise;
270
- const ops = [];
271
- const addListener = proxyState[2];
272
- let isListenerActive = false;
273
- const listener = (op) => {
274
- if (op) {
275
- ops.push(op);
276
- }
277
- if (notifyInSync) {
278
- callback(ops.splice(0));
279
- return;
280
- }
281
- if (!promise) {
282
- promise = Promise.resolve().then(() => {
283
- promise = void 0;
284
- if (isListenerActive) {
285
- callback(ops.splice(0));
286
- }
287
- });
288
- }
289
- };
290
- const removeListener = addListener(listener);
291
- isListenerActive = true;
292
- return () => {
293
- isListenerActive = false;
294
- removeListener();
295
- };
296
- }
297
- function snapshot(proxyObject) {
298
- const proxyState = proxyStateMap.get(proxyObject);
299
- if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production" && !proxyState) {
300
- console.warn("Please use proxy object");
301
- }
302
- const [target, ensureVersion] = proxyState;
303
- return createSnapshot(target, ensureVersion());
304
- }
305
- function ref(obj) {
306
- refSet.add(obj);
307
- return obj;
308
- }
309
- function unstable_getInternalStates() {
310
- return {
311
- proxyStateMap,
312
- refSet,
313
- snapCache,
314
- versionHolder,
315
- proxyCache
316
- };
317
- }
318
- function unstable_replaceInternalFunction(name, fn) {
319
- switch (name) {
320
- case "objectIs":
321
- objectIs = fn(objectIs);
322
- break;
323
- case "newProxy":
324
- newProxy = fn(newProxy);
325
- break;
326
- case "canProxy":
327
- canProxy = fn(canProxy);
328
- break;
329
- case "createSnapshot":
330
- createSnapshot = fn(createSnapshot);
331
- break;
332
- case "createHandler":
333
- createHandler = fn(createHandler);
334
- break;
335
- default:
336
- throw new Error("unknown function");
337
- }
338
- }
339
-
340
- // src/unsafeTrack.ts
341
- var unsafeTrackedSet = /* @__PURE__ */ new WeakSet();
342
- function unsafeTrack(value) {
343
- unsafeTrackedSet.add(value);
344
- return value;
345
- }
346
- function isUnsafeTracked(value) {
347
- return typeof value === "object" && value !== null && unsafeTrackedSet.has(value);
348
- }
349
-
350
- // src/valtio/classify.ts
351
- var { refSet: refSet2 } = unstable_getInternalStates();
56
+ // src/classify.ts
57
+ var isDangerousKind = (kind) => kind !== "plain" && kind !== "plainArray" && kind !== "cleanClass";
352
58
  var sourceCache = /* @__PURE__ */ new WeakMap();
59
+ var kindCache = /* @__PURE__ */ new WeakMap();
353
60
  var readSource = (constructor) => {
354
61
  const cached = sourceCache.get(constructor);
355
62
  if (cached !== void 0) return cached;
@@ -357,16 +64,31 @@ var readSource = (constructor) => {
357
64
  sourceCache.set(constructor, source);
358
65
  return source;
359
66
  };
67
+ var privateNameAccess = /\.\s*#[A-Za-z_$]/;
68
+ var privateNameDeclaration = new RegExp(
69
+ String.raw`[{;}](?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n|static\b|async\b|get\b|set\b|\*)*` + String.raw`#[A-Za-z_$][\w$]*` + String.raw`(?:[ \t]*(?:\/\*[\s\S]*?\*\/[ \t]*)*[(=;}]|[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/[ \t]*)?(?:\r?\n|$))`,
70
+ "m"
71
+ );
72
+ var hasPrivateName = (source) => privateNameAccess.test(source) || privateNameDeclaration.test(source);
360
73
  var classifyChain = (initialConstructor) => {
74
+ if (typeof initialConstructor !== "function") return "cleanClass";
75
+ const cached = kindCache.get(initialConstructor);
76
+ if (cached !== void 0) return cached;
361
77
  let sawNativeSource = false;
362
78
  let current = initialConstructor;
79
+ let kind = "cleanClass";
363
80
  while (typeof current === "function" && current !== Object && current !== Array && current !== Function.prototype) {
364
81
  const source = readSource(current);
365
- if (source.includes("#")) return "privateClass";
82
+ if (hasPrivateName(source)) {
83
+ kind = "privateClass";
84
+ break;
85
+ }
366
86
  if (source.includes("[native code]")) sawNativeSource = true;
367
87
  current = Reflect.getPrototypeOf(current);
368
88
  }
369
- return sawNativeSource ? "nativeClass" : "cleanClass";
89
+ if (kind !== "privateClass") kind = sawNativeSource ? "nativeClass" : "cleanClass";
90
+ kindCache.set(initialConstructor, kind);
91
+ return kind;
370
92
  };
371
93
  function classifyValue(value) {
372
94
  const prototype = Object.getPrototypeOf(value);
@@ -375,1164 +97,581 @@ function classifyValue(value) {
375
97
  if (prototype === Object.prototype || prototype === null) return "plain";
376
98
  return classifyChain(value.constructor);
377
99
  }
378
- function hasOwnEnumerableFunction(value) {
379
- for (const key of Reflect.ownKeys(value)) {
380
- const descriptor = Object.getOwnPropertyDescriptor(value, key);
381
- if (!descriptor?.enumerable || !("value" in descriptor)) continue;
382
- if (typeof descriptor.value === "function") return true;
383
- }
384
- return false;
100
+
101
+ // src/node.ts
102
+ var byRaw = /* @__PURE__ */ new WeakMap();
103
+ var byProxy = /* @__PURE__ */ new WeakMap();
104
+ var proxyHandler;
105
+ function installProxyHandler(handler2) {
106
+ proxyHandler = handler2;
385
107
  }
386
- function admissionLane(value) {
387
- if (typeof value !== "object" || value === null) return "leaf";
388
- if (refSet2.has(value)) return "leaf";
389
- if (isUnsafeTracked(value)) return "track";
390
- const kind = classifyValue(value);
391
- if ((kind === "plain" || kind === "plainArray" || kind === "cleanClass") && Object.isFrozen(value)) return "leaf";
392
- if (kind === "plain" || kind === "plainArray") return "track";
393
- if (kind === "cleanClass" && !hasOwnEnumerableFunction(value)) return "track";
394
- return "reject";
108
+ function recordOf(value) {
109
+ return byRaw.get(value) ?? byProxy.get(value);
395
110
  }
396
- function isTrackable(value) {
397
- return admissionLane(value) === "track";
111
+ function rawOf(value) {
112
+ return recordOf(value)?.raw ?? value;
398
113
  }
399
-
400
- // src/ops/path.ts
401
- var createOperationPath = (segments) => Object.freeze([...segments]);
402
- var appendOperationPath = (path, segment) => Object.freeze([...path, segment]);
403
- var escapeSegment = (segment) => segment.replaceAll("~", "~0").replaceAll("/", "~1");
404
- var formatSegment = (segment) => typeof segment === "string" ? escapeSegment(segment) : String(segment);
405
- var formatOperationPath = (path) => path.length === 0 ? "" : `/${path.map(formatSegment).join("/")}`;
406
- var assertSafePath = (path) => {
407
- for (let index = 0; index < path.length; index++) {
408
- const segment = path[index];
409
- if (segment === "__proto__" || segment === "prototype" && path[index - 1] === "constructor") {
410
- throw new Error(`opshot: reserved operation path ${formatOperationPath(path)}`);
411
- }
412
- }
413
- };
414
-
415
- // src/ops/cloneValue.ts
416
- var { refSet: refSet3 } = unstable_getInternalStates();
417
- var isPlainArray = (value) => Array.isArray(value) && !refSet3.has(value);
418
- var isPlainObject = (value) => isTrackable(value) && !Array.isArray(value);
419
- var isCloneable = (value) => isTrackable(value);
420
- var CyclicValueError = class extends Error {
421
- constructor(path) {
422
- super(`opshot: cyclic value at ${formatOperationPath(path)}; use ignore() for back-linked structures, or ids`);
423
- this.name = "CyclicValueError";
424
- this.path = createOperationPath(path);
425
- }
426
- };
427
- var cyclicError = (path) => new CyclicValueError(path);
428
- var getCyclicPath = (error) => error instanceof CyclicValueError ? error.path : void 0;
429
- var CLONE_IN_PROGRESS = /* @__PURE__ */ Symbol("opshot.cloneValue.inProgress");
430
- var cloneValue = (value, memo2, path) => {
431
- if (!isCloneable(value)) return value;
432
- const cached = memo2.get(value);
433
- if (cached === CLONE_IN_PROGRESS) throw cyclicError(path);
434
- if (cached !== void 0) return cached;
435
- memo2.set(value, CLONE_IN_PROGRESS);
436
- const array = isPlainArray(value);
437
- const clone = array ? [] : {};
438
- Reflect.setPrototypeOf(clone, Reflect.getPrototypeOf(value));
439
- for (const key of Reflect.ownKeys(value)) {
440
- const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
441
- if (!descriptor) continue;
442
- if ("value" in descriptor) {
443
- Object.defineProperty(clone, key, {
444
- ...descriptor,
445
- value: cloneValue(descriptor.value, memo2, path)
446
- });
447
- } else {
448
- Object.defineProperty(clone, key, descriptor);
449
- }
450
- }
451
- memo2.set(value, clone);
452
- if (isUnsafeTracked(value)) unsafeTrack(clone);
453
- return clone;
454
- };
455
-
456
- // src/react/wrapperRegistry.ts
457
- var wrapperTargets = /* @__PURE__ */ new WeakMap();
458
- var registerWrapperTarget = (wrapper, target) => {
459
- wrapperTargets.set(wrapper, target);
460
- };
461
- var getRegisteredWrapperTarget = (wrapper) => wrapperTargets.get(wrapper);
462
-
463
- // src/identity.ts
464
- var isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
465
- var targetRegistry = /* @__PURE__ */ new WeakMap();
466
- var identityTokenRegistry = /* @__PURE__ */ new WeakMap();
467
- var { proxyStateMap: proxyStateMap2 } = unstable_getInternalStates();
468
- function registerSnapshotCopy(copy, target) {
469
- targetRegistry.set(copy, target);
114
+ function proxyOf(raw) {
115
+ const existing = recordOf(raw);
116
+ if (existing !== void 0) return existing.proxy;
117
+ if (proxyHandler === void 0) throw new Error("opshot: proxy handler is not installed");
118
+ const proxy = new Proxy(raw, proxyHandler);
119
+ const record = { raw, proxy, memberships: /* @__PURE__ */ new Map() };
120
+ byRaw.set(raw, record);
121
+ byProxy.set(proxy, record);
122
+ return proxy;
470
123
  }
471
- function getRegisteredTarget(copy) {
472
- return targetRegistry.get(copy);
124
+ function handlesOf(node) {
125
+ return membershipsOf(node).map(([handle]) => handle);
473
126
  }
474
- function peelIdentityLayer(current) {
475
- const untracked = getUntracked(current);
476
- if (untracked !== null && untracked !== current) return untracked;
477
- const wrapperTarget = getRegisteredWrapperTarget(current);
478
- if (wrapperTarget !== void 0 && wrapperTarget !== current) return wrapperTarget;
479
- const registeredTarget = targetRegistry.get(current);
480
- if (registeredTarget !== void 0 && registeredTarget !== current) return registeredTarget;
481
- return void 0;
127
+ function membershipsOf(node) {
128
+ const record = recordOf(rawOf(node));
129
+ if (record === void 0) return [];
130
+ const memberships = new Array();
131
+ for (const [handle, membership] of record.memberships) {
132
+ if (membership.edges > 0) memberships.push([handle, membership]);
133
+ }
134
+ return memberships;
482
135
  }
483
- function resolveIdentity(value) {
484
- let current = value;
485
- while (isObjectLike(current)) {
486
- const peeled = peelIdentityLayer(current);
487
- if (peeled !== void 0) {
488
- current = peeled;
489
- continue;
490
- }
491
- const proxyState = proxyStateMap2.get(current);
492
- if (proxyState !== void 0 && proxyState[0] !== current) {
493
- current = proxyState[0];
494
- continue;
495
- }
496
- break;
497
- }
498
- return current;
499
- }
500
- function identify(value) {
501
- const target = resolveIdentity(value);
502
- if (!isObjectLike(target)) return value;
503
- const existing = identityTokenRegistry.get(target);
504
- if (existing !== void 0) return existing;
505
- const token = Object.freeze({});
506
- identityTokenRegistry.set(target, token);
507
- return token;
136
+
137
+ // src/ignore.ts
138
+ var ignored = /* @__PURE__ */ new WeakSet();
139
+ function ignore(value, on = true) {
140
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
141
+ if (on) ignored.add(rawOf(value));
142
+ else ignored.delete(rawOf(value));
143
+ return value;
508
144
  }
509
- function isSameIdentity(first, second) {
510
- const resolvedFirst = resolveIdentity(first);
511
- const resolvedSecond = resolveIdentity(second);
512
- return resolvedFirst === resolvedSecond || resolvedFirst !== resolvedFirst && resolvedSecond !== resolvedSecond;
145
+ var isIgnored = (value) => ignored.has(rawOf(value));
146
+
147
+ // src/unsafeTrack.ts
148
+ var unsafeMarked = /* @__PURE__ */ new WeakSet();
149
+ function unsafeTrack(value, on = true) {
150
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
151
+ if (on) unsafeMarked.add(rawOf(value));
152
+ else unsafeMarked.delete(rawOf(value));
153
+ return value;
513
154
  }
155
+ var isUnsafeMarked = (value) => unsafeMarked.has(rawOf(value));
514
156
 
515
- // src/ops/operation.ts
516
- var operationBrand = /* @__PURE__ */ Symbol.for("opshot.operation");
517
- var valueOriginals = /* @__PURE__ */ new WeakMap();
518
- var OperationHalf = class {
519
- constructor(path) {
520
- this.path = createOperationPath(path);
521
- }
522
- };
523
- Object.defineProperty(OperationHalf.prototype, operationBrand, { value: true });
524
- var ValueHalf = class extends OperationHalf {
525
- get value() {
526
- return cloneValue(valueOriginals.get(this), /* @__PURE__ */ new WeakMap(), this.path);
527
- }
528
- constructor(path, value) {
529
- super(path);
530
- valueOriginals.set(this, value);
531
- if (!isCloneable(value)) Object.defineProperty(this, "value", { value, enumerable: true });
532
- }
533
- };
534
- var AddHalf = class extends ValueHalf {
535
- constructor() {
536
- super(...arguments);
537
- this.op = "add";
538
- }
539
- };
540
- var ReplaceHalf = class extends ValueHalf {
541
- constructor() {
542
- super(...arguments);
543
- this.op = "replace";
544
- }
545
- };
546
- var RemoveHalf = class extends OperationHalf {
547
- constructor() {
548
- super(...arguments);
549
- this.op = "remove";
157
+ // src/utils/dataEntries.ts
158
+ var walkDataEntries = (value) => {
159
+ const entries = new Array();
160
+ for (const key of Reflect.ownKeys(value)) {
161
+ if (typeof key !== "string" || key === "__proto__") continue;
162
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
163
+ if (descriptor === void 0 || !descriptor.enumerable || !("value" in descriptor)) continue;
164
+ entries.push({ key, value: descriptor.value, writable: descriptor.writable === true });
550
165
  }
166
+ return entries;
551
167
  };
552
- var isOperation = (value) => typeof value === "object" && value !== null && operationBrand in value;
553
- var getValueOriginal = (half) => valueOriginals.get(half);
554
- var createAddOperation = (path, value) => new AddHalf(path, value);
555
- var createReplaceOperation = (path, value) => new ReplaceHalf(path, value);
556
- var createRemoveOperation = (path) => new RemoveHalf(path);
557
168
 
558
- // src/ops/weight.ts
559
- var NODE_WEIGHT = 32;
560
- var KEY_WEIGHT = 16;
561
- var CHARACTER_WEIGHT = 2;
562
- var LEAF_WEIGHT = 16;
563
- var OPERATION_WEIGHT = 512;
564
- var addWeight = (state, amount) => {
565
- state.weight += amount;
566
- return state.weight <= state.budget;
567
- };
568
- var weigh = (value, state) => {
569
- if (state.weight > state.budget) return;
570
- if (typeof value === "string") {
571
- addWeight(state, LEAF_WEIGHT + CHARACTER_WEIGHT * value.length);
572
- return;
573
- }
574
- if (value === null || typeof value !== "object" && typeof value !== "function") {
575
- addWeight(state, LEAF_WEIGHT);
576
- return;
577
- }
578
- if (state.seen.has(value)) return;
579
- if (isCloneable(value)) {
580
- state.seen.add(value);
581
- if (!addWeight(state, NODE_WEIGHT)) return;
582
- for (const key of Object.keys(value)) {
583
- const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
584
- if (!descriptor || !("value" in descriptor)) continue;
585
- if (!addWeight(state, KEY_WEIGHT)) return;
586
- weigh(descriptor.value, state);
587
- if (state.weight > state.budget) return;
169
+ // src/edges.ts
170
+ var isTrackedEntry = (value, writable) => writable && typeof value === "object" && value !== null && !isIgnored(value) && !Object.isFrozen(value);
171
+ var checkNode = (node, entries, route) => {
172
+ const kind = classifyValue(node);
173
+ if (isDangerousKind(kind)) throw rejectionError(node, kind, route);
174
+ for (const entry of entries) {
175
+ if (typeof entry.value === "function") {
176
+ if (kind === "cleanClass") throw rejectionError(node, "cleanClass", [...route, entry.key]);
177
+ continue;
588
178
  }
179
+ if (typeof entry.value !== "object" || entry.value === null) continue;
180
+ if (isIgnored(entry.value) || Object.isFrozen(entry.value)) continue;
181
+ if (!entry.writable) throw nonWritablePropertyError(node, [...route, entry.key]);
589
182
  }
590
183
  };
591
- var weighValue = (value, budget) => {
592
- const state = { weight: 0, budget, seen: /* @__PURE__ */ new WeakSet() };
593
- weigh(value, state);
594
- return state.weight;
595
- };
596
-
597
- // src/ops/diff.ts
598
- var UNCAPPED_WEIGHT = Number.MAX_SAFE_INTEGER;
599
- var IncompatibleSnapshotRootsError = class extends Error {
600
- constructor() {
601
- super("opshot: diffSnapshots requires compatible supported object roots");
602
- this.name = "IncompatibleSnapshotRootsError";
184
+ var descend = (handle, node, route, checked) => {
185
+ const entries = walkDataEntries(node);
186
+ if (checked) checkNode(node, entries, route);
187
+ for (const entry of entries) {
188
+ if (isTrackedEntry(entry.value, entry.writable))
189
+ attach(handle, node, entry.key, entry.value, [...route, entry.key]);
603
190
  }
604
191
  };
605
- var addPair = (path, after) => ({
606
- do: createAddOperation(path, after),
607
- undo: createRemoveOperation(path)
608
- });
609
- var removePair = (path, before) => ({
610
- do: createRemoveOperation(path),
611
- undo: createAddOperation(path, before)
612
- });
613
- var replacePair = (path, before, after) => ({
614
- do: createReplaceOperation(path, after),
615
- undo: createReplaceOperation(path, before)
616
- });
617
- var weighCarried = (value) => weighValue(value, UNCAPPED_WEIGHT);
618
- var pushAdd = (ops, path, after) => {
619
- ops.push(addPair(path, after));
620
- return OPERATION_WEIGHT + weighCarried(after);
621
- };
622
- var pushRemove = (ops, path, before) => {
623
- ops.push(removePair(path, before));
624
- return OPERATION_WEIGHT + weighCarried(before);
625
- };
626
- var pushReplace = (ops, path, before, after) => {
627
- ops.push(replacePair(path, before, after));
628
- return OPERATION_WEIGHT + weighCarried(before) + weighCarried(after);
629
- };
630
- var tryCollapse = (before, after, path, ops, opsStart, atomicWeight) => {
631
- if (atomicWeight === 0) return 0;
632
- const beforeWeight = weighValue(before, atomicWeight);
633
- const afterWeight = weighValue(after, atomicWeight - beforeWeight);
634
- const collapsedWeight = OPERATION_WEIGHT + beforeWeight + afterWeight;
635
- if (collapsedWeight < atomicWeight) {
636
- assertSafeSubtree(before, path);
637
- assertSafeSubtree(after, path);
638
- ops.splice(opsStart, ops.length - opsStart, replacePair(path, before, after));
639
- return collapsedWeight;
640
- }
641
- return atomicWeight;
642
- };
643
- var hasAncestorPair = (ancestors, before, after) => ancestors.get(before)?.has(after) ?? false;
644
- var enterAncestorPair = (ancestors, before, after) => {
645
- const afterSet = ancestors.get(before) ?? /* @__PURE__ */ new Set();
646
- afterSet.add(after);
647
- ancestors.set(before, afterSet);
648
- };
649
- var exitAncestorPair = (ancestors, before, after) => {
650
- const afterSet = ancestors.get(before);
651
- if (!afterSet) return;
652
- afterSet.delete(after);
653
- if (afterSet.size === 0) ancestors.delete(before);
654
- };
655
- var isObjectLike2 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
656
- var sharesStorageIdentity = (before, after) => isObjectLike2(before) && isObjectLike2(after) && isSameIdentity(before, after);
657
- var assertSafeSubtree = (value, path, activeAncestors = /* @__PURE__ */ new WeakSet()) => {
658
- if (!isPlainArray(value) && !isPlainObject(value)) return;
659
- if (activeAncestors.has(value)) return;
660
- activeAncestors.add(value);
661
- try {
662
- for (const key of Object.keys(value)) {
663
- const nextPath = appendOperationPath(path, key);
664
- assertSafePath(nextPath);
665
- const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
666
- if (descriptor && "value" in descriptor) assertSafeSubtree(descriptor.value, nextPath, activeAncestors);
667
- }
668
- } finally {
669
- activeAncestors.delete(value);
192
+ var flip = (handle, node, route) => {
193
+ const membership = recordOf(node)?.memberships.get(handle);
194
+ if (!membership?.exempt) return;
195
+ membership.exempt = false;
196
+ const entries = walkDataEntries(node);
197
+ checkNode(node, entries, route);
198
+ for (const entry of entries) {
199
+ if (!isTrackedEntry(entry.value, entry.writable)) continue;
200
+ const child = rawOf(entry.value);
201
+ if (recordOf(child)?.memberships.has(handle) === true) flip(handle, child, [...route, entry.key]);
670
202
  }
671
203
  };
672
- var isCanonicalArrayIndex = (key) => {
673
- const index = Number(key);
674
- return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key;
675
- };
676
- var diffObjectProperties = (before, after, path, ops, ancestors, ignoreArrayIndexes) => {
677
- let weight = 0;
678
- for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)])) {
679
- if (ignoreArrayIndexes && isCanonicalArrayIndex(key)) continue;
680
- const nextPath = appendOperationPath(path, key);
681
- assertSafePath(nextPath);
682
- const beforeDescriptor = Reflect.getOwnPropertyDescriptor(before, key);
683
- const afterDescriptor = Reflect.getOwnPropertyDescriptor(after, key);
684
- if (beforeDescriptor?.get || afterDescriptor?.get) continue;
685
- if (!beforeDescriptor) {
686
- assertSafeSubtree(Reflect.get(after, key), nextPath);
687
- weight += pushAdd(ops, nextPath, Reflect.get(after, key));
688
- } else if (!afterDescriptor) {
689
- assertSafeSubtree(Reflect.get(before, key), nextPath);
690
- weight += pushRemove(ops, nextPath, Reflect.get(before, key));
691
- } else {
692
- weight += diffValue(Reflect.get(before, key), Reflect.get(after, key), nextPath, ops, ancestors);
693
- }
694
- }
695
- return weight;
696
- };
697
- var diffArray = (before, after, path, ops, ancestors) => {
698
- const overlap = Math.min(before.length, after.length);
699
- let weight = 0;
700
- for (let index = 0; index < overlap; index++) {
701
- const beforePresent = Object.hasOwn(before, index);
702
- const afterPresent = Object.hasOwn(after, index);
703
- const nextPath = appendOperationPath(path, index);
704
- if (!beforePresent && !afterPresent) continue;
705
- if (!beforePresent) weight += pushAdd(ops, nextPath, after[index]);
706
- else if (!afterPresent) weight += pushRemove(ops, nextPath, before[index]);
707
- else weight += diffValue(before[index], after[index], nextPath, ops, ancestors);
708
- }
709
- if (after.length > before.length) {
710
- weight += pushReplace(ops, appendOperationPath(path, "length"), before.length, after.length);
711
- for (let index = before.length; index < after.length; index++) {
712
- if (Object.hasOwn(after, index)) weight += pushAdd(ops, appendOperationPath(path, index), after[index]);
713
- }
714
- } else if (after.length < before.length) {
715
- for (let index = after.length; index < before.length; index++) {
716
- if (Object.hasOwn(before, index)) weight += pushRemove(ops, appendOperationPath(path, index), before[index]);
717
- }
718
- weight += pushReplace(ops, appendOperationPath(path, "length"), before.length, after.length);
204
+ var cascade = (handle, node, keys) => {
205
+ for (const key of keys) {
206
+ const value = Reflect.get(node, key);
207
+ if (typeof value !== "object" || value === null) continue;
208
+ const child = rawOf(value);
209
+ if (recordOf(child)?.memberships.has(handle) === true) detach(handle, child);
210
+ }
211
+ };
212
+ function attach(handle, parent, key, child, route) {
213
+ const parentMembership = recordOf(parent)?.memberships.get(handle);
214
+ if (parentMembership === void 0) return;
215
+ parentMembership.keys.add(key);
216
+ const rawChild = rawOf(child);
217
+ proxyOf(rawChild);
218
+ const record = recordOf(rawChild);
219
+ if (record === void 0) return;
220
+ const edgeExempt = parentMembership.exempt || isUnsafeMarked(rawChild);
221
+ const membership = record.memberships.get(handle);
222
+ if (membership === void 0) {
223
+ record.memberships.set(handle, { edges: 1, exempt: edgeExempt, keys: /* @__PURE__ */ new Set() });
224
+ descend(handle, rawChild, route, !edgeExempt);
225
+ return;
719
226
  }
720
- weight += diffObjectProperties(before, after, path, ops, ancestors, true);
721
- return weight;
722
- };
723
- var walkContainer = (before, after, path, ops, ancestors, walk) => {
724
- if (hasAncestorPair(ancestors, before, after)) throw cyclicError(path);
725
- enterAncestorPair(ancestors, before, after);
227
+ membership.edges += 1;
228
+ if (membership.exempt && !edgeExempt) flip(handle, rawChild, route);
229
+ }
230
+ function attachRoot(handle, root, exempt) {
231
+ const record = recordOf(root);
232
+ if (record === void 0) return;
233
+ record.memberships.set(handle, { edges: 1, exempt, keys: /* @__PURE__ */ new Set() });
726
234
  try {
727
- if (path.length === 0) {
728
- walk();
729
- return 0;
730
- }
731
- const opsStart = ops.length;
732
- const atomicWeight = walk();
733
- return tryCollapse(before, after, path, ops, opsStart, atomicWeight);
734
- } finally {
735
- exitAncestorPair(ancestors, before, after);
736
- }
737
- };
738
- var diffValue = (before, after, path, ops, ancestors) => {
739
- if (Object.is(before, after)) return 0;
740
- if (path.length > 0 && isObjectLike2(before) && isObjectLike2(after) && !sharesStorageIdentity(before, after)) {
741
- assertSafeSubtree(before, path);
742
- assertSafeSubtree(after, path);
743
- return pushReplace(ops, path, before, after);
744
- }
745
- if (isPlainArray(before) && isPlainArray(after)) {
746
- return walkContainer(before, after, path, ops, ancestors, () => diffArray(before, after, path, ops, ancestors));
747
- }
748
- if (isPlainObject(before) && isPlainObject(after)) {
749
- return walkContainer(
750
- before,
751
- after,
752
- path,
753
- ops,
754
- ancestors,
755
- () => diffObjectProperties(before, after, path, ops, ancestors, false)
756
- );
235
+ descend(handle, root, [], !exempt);
236
+ } catch (error) {
237
+ evict(handle, root);
238
+ throw error;
757
239
  }
758
- assertSafeSubtree(before, path);
759
- assertSafeSubtree(after, path);
760
- return pushReplace(ops, path, before, after);
761
- };
762
- var getRootKind = (value) => {
763
- if (isPlainArray(value)) return "plainArray";
764
- if (isPlainObject(value)) return "plainObject";
765
- return void 0;
766
- };
767
- function diffSnapshots(before, after) {
768
- const beforeKind = getRootKind(before);
769
- const afterKind = getRootKind(after);
770
- if (beforeKind === void 0 || beforeKind !== afterKind) throw new IncompatibleSnapshotRootsError();
771
- const ops = new Array();
772
- diffValue(before, after, createOperationPath([]), ops, /* @__PURE__ */ new Map());
773
- return ops;
774
240
  }
775
-
776
- // src/emit/emitterDeliver.ts
777
- var deliver = (record, ops, meta) => {
778
- const groupDelivers = [...record.groupListeners?.values() ?? []].flatMap((byChannel) => [...byChannel.values()]);
779
- for (const deliverToListener of groupDelivers) deliverToListener(record.target, ops, meta);
780
- const ownDelivers = [...record.listeners.values()].flatMap((byChannel) => [...byChannel.values()]);
781
- for (const deliverToListener of ownDelivers) deliverToListener(ops, meta);
782
- };
783
-
784
- // src/emit/resolveEmitterTarget.ts
785
- var { proxyStateMap: proxyStateMap3 } = unstable_getInternalStates();
786
- var isObjectLike3 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
787
- function resolveEmitterTarget(state) {
788
- let current = state;
789
- while (isObjectLike3(current)) {
790
- if (proxyStateMap3.has(current)) return current;
791
- const peeled = peelIdentityLayer(current);
792
- if (peeled === void 0) break;
793
- current = peeled;
794
- }
795
- if (!isObjectLike3(current) || !proxyStateMap3.has(current)) throw new Error("opshot: expected a state object");
796
- return current;
241
+ function detach(handle, child) {
242
+ const rawChild = rawOf(child);
243
+ const record = recordOf(rawChild);
244
+ const membership = record?.memberships.get(handle);
245
+ if (record === void 0 || membership === void 0) return;
246
+ membership.edges -= 1;
247
+ if (membership.edges > 0) return;
248
+ record.memberships.delete(handle);
249
+ cascade(handle, rawChild, membership.keys);
797
250
  }
798
-
799
- // src/emit/emitterRegistry.ts
800
- var emitters = /* @__PURE__ */ new WeakMap();
801
- function getEmitter(state) {
802
- return emitters.get(resolveEmitterTarget(state));
803
- }
804
- var hasListeners = (record) => record.listeners.size > 0 || (record.groupListeners?.size ?? 0) > 0;
805
- function getOrCreateEmitter(target, groupListeners) {
806
- const resolved = resolveEmitterTarget(target);
807
- const existing = emitters.get(resolved);
808
- if (existing !== void 0) return existing;
809
- const record = {
810
- listeners: /* @__PURE__ */ new Map(),
811
- groupListeners,
812
- lastReported: snapshot(resolved),
813
- isMutating: false,
814
- target: resolved
815
- };
816
- emitters.set(resolved, record);
817
- return record;
818
- }
819
- function deleteEmitter(target) {
820
- emitters.delete(target);
251
+ function evict(handle, node) {
252
+ const rawNode = rawOf(node);
253
+ const record = recordOf(rawNode);
254
+ const membership = record?.memberships.get(handle);
255
+ if (record === void 0 || membership === void 0) return;
256
+ record.memberships.delete(handle);
257
+ cascade(handle, rawNode, membership.keys);
821
258
  }
822
259
 
823
- // src/emit/emitterBare.ts
824
- var augmentBareCycleError = (error) => {
825
- const path = getCyclicPath(error);
826
- if (path === void 0) return void 0;
827
- return new Error(
828
- `opshot: a bare write created a cyclic value at ${formatOperationPath(path)}. Cycles cannot be tracked. This surfaced asynchronously because the write was not inside transact. Use transact for catchable cycle errors, ignore() for back-linked structures, or ids.`
829
- );
830
- };
831
- var requireObjectSnapshot = (value) => {
832
- if (value !== null && (typeof value === "object" || typeof value === "function")) return value;
833
- throw new Error("opshot: state snapshots must have an object root");
834
- };
835
- var armWatchdog = (record) => {
836
- if (record.disarm !== void 0) return;
837
- record.lastReported = snapshot(record.target);
838
- record.disarm = subscribe(record.target, () => {
839
- emitBareFlush(record.target);
840
- });
841
- };
842
- var disarmWatchdog = (record) => {
843
- record.disarm?.();
844
- record.disarm = void 0;
845
- };
846
- var reportBareDiff = (record) => {
847
- const current = snapshot(record.target);
848
- if (current === record.lastReported) return;
849
- const previous = record.lastReported;
850
- record.lastReported = current;
851
- if (!hasListeners(record)) return;
852
- try {
853
- const ops = diffSnapshots(requireObjectSnapshot(previous), requireObjectSnapshot(current));
854
- if (ops.length === 0) return;
855
- deliver(record, ops, void 0);
856
- } catch (error) {
857
- throw augmentBareCycleError(error) ?? error;
260
+ // src/handle.ts
261
+ function handleOf(state) {
262
+ const raw = rawOf(state);
263
+ const record = recordOf(raw);
264
+ if (record === void 0) return void 0;
265
+ for (const handle of record.memberships.keys()) {
266
+ if (handle.root === raw) return handle;
858
267
  }
859
- };
860
- var settlePendingBare = (record) => {
861
- reportBareDiff(record);
862
- };
863
- function emitBareFlush(target) {
864
- const record = getEmitter(target);
865
- if (record === void 0) return;
866
- reportBareDiff(record);
268
+ return void 0;
867
269
  }
868
- function mintGroupedEmitter(target, groupListeners) {
869
- const record = getOrCreateEmitter(target, groupListeners);
870
- armWatchdog(record);
871
- return record;
270
+ function requireHandle(state, message) {
271
+ const handle = handleOf(state);
272
+ if (handle === void 0) throw new Error(message);
273
+ return handle;
872
274
  }
873
275
 
874
- // src/utils/constructorName.ts
875
- var constructorName = (candidate) => typeof candidate === "function" && candidate.name !== "" ? candidate.name : "Object";
876
-
877
- // src/valtio/boundaryErrors.ts
878
- var ignoreOption = "ignore(value) to store it by reference, untracked";
879
- var unsafeTrackDataOption = "unsafeTrack(value) to track its data anyway";
880
- var unsafeTrackPrivateOption = "unsafeTrack(value) tracks public fields while private methods throw on snapshots and undo drops that state";
881
- var unsafeTrackSlotOption = "unsafeTrack(value) tracks public fields while slot methods throw on snapshots and undo drops that state";
882
- var unsafeTrackLossyOption = "unsafeTrack(value) to track it lossily";
883
- var boundaryError = (className, reason, options) => new Error(
884
- `opshot: ${className} cannot be tracked (${reason}). Options:
885
- ${options.map((option) => `- ${option}`).join("\n")}`
886
- );
887
- var slotContainerError = (className, trackedName) => boundaryError(className, "its state lives in internal slots", [
888
- `use ${trackedName} for a tracked equivalent`,
889
- unsafeTrackLossyOption,
890
- ignoreOption
891
- ]);
892
- var arraySubclassError = (className) => boundaryError(className, "array subclasses lose their prototype in snapshots", [
893
- unsafeTrackDataOption,
894
- ignoreOption
895
- ]);
896
- var cleanClassError = (className) => boundaryError(className, "arrow-method writes won't be tracked", [unsafeTrackDataOption, ignoreOption]);
897
- var privateClassError = (className) => boundaryError(className, "its state is hidden in private fields", [unsafeTrackPrivateOption, ignoreOption]);
898
- var nativeClassError = (className) => boundaryError(className, "its state is hidden in internal slots", [unsafeTrackSlotOption, ignoreOption]);
899
- var snapshotDonationError = (key) => new Error(
900
- `opshot: cannot assign a snapshot generation at "${String(key)}": a snapshot generation is a read-view, and assigning it creates a dead region. Clone the value, or replay through applyOps.`
901
- );
902
- var reservedDataPathError = (path) => new Error(`opshot: reserved data path /${path.join("/")}`);
903
- var inheritsFromPrototype = (value, prototype) => {
904
- for (let current = Reflect.getPrototypeOf(value); current !== null; current = Reflect.getPrototypeOf(current))
905
- if (current === prototype) return true;
906
- return false;
907
- };
908
- var rejectionError = (value, kind) => {
909
- const className = constructorName(value.constructor);
910
- if (inheritsFromPrototype(value, Map.prototype)) return slotContainerError(className, "TrackedMap");
911
- if (inheritsFromPrototype(value, Set.prototype)) return slotContainerError(className, "TrackedSet");
912
- if (inheritsFromPrototype(value, Date.prototype)) return slotContainerError(className, "TrackedDate");
913
- switch (kind) {
914
- case "arraySubclass":
915
- return arraySubclassError(className);
916
- case "cleanClass":
917
- return cleanClassError(className);
918
- case "privateClass":
919
- return privateClassError(className);
920
- case "nativeClass":
921
- return nativeClassError(className);
276
+ // src/batch.ts
277
+ var metaStack = [];
278
+ function currentMeta() {
279
+ return metaStack.length === 0 ? void 0 : metaStack[metaStack.length - 1];
280
+ }
281
+ function batch(callback, meta) {
282
+ metaStack.push(meta);
283
+ try {
284
+ callback();
285
+ } finally {
286
+ metaStack.pop();
922
287
  }
923
- };
288
+ }
924
289
 
925
- // src/valtio/constructorPathGuard.ts
926
- var { refSet: refSet4 } = unstable_getInternalStates();
927
- var constructorPathTargetCounts = /* @__PURE__ */ new WeakMap();
928
- var rootGraphsByRoot = /* @__PURE__ */ new WeakMap();
929
- var rootGraphReferences = /* @__PURE__ */ new WeakMap();
930
- var rootGraphsByTarget = /* @__PURE__ */ new WeakMap();
931
- var getRawObject = (value) => {
932
- const resolved = resolveIdentity(value);
933
- return typeof resolved === "object" && resolved !== null ? resolved : void 0;
934
- };
935
- var getTrackedRawObject = (value) => {
936
- const target = getRawObject(value);
937
- if (!target || refSet4.has(target) || Object.isFrozen(target)) return void 0;
938
- return isTrackable(target) ? target : void 0;
939
- };
940
- var getEnumerableDataChild = (target, key) => {
941
- if (typeof key !== "string") return void 0;
942
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
943
- if (!descriptor?.enumerable || !("value" in descriptor)) return void 0;
944
- return getTrackedRawObject(descriptor.value);
945
- };
946
- var adjustConstructorPathTarget = (target, change) => {
947
- const next = (constructorPathTargetCounts.get(target) ?? 0) + change;
948
- if (next > 0) constructorPathTargetCounts.set(target, next);
949
- else constructorPathTargetCounts.delete(target);
950
- };
951
- var constructorPathTargetCount = (target) => constructorPathTargetCounts.get(target) ?? 0;
952
- var releaseFinalizationState = (state) => {
953
- if (!state.active) return;
954
- state.active = false;
955
- for (const { target, count } of state.constructorTargets) {
956
- const resolved = target.deref();
957
- if (resolved) adjustConstructorPathTarget(resolved, -count);
958
- }
959
- state.constructorTargets = [];
960
- };
961
- var rootGraphFinalizer = new FinalizationRegistry(releaseFinalizationState);
962
- var getRootGraphReference = (graph) => {
963
- const existing = rootGraphReferences.get(graph);
964
- if (existing) return existing;
965
- const reference = new WeakRef(graph);
966
- rootGraphReferences.set(graph, reference);
967
- return reference;
968
- };
969
- var releaseRootGraph = (graph) => {
970
- const reference = getRootGraphReference(graph);
971
- for (const target of graph.targets) {
972
- const references = rootGraphsByTarget.get(target);
973
- references?.delete(reference);
974
- if (references?.size === 0) rootGraphsByTarget.delete(target);
975
- }
976
- const root = graph.root.deref();
977
- if (root) rootGraphsByRoot.delete(root);
978
- releaseFinalizationState(graph.finalizationState);
979
- rootGraphFinalizer.unregister(graph.finalizationState);
980
- graph.targets.clear();
981
- graph.constructorTargets.clear();
982
- };
983
- var getRootGraphs = (target) => {
984
- const references = rootGraphsByTarget.get(target);
985
- if (!references) return [];
986
- const graphs = new Array();
987
- for (const reference of references) {
988
- const graph = reference.deref();
989
- if (!graph) {
990
- references.delete(reference);
991
- continue;
992
- }
993
- if (!graph.root.deref()) {
994
- releaseRootGraph(graph);
995
- continue;
290
+ // src/emit/emitterDeliver.ts
291
+ var runDelivery = (pending, failures) => {
292
+ for (const deliver of pending.deliveries) {
293
+ try {
294
+ deliver(pending.operations);
295
+ } catch (error) {
296
+ failures.push(error);
996
297
  }
997
- graphs.push(graph);
998
298
  }
999
- if (references.size === 0) rootGraphsByTarget.delete(target);
1000
- return graphs;
1001
299
  };
1002
- var recomputeRootGraph = (graph) => {
1003
- const root = graph.root.deref();
1004
- if (!root) {
1005
- releaseRootGraph(graph);
1006
- return;
1007
- }
1008
- const targets = /* @__PURE__ */ new Set();
1009
- const constructorTargets = /* @__PURE__ */ new Map();
1010
- const visit = (target) => {
1011
- if (targets.has(target)) return;
1012
- targets.add(target);
1013
- const constructorTarget = getEnumerableDataChild(target, "constructor");
1014
- if (constructorTarget)
1015
- constructorTargets.set(constructorTarget, (constructorTargets.get(constructorTarget) ?? 0) + 1);
1016
- for (const key of Object.keys(target)) {
1017
- const child = getEnumerableDataChild(target, key);
1018
- if (child) visit(child);
1019
- }
1020
- };
1021
- visit(root);
1022
- for (const [target, count] of graph.constructorTargets) adjustConstructorPathTarget(target, -count);
1023
- for (const [target, count] of constructorTargets) adjustConstructorPathTarget(target, count);
1024
- for (const target of graph.targets) {
1025
- if (targets.has(target)) continue;
1026
- const references = rootGraphsByTarget.get(target);
1027
- references?.delete(getRootGraphReference(graph));
1028
- if (references?.size === 0) rootGraphsByTarget.delete(target);
1029
- }
1030
- for (const target of targets) {
1031
- if (graph.targets.has(target)) continue;
1032
- const references = rootGraphsByTarget.get(target) ?? /* @__PURE__ */ new Set();
1033
- references.add(getRootGraphReference(graph));
1034
- rootGraphsByTarget.set(target, references);
1035
- }
1036
- graph.targets = targets;
1037
- graph.constructorTargets = constructorTargets;
1038
- graph.finalizationState.constructorTargets = [...constructorTargets].map(([target, count]) => ({
1039
- target: new WeakRef(target),
1040
- count
1041
- }));
1042
- };
1043
- var registerTrackedRoot = (value) => {
1044
- const root = getTrackedRawObject(value);
1045
- if (!root || rootGraphsByRoot.has(root)) return;
1046
- const finalizationState = { active: true, constructorTargets: [] };
1047
- const graph = {
1048
- root: new WeakRef(root),
1049
- finalizationState,
1050
- targets: /* @__PURE__ */ new Set(),
1051
- constructorTargets: /* @__PURE__ */ new Map()
1052
- };
1053
- rootGraphsByRoot.set(root, graph);
1054
- rootGraphFinalizer.register(root, finalizationState, finalizationState);
1055
- recomputeRootGraph(graph);
300
+ var raiseFailures = (failures) => {
301
+ if (failures.length === 0) return;
302
+ if (failures.length > 1) throw new AggregateError(failures, "opshot: listeners failed during delivery");
303
+ throw failures[0];
1056
304
  };
1057
-
1058
- // src/valtio/snapshotAccessors.ts
1059
- var { refSet: refSet5, proxyStateMap: proxyStateMap4, snapCache: snapCache2 } = unstable_getInternalStates();
1060
- var createSnapshotPreservingAccessors = (target, version) => {
1061
- const cached = snapCache2.get(target);
1062
- if (cached?.[0] === version) {
1063
- const cachedSnapshot = cached[1];
1064
- registerSnapshotCopy(cachedSnapshot, target);
1065
- return cachedSnapshot;
1066
- }
1067
- const snap = Array.isArray(target) ? [] : Object.create(Reflect.getPrototypeOf(target));
1068
- registerSnapshotCopy(snap, target);
1069
- markToTrack(snap, true);
1070
- snapCache2.set(target, [version, snap]);
1071
- for (const key of Reflect.ownKeys(target)) {
1072
- if (Object.getOwnPropertyDescriptor(snap, key)) continue;
1073
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
1074
- if (!descriptor) continue;
1075
- if (descriptor.get || descriptor.set) {
1076
- Object.defineProperty(snap, key, {
1077
- get: descriptor.get,
1078
- set: descriptor.set,
1079
- enumerable: descriptor.enumerable,
1080
- configurable: true
1081
- });
1082
- continue;
1083
- }
1084
- const value = Reflect.get(target, key);
1085
- const snapshotDescriptor = { value, enumerable: descriptor.enumerable, configurable: true };
1086
- if (typeof value === "object" && value !== null) {
1087
- if (refSet5.has(value)) {
1088
- markToTrack(value, false);
1089
- } else {
1090
- const childState = proxyStateMap4.get(value);
1091
- if (childState)
1092
- snapshotDescriptor.value = createSnapshotPreservingAccessors(childState[0], childState[1]());
1093
- }
1094
- }
1095
- Object.defineProperty(snap, key, snapshotDescriptor);
1096
- }
1097
- if (Array.isArray(target) && snap.length !== target.length) {
1098
- snap.length = target.length;
1099
- }
1100
- if (isUnsafeTracked(target)) unsafeTrack(snap);
1101
- return snap;
305
+ var queuedDeliveries = [];
306
+ var deliveryFailures = [];
307
+ var isDraining = false;
308
+ var prepareDelivery = (handle, operations) => ({
309
+ deliveries: [...handle.subscribers.values()],
310
+ operations
311
+ });
312
+ var enqueueDelivery = (pending) => {
313
+ queuedDeliveries.push(pending);
1102
314
  };
1103
-
1104
- // src/valtio/boundary.ts
1105
- var assertSafeDataPaths = (value, path = new Array(), activeAncestors = /* @__PURE__ */ new WeakSet()) => {
1106
- if (typeof value !== "object" || value === null || activeAncestors.has(value)) return;
1107
- activeAncestors.add(value);
315
+ var drainDeliveries = () => {
316
+ if (isDraining) return;
317
+ isDraining = true;
318
+ let failures = [];
1108
319
  try {
1109
- for (const key of Object.keys(value)) {
1110
- const nextPath = [...path, key];
1111
- if (key === "__proto__" || key === "prototype" && path[path.length - 1] === "constructor")
1112
- throw reservedDataPathError(nextPath);
1113
- const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
1114
- if (descriptor && "value" in descriptor) assertSafeDataPaths(descriptor.value, nextPath, activeAncestors);
320
+ while (queuedDeliveries.length > 0) {
321
+ for (const queued of queuedDeliveries.splice(0, queuedDeliveries.length)) {
322
+ runDelivery(queued, deliveryFailures);
323
+ }
1115
324
  }
1116
325
  } finally {
1117
- activeAncestors.delete(value);
326
+ isDraining = false;
327
+ failures = deliveryFailures.splice(0, deliveryFailures.length);
1118
328
  }
329
+ raiseFailures(failures);
1119
330
  };
1120
- var installed = false;
1121
- function installBoundary() {
1122
- if (installed) return;
1123
- installed = true;
1124
- unstable_replaceInternalFunction("canProxy", () => (value) => {
1125
- if (typeof value !== "object" || value === null) return false;
1126
- const lane = admissionLane(value);
1127
- if (lane !== "reject") return lane === "track";
1128
- const kind = classifyValue(value);
1129
- if (kind === "plain" || kind === "plainArray") return true;
1130
- throw rejectionError(value, kind);
1131
- });
1132
- unstable_replaceInternalFunction("createSnapshot", () => createSnapshotPreservingAccessors);
1133
- unstable_replaceInternalFunction(
1134
- "createHandler",
1135
- (createHandler2) => (isInitializing, addPropListener, removePropListener, notifyUpdate) => {
1136
- let setDepth = 0;
1137
- const handler = createHandler2(isInitializing, addPropListener, removePropListener, notifyUpdate);
1138
- const defaultDelete = handler.deleteProperty;
1139
- const defaultSet = handler.set;
1140
- if (!defaultDelete || !defaultSet)
1141
- throw new Error("opshot: valtio default handler is missing a mutation trap");
1142
- return {
1143
- ...handler,
1144
- deleteProperty(target, prop) {
1145
- const rootGraphs = getRootGraphs(target);
1146
- const previousChild = rootGraphs.length > 0 ? getEnumerableDataChild(target, prop) : void 0;
1147
- const hadOwn = Object.hasOwn(target, prop);
1148
- const deleted = defaultDelete(target, prop);
1149
- if (deleted && hadOwn && previousChild && !Object.hasOwn(target, prop))
1150
- for (const graph of rootGraphs) recomputeRootGraph(graph);
1151
- return deleted;
1152
- },
1153
- set(target, prop, value, receiver) {
1154
- const assigned = value;
1155
- if (prop === "__proto__") throw reservedDataPathError(["__proto__"]);
1156
- if (prop === "prototype" && constructorPathTargetCount(target) > 0)
1157
- throw reservedDataPathError(["constructor", "prototype"]);
1158
- if (prop === "constructor" && typeof assigned === "object" && assigned !== null) {
1159
- const prototypeDescriptor = Reflect.getOwnPropertyDescriptor(assigned, "prototype");
1160
- if (prototypeDescriptor?.enumerable) throw reservedDataPathError(["constructor", "prototype"]);
1161
- }
1162
- assertSafeDataPaths(assigned, typeof prop === "string" ? [prop] : []);
1163
- if (typeof assigned === "object" && assigned !== null) {
1164
- const untracked = getUntracked(assigned) ?? assigned;
1165
- if (getRegisteredTarget(untracked) !== void 0) throw snapshotDonationError(prop);
1166
- }
1167
- const rootGraphs = getRootGraphs(target);
1168
- const previousChild = rootGraphs.length > 0 ? getEnumerableDataChild(target, prop) : void 0;
1169
- const previousLength = rootGraphs.length > 0 && Array.isArray(target) && prop === "length" ? Reflect.get(target, "length") : void 0;
1170
- setDepth += 1;
1171
- try {
1172
- const written = defaultSet(target, prop, value, receiver);
1173
- const currentChild = rootGraphs.length > 0 ? getEnumerableDataChild(target, prop) : void 0;
1174
- const currentLength = previousLength === void 0 ? void 0 : Reflect.get(target, "length");
1175
- if (previousChild !== currentChild || previousLength !== currentLength)
1176
- for (const graph of rootGraphs) recomputeRootGraph(graph);
1177
- return written;
1178
- } finally {
1179
- setDepth -= 1;
1180
- }
1181
- },
1182
- defineProperty(target, prop, descriptor) {
1183
- if (setDepth > 0 || isInitializing()) return Reflect.defineProperty(target, prop, descriptor);
1184
- throw new Error(
1185
- "opshot: defineProperty is not supported on tracked state; define properties in the createMutableState input"
1186
- );
1187
- },
1188
- setPrototypeOf() {
1189
- throw new Error("opshot: setPrototypeOf is not supported on tracked state");
1190
- }
1191
- };
1192
- }
1193
- );
1194
- }
1195
331
 
1196
- // src/createMutableState.ts
1197
- function createMutableState(properties, group) {
1198
- installBoundary();
1199
- assertSafeDataPaths(properties);
1200
- const base = Object.create(Reflect.getPrototypeOf(properties));
1201
- Object.defineProperties(base, Object.getOwnPropertyDescriptors(properties));
1202
- const proxied = proxy(base);
1203
- registerTrackedRoot(base);
1204
- if (group !== void 0) {
1205
- mintGroupedEmitter(proxied, getGroupListeners(group));
1206
- }
1207
- return proxied;
1208
- }
1209
-
1210
- // src/createGroup.ts
1211
- var groupListenersByGroup = /* @__PURE__ */ new WeakMap();
1212
- function isGroup(value) {
1213
- return typeof value === "object" && value !== null && groupListenersByGroup.has(value);
1214
- }
1215
- function getGroupListeners(group) {
1216
- const listeners = groupListenersByGroup.get(group);
1217
- if (listeners === void 0) throw new Error("opshot: unknown group");
1218
- return listeners;
1219
- }
1220
- function createGroup() {
1221
- const listeners = /* @__PURE__ */ new Map();
1222
- const group = {
1223
- createMutableState(properties) {
1224
- return createMutableState(properties, group);
1225
- }
1226
- };
1227
- groupListenersByGroup.set(group, listeners);
1228
- return group;
1229
- }
1230
-
1231
- // src/emit/emitterListeners.ts
1232
- function addStateListener(state, listener, channelId, deliver2) {
1233
- const target = resolveEmitterTarget(state);
1234
- const record = getOrCreateEmitter(target);
1235
- if (record.disarm === void 0 && record.groupListeners === void 0) {
1236
- armWatchdog(record);
1237
- }
1238
- let byChannel = record.listeners.get(listener);
1239
- if (byChannel === void 0) {
1240
- byChannel = /* @__PURE__ */ new Map();
1241
- record.listeners.set(listener, byChannel);
1242
- }
1243
- byChannel.set(channelId, deliver2);
1244
- return () => {
1245
- const channels = record.listeners.get(listener);
1246
- if (channels?.has(channelId) !== true) return;
1247
- settlePendingBare(record);
1248
- channels.delete(channelId);
1249
- if (channels.size === 0) record.listeners.delete(listener);
1250
- if (record.groupListeners === void 0 && record.listeners.size === 0) {
1251
- disarmWatchdog(record);
1252
- deleteEmitter(target);
1253
- }
1254
- };
1255
- }
1256
- function addGroupListener(groupListeners, listener, channelId, deliver2) {
1257
- let byChannel = groupListeners.get(listener);
1258
- if (byChannel === void 0) {
1259
- byChannel = /* @__PURE__ */ new Map();
1260
- groupListeners.set(listener, byChannel);
1261
- }
1262
- byChannel.set(channelId, deliver2);
1263
- return () => {
1264
- const channels = groupListeners.get(listener);
1265
- if (channels?.has(channelId) !== true) return;
1266
- channels.delete(channelId);
1267
- if (channels.size === 0) groupListeners.delete(listener);
1268
- };
1269
- }
1270
-
1271
- // src/transact.ts
1272
- function transact(state, mutate, meta) {
1273
- const record = getEmitter(state);
1274
- if (record === void 0) {
1275
- mutate();
332
+ // src/emit/window.ts
333
+ function recordOperation(handle, raw, pending) {
334
+ let byKey = handle.pendingIndex.get(raw);
335
+ if (byKey === void 0) {
336
+ byKey = /* @__PURE__ */ new Map();
337
+ handle.pendingIndex.set(raw, byKey);
338
+ }
339
+ const index = byKey.get(pending.key);
340
+ const existing = index === void 0 ? void 0 : handle.pending[index];
341
+ if (existing !== void 0 && Object.is(existing.meta, pending.meta)) {
342
+ existing.after = pending.after;
343
+ existing.hasAfter = pending.hasAfter;
1276
344
  return;
1277
345
  }
1278
- if (record.isMutating) throw new Error("opshot: nested transact on the same state");
1279
- settlePendingBare(record);
1280
- record.isMutating = true;
1281
- try {
1282
- mutate();
1283
- } finally {
1284
- record.isMutating = false;
1285
- }
1286
- const after = snapshot(record.target);
1287
- const before = record.lastReported;
1288
- record.lastReported = after;
1289
- if (before === after) return;
1290
- if (!hasListeners(record)) return;
1291
- const ops = diffSnapshots(requireObjectSnapshot(before), requireObjectSnapshot(after));
1292
- if (ops.length === 0) return;
1293
- deliver(record, ops, meta);
346
+ handle.pending.push(pending);
347
+ byKey.set(pending.key, handle.pending.length - 1);
348
+ if (handle.isFlushScheduled) return;
349
+ handle.isFlushScheduled = true;
350
+ const run = () => {
351
+ flush(handle);
352
+ };
353
+ void Promise.resolve().then(() => {
354
+ const emitOn = handle.emitOn;
355
+ if (emitOn === void 0) run();
356
+ else emitOn(run);
357
+ });
1294
358
  }
1295
-
1296
- // src/ops/applyOps.ts
1297
- var isObjectLike4 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
1298
- var sameValueZero = (first, second) => first === second || first !== first && second !== second;
1299
- var sameIdentity = (first, second) => sameValueZero(resolveIdentity(first), resolveIdentity(second));
1300
- var assertApplicable = (operation) => {
1301
- if (typeof operation === "object" && operation !== null && "do" in operation) {
1302
- throw new Error("opshot: applyOps applies operation halves; pass op.do or op.undo.");
1303
- }
1304
- if (!isOperation(operation)) {
1305
- throw new Error(
1306
- "opshot: this op is a copy (spread, JSON, or structuredClone) and has lost its value. Apply the op objects the listener delivered; never copy them."
1307
- );
1308
- }
1309
- };
1310
- var unresolvedError = (path) => new Error(`opshot: ${formatOperationPath(path)} does not resolve to a supported operation address`);
1311
- var matchesAppliedValue = (current, expected) => {
1312
- if (isObjectLike4(current) && isObjectLike4(expected)) return sameIdentity(current, expected);
1313
- return Object.is(current, expected);
1314
- };
1315
- var setOrThrow = (target, key, value) => {
1316
- const written = Reflect.set(target, key, value);
1317
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
1318
- const current = descriptor && "value" in descriptor ? Reflect.get(target, key) : void 0;
1319
- if (!written || !descriptor || !("value" in descriptor) || !matchesAppliedValue(current, value)) {
1320
- throw new Error(`opshot: replay could not restore ${String(key)}`);
1321
- }
1322
- };
1323
- var deleteOrThrow = (target, key) => {
1324
- if (!Reflect.deleteProperty(target, key)) throw new Error(`opshot: replay could not remove ${String(key)}`);
1325
- };
1326
- var restoreRecordedContent = (attached, recorded, restored) => {
1327
- if (restored.has(recorded)) return;
1328
- restored.add(recorded);
1329
- const recordedKeys = Reflect.ownKeys(recorded);
1330
- const orderedKeys = Array.isArray(recorded) ? ["length", ...recordedKeys.filter((key) => key !== "length")] : recordedKeys;
1331
- for (const key of orderedKeys) {
1332
- const descriptor = Reflect.getOwnPropertyDescriptor(recorded, key);
1333
- const attachedDescriptor = Reflect.getOwnPropertyDescriptor(attached, key);
1334
- if (!descriptor || !("value" in descriptor) || attachedDescriptor && !("value" in attachedDescriptor)) continue;
1335
- const value = descriptor.value;
1336
- if (isObjectLike4(value)) {
1337
- const target = getRegisteredTarget(value);
1338
- if (target !== void 0) {
1339
- setOrThrow(attached, key, target);
1340
- const child = Reflect.get(attached, key);
1341
- if (!isObjectLike4(child)) throw new Error(`opshot: replay could not reattach ${String(key)}`);
1342
- restoreRecordedContent(child, value, restored);
1343
- continue;
1344
- }
1345
- }
1346
- setOrThrow(attached, key, value);
1347
- }
1348
- for (const key of Reflect.ownKeys(attached)) {
1349
- if (Object.hasOwn(recorded, key)) continue;
1350
- const descriptor = Reflect.getOwnPropertyDescriptor(attached, key);
1351
- if (descriptor && "value" in descriptor) deleteOrThrow(attached, key);
359
+ function flush(handle) {
360
+ handle.isFlushScheduled = false;
361
+ const pending = handle.pending.splice(0);
362
+ handle.pendingIndex.clear();
363
+ const operations = new Array();
364
+ for (const item of pending) {
365
+ if (item.hasBefore === item.hasAfter && (!item.hasBefore || Object.is(item.before, item.after))) continue;
366
+ const operation = {
367
+ node: item.node,
368
+ key: item.key,
369
+ meta: item.meta,
370
+ ...item.hasBefore ? { before: item.before } : {},
371
+ ...item.hasAfter ? { after: item.after } : {}
372
+ };
373
+ operations.push(Object.freeze(operation));
1352
374
  }
1353
- };
1354
- var getValuePayload = (operation) => {
1355
- const original = getValueOriginal(operation);
1356
- if (original !== void 0) {
1357
- if (isObjectLike4(original) && getRegisteredTarget(original) !== void 0)
1358
- return { recorded: original, fallback: void 0 };
1359
- return { recorded: original, fallback: operation.value };
1360
- }
1361
- return { recorded: operation.value, fallback: operation.value };
1362
- };
1363
- var restoreValue = (payload, attach, readAttached) => {
1364
- if (isObjectLike4(payload.recorded)) {
1365
- const target = getRegisteredTarget(payload.recorded);
1366
- if (target !== void 0) {
1367
- attach(target);
1368
- const attached = readAttached();
1369
- if (!isObjectLike4(attached)) throw new Error("opshot: replay could not read a reattached target");
1370
- restoreRecordedContent(attached, payload.recorded, /* @__PURE__ */ new WeakSet());
1371
- return;
375
+ const edges = /* @__PURE__ */ new Map();
376
+ const nodes = /* @__PURE__ */ new Set();
377
+ for (const operation of operations) {
378
+ const raw = rawOf(operation.node);
379
+ let keys = edges.get(raw);
380
+ if (keys === void 0) {
381
+ keys = /* @__PURE__ */ new Set();
382
+ edges.set(raw, keys);
1372
383
  }
1373
- }
1374
- attach(payload.fallback);
1375
- };
1376
- var getInheritedDescriptor = (target, key) => {
1377
- let prototype = Reflect.getPrototypeOf(target);
1378
- while (prototype !== null) {
1379
- const descriptor = Reflect.getOwnPropertyDescriptor(prototype, key);
1380
- if (descriptor) return descriptor;
1381
- prototype = Reflect.getPrototypeOf(prototype);
384
+ keys.add(operation.key);
385
+ nodes.add(raw);
386
+ }
387
+ const dirty = { edges, nodes };
388
+ handle.lastDirty = dirty;
389
+ if (operations.length === 0) return;
390
+ enqueueDelivery(prepareDelivery(handle, operations));
391
+ drainDeliveries();
392
+ }
393
+
394
+ // src/utils/predicates.ts
395
+ var isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
396
+
397
+ // src/proxy.ts
398
+ var prototypeFunctionOf = (target, key) => {
399
+ for (let holder = Reflect.getPrototypeOf(target); holder !== null; holder = Reflect.getPrototypeOf(holder)) {
400
+ const descriptor = Reflect.getOwnPropertyDescriptor(holder, key);
401
+ if (descriptor === void 0) continue;
402
+ const method = "value" in descriptor ? descriptor.value : void 0;
403
+ return typeof method === "function" ? method : void 0;
1382
404
  }
1383
405
  return void 0;
1384
406
  };
1385
- var isCanonicalArrayIndex2 = (segment) => Number.isInteger(segment) && typeof segment === "number" && segment >= 0 && segment < 4294967295;
1386
- var isCanonicalArrayIndexString = (segment) => {
1387
- const index = Number(segment);
1388
- return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === segment;
1389
- };
1390
- var requirePlainSegment = (parent, segment, path) => {
1391
- if (Array.isArray(parent)) {
1392
- if (isCanonicalArrayIndex2(segment)) return segment;
1393
- if (typeof segment === "string" && !isCanonicalArrayIndexString(segment)) return segment;
1394
- throw unresolvedError(path);
1395
- }
1396
- if (typeof segment === "string") return segment;
1397
- throw unresolvedError(path);
407
+ var isRideAlongKey = (target, key) => {
408
+ if (key === "__proto__") return true;
409
+ if (key === "length") return false;
410
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
411
+ return descriptor?.enumerable === false;
1398
412
  };
1399
- var requirePlainProperty = (parent, segment, path) => {
1400
- if (Array.isArray(parent) && segment === "length") return parent.length;
1401
- const key = requirePlainSegment(parent, segment, path);
1402
- const descriptor = Reflect.getOwnPropertyDescriptor(parent, key);
1403
- if (!descriptor || !descriptor.enumerable || !("value" in descriptor)) throw unresolvedError(path);
1404
- return Reflect.get(parent, key);
413
+ var writesThroughAccessor = (target, property) => {
414
+ let holder = target;
415
+ while (holder !== null) {
416
+ const descriptor = Reflect.getOwnPropertyDescriptor(holder, property);
417
+ if (descriptor !== void 0) return !("value" in descriptor);
418
+ holder = Reflect.getPrototypeOf(holder);
419
+ }
420
+ return false;
1405
421
  };
1406
- var resolveTraversalSegment = (parent, segment, path) => {
1407
- if (!isObjectLike4(parent)) throw unresolvedError(path);
1408
- return requirePlainProperty(parent, segment, path);
422
+ var proxied = (value) => isObjectLike(value) ? recordOf(value)?.proxy ?? value : value;
423
+ var truncatedOwnEntriesOf = (target, next) => {
424
+ if (!Array.isArray(target)) return [];
425
+ const coercible = next === null || typeof next !== "object" && typeof next !== "function";
426
+ const newLength = coercible ? Number(next) : Number.NaN;
427
+ if (!Number.isInteger(newLength) || newLength < 0 || newLength >= target.length) return [];
428
+ const truncated = new Array();
429
+ for (let index = newLength; index < target.length; index += 1) {
430
+ const key = String(index);
431
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
432
+ if (descriptor === void 0 || !("value" in descriptor)) continue;
433
+ truncated.push({
434
+ key,
435
+ value: descriptor.value,
436
+ writable: descriptor.writable === true
437
+ });
438
+ }
439
+ return truncated;
1409
440
  };
1410
- var resolveTerminal = (root, path) => {
1411
- assertSafePath(path);
1412
- if (path.length === 0) throw new Error("opshot: root operations are not supported");
1413
- let parent = root;
1414
- for (let index = 0; index < path.length - 1; index++) parent = resolveTraversalSegment(parent, path[index], path);
1415
- if (!isObjectLike4(parent)) throw unresolvedError(path);
1416
- return { parent, segment: path[path.length - 1] };
441
+ var ownDataDescriptor = (target, key) => {
442
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
443
+ if (descriptor === void 0 || !("value" in descriptor)) return { hadPrevious: false, previous: void 0 };
444
+ return { hadPrevious: true, previous: descriptor.value };
1417
445
  };
1418
- var applyPlain = (parent, segment, operation) => {
1419
- const path = operation.path;
1420
- if (Array.isArray(parent) && segment === "length") {
1421
- if (operation.op !== "replace" || typeof operation.value !== "number" || !Number.isInteger(operation.value) || operation.value < 0 || operation.value > 4294967295) {
1422
- throw unresolvedError(path);
446
+ var handler = {
447
+ get(target, key, receiver) {
448
+ const value = Reflect.get(target, key, receiver);
449
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
450
+ const locked = descriptor?.writable === false && descriptor.configurable === false;
451
+ if (locked || !isObjectLike(value)) return value;
452
+ if (typeof value === "function") {
453
+ const method = prototypeFunctionOf(target, key);
454
+ const kind = classifyValue(target);
455
+ if (method !== void 0 && value === method && (kind === "nativeClass" || kind === "privateClass")) {
456
+ const bound = Function.prototype.bind.call(method, target);
457
+ return typeof bound === "function" ? bound : value;
458
+ }
459
+ return value;
1423
460
  }
1424
- setOrThrow(parent, "length", operation.value);
1425
- return;
1426
- }
1427
- const key = requirePlainSegment(parent, segment, path);
1428
- const descriptor = Reflect.getOwnPropertyDescriptor(parent, key);
1429
- const present = descriptor !== void 0 && descriptor.enumerable && "value" in descriptor;
1430
- if (descriptor !== void 0 && !present) throw unresolvedError(path);
1431
- if (operation.op === "add" && present) throw unresolvedError(path);
1432
- if (operation.op !== "add" && !present) throw unresolvedError(path);
1433
- if (operation.op === "add") {
1434
- const inheritedDescriptor = getInheritedDescriptor(parent, key);
1435
- if (inheritedDescriptor && !("value" in inheritedDescriptor)) {
1436
- throw new Error(`opshot: ${formatOperationPath(path)} resolves to an inherited accessor`);
461
+ return recordOf(value)?.proxy ?? value;
462
+ },
463
+ set(target, key, value, receiver) {
464
+ if (typeof key !== "string" || writesThroughAccessor(target, key) || isRideAlongKey(target, key)) {
465
+ return Reflect.set(target, key, value, receiver);
1437
466
  }
467
+ const { hadPrevious, previous } = ownDataDescriptor(target, key);
468
+ const resolved = isObjectLike(value) ? rawOf(value) : value;
469
+ const memberships = membershipsOf(target).map(([handle, membership]) => ({
470
+ handle,
471
+ membership,
472
+ hadKey: membership.keys.has(key)
473
+ }));
474
+ const truncated = key === "length" ? truncatedOwnEntriesOf(target, resolved) : [];
475
+ const previousLength = Array.isArray(target) ? target.length : void 0;
476
+ if (typeof resolved === "function" && memberships.some(({ membership }) => !membership.exempt)) {
477
+ const kind = classifyValue(target);
478
+ if (kind !== "plain" && kind !== "plainArray") throw rejectionError(target, kind, [key]);
479
+ }
480
+ const incoming = resolved !== previous && isTrackedEntry(resolved, true) ? resolved : void 0;
481
+ let rollBack;
482
+ if (incoming !== void 0) {
483
+ const started = new Array();
484
+ rollBack = () => {
485
+ for (const { handle, membership, hadKey } of started) {
486
+ detach(handle, incoming);
487
+ if (!hadKey) membership.keys.delete(key);
488
+ }
489
+ };
490
+ try {
491
+ for (const write of memberships) {
492
+ started.push(write);
493
+ attach(write.handle, target, key, incoming, [key]);
494
+ }
495
+ } catch (error) {
496
+ rollBack();
497
+ throw error;
498
+ }
499
+ }
500
+ const result = Reflect.set(target, key, resolved, receiver);
501
+ if (!result) {
502
+ rollBack?.();
503
+ return result;
504
+ }
505
+ const meta = currentMeta();
506
+ const node = proxyOf(target);
507
+ for (const { handle, membership, hadKey } of memberships) {
508
+ for (const entry of truncated) {
509
+ if (membership.keys.delete(entry.key) && isObjectLike(entry.value)) detach(handle, entry.value);
510
+ recordOperation(handle, target, {
511
+ node,
512
+ key: entry.key,
513
+ meta,
514
+ before: proxied(entry.value),
515
+ hasBefore: true,
516
+ hasAfter: false
517
+ });
518
+ }
519
+ if (hadKey && previous !== resolved) {
520
+ if (isObjectLike(previous)) detach(handle, previous);
521
+ if (incoming === void 0) membership.keys.delete(key);
522
+ }
523
+ recordOperation(handle, target, {
524
+ node,
525
+ key,
526
+ meta,
527
+ before: proxied(previous),
528
+ after: proxied(resolved),
529
+ hasBefore: hadPrevious,
530
+ hasAfter: true
531
+ });
532
+ if (key !== "length" && Array.isArray(target) && previousLength !== target.length) {
533
+ recordOperation(handle, target, {
534
+ node,
535
+ key: "length",
536
+ meta,
537
+ before: previousLength,
538
+ after: target.length,
539
+ hasBefore: true,
540
+ hasAfter: true
541
+ });
542
+ }
543
+ }
544
+ return result;
545
+ },
546
+ deleteProperty(target, key) {
547
+ if (typeof key !== "string" || isRideAlongKey(target, key)) return Reflect.deleteProperty(target, key);
548
+ const { hadPrevious, previous } = ownDataDescriptor(target, key);
549
+ const result = Reflect.deleteProperty(target, key);
550
+ if (!result || !hadPrevious) return result;
551
+ const meta = currentMeta();
552
+ const node = proxyOf(target);
553
+ for (const [handle, membership] of membershipsOf(target)) {
554
+ if (membership.keys.delete(key) && isObjectLike(previous)) detach(handle, previous);
555
+ recordOperation(handle, target, {
556
+ node,
557
+ key,
558
+ meta,
559
+ before: proxied(previous),
560
+ hasBefore: true,
561
+ hasAfter: false
562
+ });
563
+ }
564
+ return result;
565
+ },
566
+ preventExtensions(target) {
567
+ for (const [handle] of membershipsOf(target)) evict(handle, target);
568
+ return Reflect.preventExtensions(target);
569
+ },
570
+ defineProperty(target, key, descriptor) {
571
+ return Reflect.defineProperty(target, key, descriptor);
572
+ },
573
+ setPrototypeOf(target, prototype) {
574
+ return Reflect.setPrototypeOf(target, prototype);
575
+ },
576
+ has(target, key) {
577
+ return Reflect.has(target, key);
578
+ },
579
+ ownKeys(target) {
580
+ return Reflect.ownKeys(target);
581
+ },
582
+ getOwnPropertyDescriptor(target, key) {
583
+ return Reflect.getOwnPropertyDescriptor(target, key);
1438
584
  }
1439
- if (operation.op === "remove") {
1440
- deleteOrThrow(parent, key);
1441
- return;
1442
- }
1443
- if (!("value" in operation)) throw unresolvedError(path);
1444
- restoreValue(
1445
- getValuePayload(operation),
1446
- (value) => setOrThrow(parent, key, value),
1447
- () => Reflect.get(parent, key)
1448
- );
1449
585
  };
1450
- function applyOperations(root, operations) {
1451
- for (const operation of operations) {
1452
- const terminal = resolveTerminal(root, operation.path);
1453
- applyPlain(terminal.parent, terminal.segment, operation);
1454
- }
1455
- }
1456
- function applyOps(state, operations, meta) {
1457
- for (const operation of operations) assertApplicable(operation);
1458
- transact(
1459
- state,
1460
- () => {
1461
- applyOperations(resolveEmitterTarget(state), operations);
1462
- },
1463
- meta
1464
- );
1465
- }
1466
586
 
1467
- // src/subscribe.ts
1468
- function subscribe2(target, listener) {
1469
- if (isGroup(target)) {
1470
- return addGroupListener(getGroupListeners(target), listener, void 0, (state, ops, meta) => {
1471
- listener(state, ops, unwrapTransportMeta(meta));
1472
- });
1473
- }
1474
- return addStateListener(target, listener, void 0, (ops, meta) => {
1475
- listener(ops, unwrapTransportMeta(meta));
1476
- });
1477
- }
1478
- var channelStampBrand = /* @__PURE__ */ Symbol.for("opshot.channelStamp");
1479
- function stampChannelMeta(channelId, meta) {
1480
- return { [channelStampBrand]: channelId, meta };
1481
- }
1482
- function isChannelStamp(value) {
1483
- return typeof value === "object" && value !== null && channelStampBrand in value;
1484
- }
1485
- function isOwnChannelStamp(value, channelId) {
1486
- return isChannelStamp(value) && value[channelStampBrand] === channelId;
1487
- }
1488
- function unwrapTransportMeta(meta) {
1489
- if (isChannelStamp(meta)) return meta.meta;
1490
- return meta;
1491
- }
1492
- function toChannelContext(channelId, defaults, meta) {
1493
- if (isOwnChannelStamp(meta, channelId)) {
1494
- return { isTransaction: true, meta: { ...defaults, ...meta.meta } };
1495
- }
1496
- return { isTransaction: false, meta: unwrapTransportMeta(meta) };
587
+ // src/createMutableState.ts
588
+ installProxyHandler(handler);
589
+ function createMutableState(properties, options) {
590
+ const incoming = properties;
591
+ if (typeof incoming !== "object" || incoming === null) return properties;
592
+ if (isIgnored(incoming)) return properties;
593
+ if (Object.isFrozen(incoming)) return properties;
594
+ const root = rawOf(incoming);
595
+ if (handleOf(incoming) !== void 0) return proxyOf(incoming);
596
+ const strict = options?.strict !== false;
597
+ const exempt = !strict || isUnsafeMarked(root);
598
+ const handle = {
599
+ root,
600
+ strict,
601
+ emitOn: options?.emitOn,
602
+ subscribers: /* @__PURE__ */ new Map(),
603
+ pending: [],
604
+ pendingIndex: /* @__PURE__ */ new Map(),
605
+ isFlushScheduled: false
606
+ };
607
+ const proxy = proxyOf(root);
608
+ attachRoot(handle, root, exempt);
609
+ return proxy;
1497
610
  }
1498
611
 
1499
- // src/createChannel.ts
1500
- function createChannel(defaults) {
1501
- const channelId = Object.freeze({});
1502
- function transact2(state, mutate, meta) {
1503
- transact(state, mutate, stampChannelMeta(channelId, meta));
1504
- }
1505
- function subscribe3(target, listener) {
1506
- if (isGroup(target)) {
1507
- return addGroupListener(getGroupListeners(target), listener, channelId, (state, ops, meta) => {
1508
- listener(
1509
- state,
1510
- ops,
1511
- toChannelContext(channelId, defaults, meta)
1512
- );
1513
- });
612
+ // src/identity.ts
613
+ var readProxyTargets = /* @__PURE__ */ new WeakMap();
614
+ var identityRecords = /* @__PURE__ */ new WeakMap();
615
+ var nextInternId = 0;
616
+ var registerReadProxyTarget = (readProxy, target) => {
617
+ readProxyTargets.set(readProxy, target);
618
+ };
619
+ var getRegisteredReadProxyTarget = (readProxy) => readProxyTargets.get(readProxy);
620
+ function resolveIdentity(value) {
621
+ let current = value;
622
+ while (isObjectLike(current)) {
623
+ const readTarget = getRegisteredReadProxyTarget(current);
624
+ if (readTarget !== void 0 && readTarget !== current) {
625
+ current = readTarget;
626
+ continue;
1514
627
  }
1515
- return addStateListener(target, listener, channelId, (ops, meta) => {
1516
- listener(
1517
- ops,
1518
- toChannelContext(channelId, defaults, meta)
1519
- );
1520
- });
628
+ const raw = rawOf(current);
629
+ if (raw !== current) {
630
+ current = raw;
631
+ continue;
632
+ }
633
+ break;
1521
634
  }
1522
- function applyOps2(state, operations, meta) {
1523
- applyOps(state, operations, stampChannelMeta(channelId, meta));
635
+ return current;
636
+ }
637
+ var recordFor = (key) => {
638
+ let record = identityRecords.get(key);
639
+ if (record === void 0) {
640
+ record = {};
641
+ identityRecords.set(key, record);
1524
642
  }
1525
- return { transact: transact2, subscribe: subscribe3, applyOps: applyOps2 };
643
+ return record;
644
+ };
645
+ function identify(value) {
646
+ const target = resolveIdentity(value);
647
+ if (!isObjectLike(target)) return value;
648
+ const record = recordFor(target);
649
+ if (record.token !== void 0) return record.token;
650
+ const token = Object.freeze({});
651
+ record.token = token;
652
+ return token;
653
+ }
654
+ var internIdentity = (key) => {
655
+ const resolved = resolveIdentity(key);
656
+ const record = recordFor(resolved);
657
+ if (record.id !== void 0) return record.id;
658
+ const id = nextInternId;
659
+ nextInternId += 1;
660
+ record.id = id;
661
+ return id;
662
+ };
663
+ function isSameIdentity(first, second) {
664
+ const resolvedFirst = resolveIdentity(first);
665
+ const resolvedSecond = resolveIdentity(second);
666
+ return resolvedFirst === resolvedSecond;
1526
667
  }
1527
668
 
1528
- // src/react/resolveWrapper.ts
1529
- var { proxyStateMap: proxyStateMap5 } = unstable_getInternalStates();
1530
- var isObjectLike5 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
1531
- function unwrapWrapper(value) {
1532
- if (!isObjectLike5(value) || proxyStateMap5.has(value)) return value;
669
+ // src/peelReadProxy.ts
670
+ function peelReadProxy(value) {
671
+ if (!isObjectLike(value)) return value;
1533
672
  let current = value;
1534
- while (isObjectLike5(current)) {
1535
- const registeredTarget = getRegisteredWrapperTarget(current);
673
+ while (isObjectLike(current)) {
674
+ const registeredTarget = getRegisteredReadProxyTarget(current);
1536
675
  if (registeredTarget === void 0) break;
1537
676
  current = registeredTarget;
1538
677
  }
@@ -1540,32 +679,32 @@ function unwrapWrapper(value) {
1540
679
  }
1541
680
 
1542
681
  // src/isState.ts
1543
- var { proxyStateMap: proxyStateMap6 } = unstable_getInternalStates();
1544
682
  function isState(value) {
1545
- const resolved = unwrapWrapper(value);
683
+ const resolved = peelReadProxy(value);
1546
684
  if (typeof resolved !== "object" || resolved === null) return false;
1547
- return proxyStateMap6.has(resolved);
685
+ return recordOf(resolved)?.proxy === resolved;
1548
686
  }
1549
687
 
1550
- // src/ignore.ts
1551
- var ignore = ref;
1552
-
1553
688
  // src/tracked/facadeGuard.ts
1554
689
  var assertMutableFacade = (facade, mutationKey) => {
1555
- const facadeSource = getUntracked(facade);
1556
- const isRegisteredCopy = getRegisteredTarget(facade) !== void 0 || facadeSource !== null && getRegisteredTarget(facadeSource) !== void 0;
1557
690
  const descriptor = Reflect.getOwnPropertyDescriptor(facade, mutationKey);
1558
- if (isRegisteredCopy || descriptor !== void 0 && "writable" in descriptor && !descriptor.writable) {
1559
- throw new Error("opshot: cannot mutate a tracked collection snapshot");
691
+ if (descriptor !== void 0 && "writable" in descriptor && !descriptor.writable) {
692
+ throw new Error("opshot: cannot mutate a non-writable tracked collection");
1560
693
  }
1561
694
  };
1562
695
 
1563
696
  // src/tracked/trackedDate.ts
697
+ var DateSetYearError = class extends Error {
698
+ constructor(message) {
699
+ super(message);
700
+ this.name = "DateSetYearError";
701
+ }
702
+ };
1564
703
  var setLegacyYear = (date, year) => {
1565
704
  const setYear = Reflect.get(date, "setYear");
1566
- if (typeof setYear !== "function") throw new Error("opshot: Date.setYear is not available");
705
+ if (typeof setYear !== "function") throw new DateSetYearError("opshot: Date.setYear is not available");
1567
706
  const epochMs = Reflect.apply(setYear, date, [year]);
1568
- if (typeof epochMs !== "number") throw new Error("opshot: Date.setYear returned a non-number");
707
+ if (typeof epochMs !== "number") throw new DateSetYearError("opshot: Date.setYear returned a non-number");
1569
708
  return epochMs;
1570
709
  };
1571
710
  var constructDate = (args) => {
@@ -1588,14 +727,21 @@ var constructDate = (args) => {
1588
727
  return new Date(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
1589
728
  }
1590
729
  };
730
+ var MAX_TIME = 864e13;
731
+ var clipTime = (epochMs) => {
732
+ if (!Number.isFinite(epochMs) || Math.abs(epochMs) > MAX_TIME) return Number.NaN;
733
+ return Math.trunc(epochMs) + 0;
734
+ };
1591
735
  var TrackedDate = class {
1592
736
  constructor(...args) {
1593
- installBoundary();
1594
737
  this.epochMs = constructDate(args).getTime();
1595
738
  }
1596
739
  readDate() {
1597
740
  return new Date(this.epochMs);
1598
741
  }
742
+ readEpochMs() {
743
+ return clipTime(this.epochMs);
744
+ }
1599
745
  write(mutate) {
1600
746
  assertMutableFacade(this, "epochMs");
1601
747
  const epochMs = mutate(this.readDate());
@@ -1621,10 +767,10 @@ var TrackedDate = class {
1621
767
  return this.readDate().toLocaleTimeString(locales, options);
1622
768
  }
1623
769
  valueOf() {
1624
- return this.readDate().valueOf();
770
+ return this.readEpochMs();
1625
771
  }
1626
772
  getTime() {
1627
- return this.readDate().getTime();
773
+ return this.readEpochMs();
1628
774
  }
1629
775
  getFullYear() {
1630
776
  return this.readDate().getFullYear();
@@ -1738,8 +884,8 @@ var TrackedDate = class {
1738
884
  return this.readDate().toISOString();
1739
885
  }
1740
886
  [Symbol.toPrimitive](hint) {
1741
- const date = this.readDate();
1742
- return hint === "number" ? date[Symbol.toPrimitive]("number") : date[Symbol.toPrimitive](hint);
887
+ if (hint === "number") return this.readEpochMs();
888
+ return this.readDate()[Symbol.toPrimitive](hint);
1743
889
  }
1744
890
  };
1745
891
  Object.defineProperty(TrackedDate.prototype, Symbol.toStringTag, {
@@ -1750,19 +896,11 @@ Object.defineProperty(TrackedDate.prototype, Symbol.toStringTag, {
1750
896
  });
1751
897
 
1752
898
  // src/tracked/address.ts
1753
- var internTable = /* @__PURE__ */ new WeakMap();
1754
- var nextInternId = 0;
1755
- var internIdentity = (key) => {
1756
- const resolved = resolveIdentity(key);
1757
- if (resolved === null || typeof resolved !== "object" && typeof resolved !== "function" && typeof resolved !== "symbol") {
1758
- throw new Error("opshot: addressOf interned a non-identity value");
899
+ var UnsupportedKeyTypeError = class extends Error {
900
+ constructor(key) {
901
+ super(`opshot: addressOf received unsupported key type ${typeof key}`);
902
+ this.name = "UnsupportedKeyTypeError";
1759
903
  }
1760
- const existing = internTable.get(resolved);
1761
- if (existing !== void 0) return existing;
1762
- const id = nextInternId;
1763
- nextInternId += 1;
1764
- internTable.set(resolved, id);
1765
- return id;
1766
904
  };
1767
905
  var addressOf = (key) => {
1768
906
  if (key === null) return "z";
@@ -1785,8 +923,57 @@ var addressOf = (key) => {
1785
923
  case "function":
1786
924
  return `o${internIdentity(key)}`;
1787
925
  default:
1788
- throw new Error(`opshot: addressOf received unsupported key type ${typeof key}`);
926
+ throw new UnsupportedKeyTypeError(key);
927
+ }
928
+ };
929
+
930
+ // src/tracked/slotStore.ts
931
+ var swapChain = /* @__PURE__ */ new WeakMap();
932
+ var compactStore = (store, addressOfEntry) => {
933
+ const retired = store.slots;
934
+ const slots = new Array();
935
+ const index = {};
936
+ for (const entry of retired) {
937
+ if (entry === null || entry === void 0) continue;
938
+ index[addressOfEntry(entry)] = slots.length;
939
+ slots.push(entry);
940
+ }
941
+ store.slots = slots;
942
+ store.index = index;
943
+ swapChain.set(retired, store.slots);
944
+ };
945
+ var translateCursor = (retired, cursor, current) => {
946
+ let array = retired;
947
+ let position = cursor;
948
+ while (array !== current) {
949
+ const successor = swapChain.get(array);
950
+ if (successor === void 0) return 0;
951
+ let survivors = 0;
952
+ const bound = Math.min(position, array.length);
953
+ for (let slot = 0; slot < bound; slot += 1) {
954
+ const entry = array[slot];
955
+ if (entry !== null && entry !== void 0) survivors += 1;
956
+ }
957
+ position = survivors;
958
+ array = successor;
959
+ }
960
+ return position;
961
+ };
962
+ var deleteFromStore = (store, addr, addressOfEntry) => {
963
+ const slot = store.index[addr];
964
+ if (slot === void 0) return false;
965
+ store.slots[slot] = null;
966
+ Reflect.deleteProperty(store.index, addr);
967
+ store.count -= 1;
968
+ if (store.slots.length >= 2 * store.count) {
969
+ compactStore(store, addressOfEntry);
1789
970
  }
971
+ return true;
972
+ };
973
+ var clearStore = (store) => {
974
+ store.slots = [];
975
+ store.index = {};
976
+ store.count = 0;
1790
977
  };
1791
978
 
1792
979
  // src/tracked/iterateSlots.ts
@@ -1796,8 +983,8 @@ function* iterateSlots(getSlots) {
1796
983
  for (; ; ) {
1797
984
  const current = getSlots();
1798
985
  if (current !== slots) {
986
+ index = translateCursor(slots, index, current);
1799
987
  slots = current;
1800
- index = 0;
1801
988
  }
1802
989
  if (index >= slots.length) return;
1803
990
  const entry = slots[index];
@@ -1806,25 +993,20 @@ function* iterateSlots(getSlots) {
1806
993
  }
1807
994
  }
1808
995
 
1809
- // src/tracked/slotStore.ts
1810
- var deleteFromStore = (store, addr) => {
1811
- const slot = store.index[addr];
1812
- if (slot === void 0) return false;
1813
- store.slots[slot] = null;
1814
- Reflect.deleteProperty(store.index, addr);
1815
- store.count -= 1;
1816
- return true;
996
+ // src/tracked/trackedMap.ts
997
+ var EmptyMapSlotError = class extends Error {
998
+ constructor() {
999
+ super("opshot: TrackedMap resolved an empty slot");
1000
+ this.name = "EmptyMapSlotError";
1001
+ }
1817
1002
  };
1818
- var clearStore = (store) => {
1819
- store.slots = [];
1820
- store.index = {};
1821
- store.count = 0;
1003
+ var isObjectLike2 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
1004
+ var isStoredValue = (stored, incoming) => {
1005
+ if (Object.is(stored, incoming)) return true;
1006
+ return isObjectLike2(stored) && isObjectLike2(incoming) && isSameIdentity(stored, incoming);
1822
1007
  };
1823
-
1824
- // src/tracked/trackedMap.ts
1825
1008
  var TrackedMap = class {
1826
1009
  constructor(entries) {
1827
- installBoundary();
1828
1010
  this.slots = [];
1829
1011
  this.index = {};
1830
1012
  this.count = 0;
@@ -1844,23 +1026,29 @@ var TrackedMap = class {
1844
1026
  }
1845
1027
  set(key, value) {
1846
1028
  assertMutableFacade(this, "count");
1847
- const addr = addressOf(key);
1029
+ const stored = Object.is(key, -0) ? 0 : key;
1030
+ const addr = addressOf(stored);
1848
1031
  const slot = this.index[addr];
1849
1032
  if (slot === void 0) {
1850
1033
  const newSlot = this.slots.length;
1851
- this.slots.push([key, value]);
1034
+ this.slots.push([stored, value]);
1852
1035
  this.index[addr] = newSlot;
1853
1036
  this.count += 1;
1854
1037
  } else {
1855
1038
  const pair = this.slots[slot];
1856
- if (pair === null || pair === void 0) throw new Error("opshot: TrackedMap resolved an empty slot");
1039
+ if (pair === null || pair === void 0) throw new EmptyMapSlotError();
1040
+ if (isStoredValue(pair[1], value)) return this;
1857
1041
  this.slots[slot] = [pair[0], value];
1858
1042
  }
1859
1043
  return this;
1860
1044
  }
1861
1045
  delete(key) {
1862
1046
  assertMutableFacade(this, "count");
1863
- return deleteFromStore(this, addressOf(key));
1047
+ return deleteFromStore(
1048
+ this,
1049
+ addressOf(key),
1050
+ (pair) => addressOf(pair[0])
1051
+ );
1864
1052
  }
1865
1053
  clear() {
1866
1054
  assertMutableFacade(this, "count");
@@ -1901,7 +1089,6 @@ Object.defineProperty(TrackedMap.prototype, Symbol.toStringTag, {
1901
1089
  // src/tracked/trackedSet.ts
1902
1090
  var TrackedSet = class {
1903
1091
  constructor(values) {
1904
- installBoundary();
1905
1092
  this.slots = [];
1906
1093
  this.index = {};
1907
1094
  this.count = 0;
@@ -1915,17 +1102,22 @@ var TrackedSet = class {
1915
1102
  }
1916
1103
  add(value) {
1917
1104
  assertMutableFacade(this, "count");
1918
- const addr = addressOf(value);
1105
+ const stored = Object.is(value, -0) ? 0 : value;
1106
+ const addr = addressOf(stored);
1919
1107
  if (this.index[addr] !== void 0) return this;
1920
1108
  const slot = this.slots.length;
1921
- this.slots.push([value]);
1109
+ this.slots.push([stored]);
1922
1110
  this.index[addr] = slot;
1923
1111
  this.count += 1;
1924
1112
  return this;
1925
1113
  }
1926
1114
  delete(value) {
1927
1115
  assertMutableFacade(this, "count");
1928
- return deleteFromStore(this, addressOf(value));
1116
+ return deleteFromStore(
1117
+ this,
1118
+ addressOf(value),
1119
+ (member) => addressOf(member[0])
1120
+ );
1929
1121
  }
1930
1122
  clear() {
1931
1123
  assertMutableFacade(this, "count");
@@ -1960,15 +1152,192 @@ Object.defineProperty(TrackedSet.prototype, Symbol.toStringTag, {
1960
1152
  writable: false
1961
1153
  });
1962
1154
 
1963
- // src/react/boundary.ts
1964
- var { refSet: refSet6, proxyStateMap: proxyStateMap7 } = unstable_getInternalStates();
1965
- var KEYS_PROPERTY2 = "k";
1966
- var HAS_KEY_PROPERTY2 = "h";
1967
- var HAS_OWN_KEY_PROPERTY2 = "o";
1968
- var ALL_OWN_KEYS_PROPERTY2 = "w";
1969
- var isObjectLike6 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
1970
- var isLiveProxy = (value) => proxyStateMap7.has(value);
1971
- var getProxyTarget = (liveProxy) => proxyStateMap7.get(liveProxy)?.[0] ?? liveProxy;
1155
+ // src/emit/emitterListeners.ts
1156
+ var bindings = /* @__PURE__ */ new WeakMap();
1157
+ function addStateListener(state, listener, deliver) {
1158
+ const handle = requireHandle(state, "opshot: subscribe requires a state");
1159
+ handle.subscribers.set(listener, deliver);
1160
+ const unsubscribe = () => {
1161
+ const held = bindings.get(unsubscribe);
1162
+ if (held === void 0) return;
1163
+ held.handle.subscribers.delete(held.listener);
1164
+ bindings.delete(unsubscribe);
1165
+ };
1166
+ bindings.set(unsubscribe, { handle, listener });
1167
+ return unsubscribe;
1168
+ }
1169
+
1170
+ // src/subscribe.ts
1171
+ function subscribe(state, listener) {
1172
+ return addStateListener(state, listener, listener);
1173
+ }
1174
+
1175
+ // src/react/propWalk.ts
1176
+ var stateKeysByContainer = /* @__PURE__ */ new WeakMap();
1177
+ var noStateKeys = /* @__PURE__ */ new Set();
1178
+ var isReactOwnNode = (value) => "$$typeof" in value || typeof Node !== "undefined" && value instanceof Node;
1179
+ var canRebuild = (container) => !isDangerousKind(classifyValue(container));
1180
+ var childRole = (value, writable, mode) => {
1181
+ if (typeof value !== "object" || value === null) return "skip";
1182
+ if (isReactOwnNode(value)) return "skip";
1183
+ if (!isTrackedEntry(value, mode === "entry" || writable)) return "skip";
1184
+ if (isState(value)) return "state";
1185
+ if (canRebuild(value)) return "descend";
1186
+ return "skip";
1187
+ };
1188
+ var readVerdict = (container, pass) => pass.verdicts.get(container) ?? stateKeysByContainer.get(container) ?? noStateKeys;
1189
+ var createDiscoveryPass = () => ({
1190
+ entriesByContainer: /* @__PURE__ */ new Map(),
1191
+ verdicts: /* @__PURE__ */ new Map(),
1192
+ inProgress: /* @__PURE__ */ new Set(),
1193
+ relaxable: /* @__PURE__ */ new Set()
1194
+ });
1195
+ function visitContainer(container, pass, mode) {
1196
+ if (mode === "nested" && stateKeysByContainer.has(container)) return false;
1197
+ if (pass.inProgress.has(container)) return true;
1198
+ if (pass.verdicts.has(container)) return pass.relaxable.has(container);
1199
+ pass.inProgress.add(container);
1200
+ const entries = walkDataEntries(container);
1201
+ const keys = /* @__PURE__ */ new Set();
1202
+ let dependedOnBackEdge = false;
1203
+ pass.entriesByContainer.set(container, entries);
1204
+ for (const entry of entries) {
1205
+ const role = childRole(entry.value, entry.writable, mode);
1206
+ if (role === "state") {
1207
+ keys.add(entry.key);
1208
+ continue;
1209
+ }
1210
+ if (role !== "descend") continue;
1211
+ const child = entry.value;
1212
+ if (typeof child !== "object" || child === null) continue;
1213
+ if (visitContainer(child, pass, "nested")) dependedOnBackEdge = true;
1214
+ if (readVerdict(child, pass).size > 0) keys.add(entry.key);
1215
+ }
1216
+ pass.inProgress.delete(container);
1217
+ pass.verdicts.set(container, keys);
1218
+ if (dependedOnBackEdge) pass.relaxable.add(container);
1219
+ return dependedOnBackEdge;
1220
+ }
1221
+ function relaxVerdicts(pass, entryContainer) {
1222
+ let gained = true;
1223
+ while (gained) {
1224
+ gained = false;
1225
+ for (const container of pass.relaxable) {
1226
+ const keys = pass.verdicts.get(container);
1227
+ const entries = pass.entriesByContainer.get(container);
1228
+ if (keys === void 0 || entries === void 0) continue;
1229
+ for (const entry of entries) {
1230
+ if (keys.has(entry.key)) continue;
1231
+ if (childRole(entry.value, entry.writable, container === entryContainer ? "entry" : "nested") !== "descend")
1232
+ continue;
1233
+ const child = entry.value;
1234
+ if (typeof child !== "object" || child === null) continue;
1235
+ if (readVerdict(child, pass).size === 0) continue;
1236
+ keys.add(entry.key);
1237
+ gained = true;
1238
+ }
1239
+ }
1240
+ }
1241
+ }
1242
+ var cacheNestedVerdicts = (pass, skip) => {
1243
+ for (const [visited, keys] of pass.verdicts) {
1244
+ if (visited === skip) continue;
1245
+ stateKeysByContainer.set(visited, keys);
1246
+ }
1247
+ };
1248
+ function discoverStateKeys(container) {
1249
+ const cached = stateKeysByContainer.get(container);
1250
+ if (cached !== void 0) return cached;
1251
+ if (isReactOwnNode(container)) return noStateKeys;
1252
+ if (Object.isFrozen(container) || !canRebuild(container)) return noStateKeys;
1253
+ const pass = createDiscoveryPass();
1254
+ visitContainer(container, pass, "nested");
1255
+ relaxVerdicts(pass);
1256
+ cacheNestedVerdicts(pass);
1257
+ return pass.verdicts.get(container) ?? noStateKeys;
1258
+ }
1259
+ function substituteContainer(container, substitution, wrap, mode) {
1260
+ const rebuilt = substitution.rebuiltByContainer.get(container);
1261
+ if (rebuilt !== void 0) return rebuilt;
1262
+ let stateKeys;
1263
+ if (mode === "nested") {
1264
+ stateKeys = discoverStateKeys(container);
1265
+ } else {
1266
+ const pass = createDiscoveryPass();
1267
+ visitContainer(container, pass, "entry");
1268
+ relaxVerdicts(pass, container);
1269
+ cacheNestedVerdicts(pass, container);
1270
+ stateKeys = pass.verdicts.get(container) ?? noStateKeys;
1271
+ }
1272
+ if (stateKeys.size === 0) {
1273
+ substitution.rebuiltByContainer.set(container, container);
1274
+ return container;
1275
+ }
1276
+ const clone = Array.isArray(container) ? [] : {};
1277
+ Reflect.setPrototypeOf(clone, Reflect.getPrototypeOf(container));
1278
+ substitution.rebuiltByContainer.set(container, clone);
1279
+ const descriptors = Object.getOwnPropertyDescriptors(container);
1280
+ for (const key of stateKeys) {
1281
+ const descriptor = descriptors[key];
1282
+ if (descriptor === void 0 || !("value" in descriptor)) continue;
1283
+ const value = descriptor.value;
1284
+ const role = childRole(value, descriptor.writable === true, mode);
1285
+ if (role === "state") {
1286
+ const source = peelReadProxy(value);
1287
+ if (typeof source !== "object" || source === null) continue;
1288
+ if (!substitution.visitedSources.has(source)) {
1289
+ substitution.visitedSources.add(source);
1290
+ substitution.sources.push(source);
1291
+ }
1292
+ descriptors[key] = { ...descriptor, value: wrap(source) };
1293
+ continue;
1294
+ }
1295
+ if (role !== "descend") continue;
1296
+ if (typeof value !== "object" || value === null) continue;
1297
+ descriptors[key] = { ...descriptor, value: substituteContainer(value, substitution, wrap, "nested") };
1298
+ }
1299
+ Object.defineProperties(clone, descriptors);
1300
+ return clone;
1301
+ }
1302
+ function substituteStates(root, wrap) {
1303
+ const substitution = {
1304
+ rebuiltByContainer: /* @__PURE__ */ new Map(),
1305
+ sources: [],
1306
+ visitedSources: /* @__PURE__ */ new Set()
1307
+ };
1308
+ if (isReactOwnNode(root) || !canRebuild(root)) {
1309
+ return { props: root, sources: substitution.sources };
1310
+ }
1311
+ return { props: substituteContainer(root, substitution, wrap, "entry"), sources: substitution.sources };
1312
+ }
1313
+ var NO_SLOT = /* @__PURE__ */ Symbol("opshot.noDispatcherSlot");
1314
+ var readDispatcher = () => {
1315
+ const internals = React;
1316
+ const modern = internals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
1317
+ if (modern !== void 0) return modern.H;
1318
+ const legacy = internals.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?.ReactCurrentDispatcher;
1319
+ if (legacy !== void 0) return legacy.current;
1320
+ return NO_SLOT;
1321
+ };
1322
+ var nonRenderDispatcher;
1323
+ var learnNonRenderDispatcher = () => {
1324
+ const current = readDispatcher();
1325
+ if (current === NO_SLOT || current === null || current === void 0) return;
1326
+ nonRenderDispatcher = current;
1327
+ };
1328
+ var isRendering = () => {
1329
+ if (nonRenderDispatcher === void 0) return false;
1330
+ const current = readDispatcher();
1331
+ if (current === NO_SLOT || current === null || current === void 0) return false;
1332
+ return current !== nonRenderDispatcher;
1333
+ };
1334
+
1335
+ // src/react/readTracker.ts
1336
+ var KEYS_PROPERTY = "k";
1337
+ var HAS_KEY_PROPERTY = "h";
1338
+ var HAS_OWN_KEY_PROPERTY = "o";
1339
+ var ALL_OWN_KEYS_PROPERTY = "w";
1340
+ var isWriteProxy = (value) => recordOf(value)?.proxy === value;
1972
1341
  var getUsage = (affected, target) => {
1973
1342
  let used = affected.get(target);
1974
1343
  if (used === void 0) {
@@ -1977,13 +1346,19 @@ var getUsage = (affected, target) => {
1977
1346
  }
1978
1347
  return used;
1979
1348
  };
1980
- var recordKey = (used, type, key) => {
1981
- let set = used[type];
1982
- if (set === void 0) {
1983
- set = /* @__PURE__ */ new Set();
1984
- used[type] = set;
1985
- }
1986
- set.add(key);
1349
+ var recordIn = (slot, key, value) => {
1350
+ const map = slot ?? /* @__PURE__ */ new Map();
1351
+ if (!map.has(key)) map.set(key, value);
1352
+ return map;
1353
+ };
1354
+ var recordGet = (used, key, value) => {
1355
+ used[KEYS_PROPERTY] = recordIn(used[KEYS_PROPERTY], key, value);
1356
+ };
1357
+ var recordHas = (used, key, value) => {
1358
+ used[HAS_KEY_PROPERTY] = recordIn(used[HAS_KEY_PROPERTY], key, value);
1359
+ };
1360
+ var recordOwn = (used, key, value) => {
1361
+ used[HAS_OWN_KEY_PROPERTY] = recordIn(used[HAS_OWN_KEY_PROPERTY], key, value);
1987
1362
  };
1988
1363
  var getPrototypeMethod = (target, prop) => {
1989
1364
  let prototype = Reflect.getPrototypeOf(target);
@@ -1997,260 +1372,215 @@ var getPrototypeMethod = (target, prop) => {
1997
1372
  }
1998
1373
  return void 0;
1999
1374
  };
2000
- var wrapperBoundMethods = /* @__PURE__ */ new WeakMap();
2001
- var bindMethodToWrapper = (wrapper, method) => {
2002
- let methods = wrapperBoundMethods.get(wrapper);
2003
- if (methods === void 0) {
2004
- methods = /* @__PURE__ */ new WeakMap();
2005
- wrapperBoundMethods.set(wrapper, methods);
2006
- }
2007
- const existing = methods.get(method);
2008
- if (existing !== void 0) return existing;
2009
- const bound = Function.prototype.bind.call(method, wrapper);
2010
- methods.set(method, bound);
2011
- return bound;
2012
- };
2013
- function createBoundary() {
1375
+ var UnregisteredReadTrackerError = class extends Error {
1376
+ constructor() {
1377
+ super("opshot: readsIntersectDirty received an unregistered tracker");
1378
+ this.name = "UnregisteredReadTrackerError";
1379
+ }
1380
+ };
1381
+ var trackerPartitions = /* @__PURE__ */ new WeakMap();
1382
+ var partitionsOf = (tracker) => {
1383
+ const partitions = trackerPartitions.get(tracker);
1384
+ if (partitions === void 0) throw new UnregisteredReadTrackerError();
1385
+ return partitions;
1386
+ };
1387
+ var recordedKeysOf = (used) => {
1388
+ const keyMaps = new Array();
1389
+ if (used[KEYS_PROPERTY] !== void 0) keyMaps.push(used[KEYS_PROPERTY]);
1390
+ if (used[HAS_KEY_PROPERTY] !== void 0) keyMaps.push(used[HAS_KEY_PROPERTY]);
1391
+ if (used[HAS_OWN_KEY_PROPERTY] !== void 0) keyMaps.push(used[HAS_OWN_KEY_PROPERTY]);
1392
+ return keyMaps;
1393
+ };
1394
+ function readsIntersectDirty(tracker, dirty) {
1395
+ for (const partition of partitionsOf(tracker).values()) {
1396
+ for (const [writeProxy, used] of partition.affected) {
1397
+ const raw = rawOf(writeProxy);
1398
+ const edges = dirty.edges.get(raw);
1399
+ for (const keys of recordedKeysOf(used)) {
1400
+ for (const key of keys.keys()) {
1401
+ if (typeof key === "symbol") continue;
1402
+ if (edges?.has(key) === true) return true;
1403
+ }
1404
+ }
1405
+ if (used[ALL_OWN_KEYS_PROPERTY] !== void 0 && dirty.nodes.has(raw)) return true;
1406
+ }
1407
+ for (const writeProxy of partition.identityReads) {
1408
+ if (partition.affected.has(writeProxy)) continue;
1409
+ if (dirty.nodes.has(rawOf(writeProxy))) return true;
1410
+ }
1411
+ }
1412
+ return false;
1413
+ }
1414
+ function readsChanged(tracker) {
1415
+ for (const partition of partitionsOf(tracker).values()) {
1416
+ for (const [writeProxy, used] of partition.affected) {
1417
+ const gets = used[KEYS_PROPERTY];
1418
+ if (gets !== void 0) {
1419
+ for (const [key, stored] of gets) {
1420
+ if (!Object.is(Reflect.get(writeProxy, key, writeProxy), stored)) return true;
1421
+ }
1422
+ }
1423
+ const hasKeys = used[HAS_KEY_PROPERTY];
1424
+ if (hasKeys !== void 0) {
1425
+ for (const [key, stored] of hasKeys) {
1426
+ if (!Object.is(Reflect.has(writeProxy, key), stored)) return true;
1427
+ }
1428
+ }
1429
+ const ownKeys = used[HAS_OWN_KEY_PROPERTY];
1430
+ if (ownKeys !== void 0) {
1431
+ for (const [key, stored] of ownKeys) {
1432
+ const present = Reflect.getOwnPropertyDescriptor(writeProxy, key) !== void 0;
1433
+ if (!Object.is(present, stored)) return true;
1434
+ }
1435
+ }
1436
+ const listed = used[ALL_OWN_KEYS_PROPERTY];
1437
+ if (listed !== void 0) {
1438
+ const current = Reflect.ownKeys(writeProxy);
1439
+ if (current.length !== listed.length) return true;
1440
+ for (let index = 0; index < listed.length; index += 1) {
1441
+ if (!Object.is(current[index], listed[index])) return true;
1442
+ }
1443
+ }
1444
+ }
1445
+ }
1446
+ return false;
1447
+ }
1448
+ function createReadTracker() {
2014
1449
  const partitions = /* @__PURE__ */ new Map();
2015
- const targets = /* @__PURE__ */ new Map();
2016
1450
  let afterRender = false;
2017
- const getPartition = (sourceProxy) => {
2018
- let partition = partitions.get(sourceProxy);
1451
+ let releasing = false;
1452
+ const getPartition = (writeProxy) => {
1453
+ let partition = partitions.get(writeProxy);
2019
1454
  if (partition === void 0) {
2020
1455
  partition = {
2021
- sourceProxy,
2022
- previousRootSnapshot: void 0,
2023
1456
  affected: /* @__PURE__ */ new Map(),
2024
- baselines: /* @__PURE__ */ new Map(),
1457
+ identityReads: /* @__PURE__ */ new Set(),
2025
1458
  proxyCache: /* @__PURE__ */ new WeakMap()
2026
1459
  };
2027
- partitions.set(sourceProxy, partition);
1460
+ partitions.set(writeProxy, partition);
2028
1461
  }
2029
1462
  return partition;
2030
1463
  };
2031
- const trackUsage = (partition, liveProxy) => afterRender ? {} : getUsage(partition.affected, liveProxy);
2032
- const ensureBaseline = (partition, liveProxy) => {
2033
- if (afterRender) return snapshot(liveProxy);
2034
- const existing = partition.baselines.get(liveProxy);
2035
- if (existing !== void 0) return existing;
2036
- const baseline = snapshot(liveProxy);
2037
- partition.baselines.set(liveProxy, baseline);
2038
- return baseline;
2039
- };
2040
- const ensureRootBaseline = (partition) => {
2041
- if (afterRender) return partition.previousRootSnapshot ?? snapshot(partition.sourceProxy);
2042
- if (partition.previousRootSnapshot === void 0) {
2043
- partition.previousRootSnapshot = snapshot(partition.sourceProxy);
2044
- partition.baselines.set(partition.sourceProxy, partition.previousRootSnapshot);
2045
- }
2046
- return partition.previousRootSnapshot;
2047
- };
2048
- const registerTarget = (liveProxy) => {
2049
- if (targets.has(liveProxy)) return;
2050
- targets.set(liveProxy, {
2051
- lastIdentitySnapshot: snapshot(liveProxy)
2052
- });
1464
+ const shouldRecord = () => !afterRender || isRendering();
1465
+ const trackUsage = (partition, writeProxy) => shouldRecord() ? getUsage(partition.affected, writeProxy) : {};
1466
+ const recordIdentity = (partition, value) => {
1467
+ if (!shouldRecord()) return;
1468
+ if (isObjectLike(value) && isWriteProxy(value)) partition.identityReads.add(value);
2053
1469
  };
2054
- const wrapLive = (liveProxy, partition) => {
2055
- ensureBaseline(partition, liveProxy);
2056
- registerTarget(liveProxy);
2057
- const cached = partition.proxyCache.get(liveProxy);
1470
+ const toReadProxy = (writeProxy, partition) => {
1471
+ const raw = rawOf(writeProxy);
1472
+ const cached = partition.proxyCache.get(raw);
2058
1473
  if (cached !== void 0) return cached;
2059
- const storageTarget = getProxyTarget(liveProxy);
2060
- const wrapperBox = {};
2061
- const handler = {
1474
+ const target = raw;
1475
+ const readProxyBox = {};
1476
+ const boundMethods = /* @__PURE__ */ new WeakMap();
1477
+ const bindMethodToReadProxy = (readProxy2, method) => {
1478
+ const existing = boundMethods.get(method);
1479
+ if (existing !== void 0) return existing;
1480
+ const bound = Function.prototype.bind.call(method, readProxy2);
1481
+ boundMethods.set(method, bound);
1482
+ return bound;
1483
+ };
1484
+ const handler2 = {
2062
1485
  get(_target, prop) {
2063
- ensureRootBaseline(partition);
2064
- const value = Reflect.get(liveProxy, prop, liveProxy);
2065
- const wrapper2 = wrapperBox.current;
2066
- const used = trackUsage(partition, liveProxy);
2067
- recordKey(used, KEYS_PROPERTY2, prop);
2068
- ensureBaseline(partition, liveProxy);
1486
+ const readProxy2 = readProxyBox.current;
1487
+ const value = Reflect.get(writeProxy, prop, readProxy2 ?? writeProxy);
1488
+ const used = trackUsage(partition, writeProxy);
1489
+ recordGet(used, prop, value);
1490
+ recordIdentity(partition, value);
2069
1491
  if (typeof value === "function") {
2070
- const method = getPrototypeMethod(storageTarget, prop);
2071
- if (method !== void 0 && value === method && wrapper2 !== void 0) {
2072
- return bindMethodToWrapper(wrapper2, method);
1492
+ const method = getPrototypeMethod(target, prop);
1493
+ if (method !== void 0 && value === method && readProxy2 !== void 0) {
1494
+ return bindMethodToReadProxy(readProxy2, method);
2073
1495
  }
2074
1496
  }
2075
- if (!isObjectLike6(value)) return value;
1497
+ if (!isObjectLike(value)) return value;
2076
1498
  if (typeof value === "function") return value;
2077
- if (refSet6.has(value)) return value;
2078
- if (!isLiveProxy(value)) return value;
2079
- return wrapLive(value, partition);
1499
+ if (!isWriteProxy(value)) return value;
1500
+ return toReadProxy(value, partition);
2080
1501
  },
2081
1502
  has(_target, prop) {
2082
- const used = trackUsage(partition, liveProxy);
2083
- recordKey(used, HAS_KEY_PROPERTY2, prop);
2084
- ensureBaseline(partition, liveProxy);
2085
- return Reflect.has(liveProxy, prop);
1503
+ const used = trackUsage(partition, writeProxy);
1504
+ const result = Reflect.has(writeProxy, prop);
1505
+ recordHas(used, prop, result);
1506
+ return result;
2086
1507
  },
2087
1508
  getOwnPropertyDescriptor(_target, prop) {
2088
- const used = trackUsage(partition, liveProxy);
2089
- recordKey(used, HAS_OWN_KEY_PROPERTY2, prop);
2090
- ensureBaseline(partition, liveProxy);
2091
- return Reflect.getOwnPropertyDescriptor(liveProxy, prop);
1509
+ const used = trackUsage(partition, writeProxy);
1510
+ const descriptor = Reflect.getOwnPropertyDescriptor(writeProxy, prop);
1511
+ recordOwn(used, prop, descriptor !== void 0);
1512
+ return descriptor;
2092
1513
  },
2093
1514
  ownKeys() {
2094
- const used = trackUsage(partition, liveProxy);
2095
- used[ALL_OWN_KEYS_PROPERTY2] = true;
2096
- ensureBaseline(partition, liveProxy);
2097
- return Reflect.ownKeys(liveProxy);
1515
+ const used = trackUsage(partition, writeProxy);
1516
+ const keys = Reflect.ownKeys(writeProxy);
1517
+ used[ALL_OWN_KEYS_PROPERTY] ??= keys;
1518
+ return keys;
2098
1519
  },
2099
1520
  set(_target, prop, value) {
2100
- ensureRootBaseline(partition);
2101
- return Reflect.set(liveProxy, prop, value, liveProxy);
1521
+ return Reflect.set(writeProxy, prop, value, writeProxy);
2102
1522
  },
2103
1523
  deleteProperty(_target, prop) {
2104
- ensureRootBaseline(partition);
2105
- return Reflect.deleteProperty(liveProxy, prop);
1524
+ return Reflect.deleteProperty(writeProxy, prop);
2106
1525
  }
2107
1526
  };
2108
- const wrapper = new Proxy(/* @__PURE__ */ Object.create(null), handler);
2109
- wrapperBox.current = wrapper;
2110
- registerWrapperTarget(wrapper, liveProxy);
2111
- partition.proxyCache.set(liveProxy, wrapper);
2112
- return wrapper;
1527
+ const readProxy = new Proxy(writeProxy, handler2);
1528
+ readProxyBox.current = readProxy;
1529
+ registerReadProxyTarget(readProxy, writeProxy);
1530
+ partition.proxyCache.set(raw, readProxy);
1531
+ return readProxy;
2113
1532
  };
2114
- return {
2115
- wrap(sourceProxy) {
2116
- if (!isLiveProxy(sourceProxy)) {
2117
- throw new Error("opshot: Boundary.wrap requires a live Valtio proxy");
2118
- }
2119
- const partition = getPartition(sourceProxy);
2120
- ensureRootBaseline(partition);
2121
- return wrapLive(sourceProxy, partition);
2122
- },
2123
- readsChanged(sourceProxy) {
2124
- const partition = partitions.get(sourceProxy);
2125
- if (partition?.previousRootSnapshot === void 0) return false;
2126
- if (partition.affected.size === 0) return false;
2127
- const translated = /* @__PURE__ */ new WeakMap();
2128
- for (const [live, usage] of partition.affected) {
2129
- const baseline = partition.baselines.get(live);
2130
- if (baseline === void 0) {
2131
- throw new Error("opshot: missing baseline snapshot for affected live proxy");
2132
- }
2133
- translated.set(baseline, usage);
1533
+ const tracker = {
1534
+ wrap(writeProxy) {
1535
+ if (!isWriteProxy(writeProxy)) {
1536
+ throw new Error("opshot: ReadTracker.wrap requires a write proxy");
2134
1537
  }
2135
- const nextRoot = snapshot(sourceProxy);
2136
- return isChanged(partition.previousRootSnapshot, nextRoot, translated, /* @__PURE__ */ new WeakMap());
1538
+ const partition = getPartition(writeProxy);
1539
+ return toReadProxy(writeProxy, partition);
2137
1540
  },
2138
1541
  captureReads() {
1542
+ learnNonRenderDispatcher();
2139
1543
  afterRender = true;
2140
1544
  },
2141
- evictChangedTargets() {
2142
- for (const [liveProxy, entry] of targets) {
2143
- const current = snapshot(liveProxy);
2144
- if (current !== entry.lastIdentitySnapshot) {
2145
- for (const partition of partitions.values()) {
2146
- partition.proxyCache.delete(liveProxy);
2147
- }
2148
- }
2149
- entry.lastIdentitySnapshot = current;
2150
- }
2151
- },
2152
1545
  resetReads() {
2153
1546
  afterRender = false;
2154
1547
  for (const partition of partitions.values()) {
2155
- partition.previousRootSnapshot = void 0;
2156
1548
  partition.affected.clear();
2157
- partition.baselines.clear();
1549
+ partition.identityReads.clear();
1550
+ partition.proxyCache = /* @__PURE__ */ new WeakMap();
2158
1551
  }
2159
1552
  },
2160
- advanceBaselines() {
2161
- for (const partition of partitions.values()) {
2162
- for (const liveProxy of [...partition.baselines.keys()]) {
2163
- partition.baselines.set(liveProxy, snapshot(liveProxy));
2164
- }
2165
- partition.previousRootSnapshot = snapshot(partition.sourceProxy);
2166
- partition.baselines.set(partition.sourceProxy, partition.previousRootSnapshot);
2167
- }
1553
+ retain() {
1554
+ releasing = false;
1555
+ },
1556
+ dispose() {
1557
+ releasing = true;
1558
+ void Promise.resolve().then(() => {
1559
+ if (!releasing) return;
1560
+ releasing = false;
1561
+ partitions.clear();
1562
+ });
2168
1563
  }
2169
1564
  };
1565
+ trackerPartitions.set(tracker, partitions);
1566
+ return tracker;
2170
1567
  }
1568
+ var useCommitEffect = typeof document === "undefined" ? useEffect : useLayoutEffect;
2171
1569
 
2172
1570
  // src/react/scope.tsx
2173
- var isPlainPrototype = (value) => {
2174
- const prototype = Reflect.getPrototypeOf(value);
2175
- return prototype === Object.prototype || prototype === null;
2176
- };
2177
- var assertSubstitutableContainer = (container) => {
2178
- if (isPlainPrototype(container)) return;
2179
- const kind = classifyValue(container);
2180
- if (kind === "plain" || kind === "plainArray" || kind === "cleanClass") return;
2181
- const className = constructorName(container.constructor);
2182
- if (kind === "arraySubclass") {
2183
- throw new Error(
2184
- `opshot: scope found a state inside ${className}, an array subclass whose prototype can't survive substitution. Move the state to a plain array, or ignore() the ${className}.`
2185
- );
2186
- }
2187
- const hidden = kind === "privateClass" ? "private fields" : "internal slots";
2188
- throw new Error(
2189
- `opshot: scope found a state inside ${className}, whose ${hidden} can't survive substitution. Move the state to a plain container, or ignore() the ${className}.`
2190
- );
2191
- };
2192
- function findStatePaths(value, maxDepth, path = [], paths = [], ancestors = /* @__PURE__ */ new Set()) {
2193
- if (isState(value)) {
2194
- paths.push(path);
2195
- return paths;
2196
- }
2197
- if (value === null || typeof value !== "object") return paths;
2198
- if ("$$typeof" in value) return paths;
2199
- if (ancestors.has(value)) return paths;
2200
- if (path.length >= maxDepth) return paths;
2201
- ancestors.add(value);
2202
- if (Array.isArray(value)) {
2203
- const foundCount = paths.length;
2204
- value.forEach((item, index) => {
2205
- findStatePaths(item, maxDepth, [...path, index], paths, ancestors);
2206
- });
2207
- if (paths.length > foundCount) assertSubstitutableContainer(value);
2208
- } else {
2209
- const foundCount = paths.length;
2210
- for (const [key, propertyValue] of Object.entries(value)) {
2211
- if (key.startsWith("__react")) continue;
2212
- findStatePaths(propertyValue, maxDepth, [...path, key], paths, ancestors);
1571
+ var sourcesKey = (sources) => `${sources.length}:${sources.map((source) => addressOf(source)).join(",")}`;
1572
+ var uniqueHandlesOf = (nodes) => {
1573
+ const unique = new Array();
1574
+ const seen = /* @__PURE__ */ new Set();
1575
+ for (const node of nodes) {
1576
+ for (const handle of handlesOf(node)) {
1577
+ if (seen.has(handle)) continue;
1578
+ seen.add(handle);
1579
+ unique.push(handle);
2213
1580
  }
2214
- if (paths.length > foundCount) assertSubstitutableContainer(value);
2215
- }
2216
- ancestors.delete(value);
2217
- return paths;
2218
- }
2219
- function getAtPath(object, path) {
2220
- let current = object;
2221
- for (const segment of path) {
2222
- if (current === null || current === void 0) return void 0;
2223
- current = current[segment];
2224
- }
2225
- return current;
2226
- }
2227
- function setAtPath(object, path, value) {
2228
- if (path.length === 0) return value;
2229
- const head = path[0];
2230
- if (head === void 0) throw new Error("setAtPath: non-empty path yielded no head segment");
2231
- const tail = path.slice(1);
2232
- const current = object[head];
2233
- const updated = setAtPath(current, tail, value);
2234
- if (Array.isArray(object)) {
2235
- const clone2 = [...object];
2236
- clone2[head] = updated;
2237
- return clone2;
2238
- }
2239
- const prototype = Reflect.getPrototypeOf(object);
2240
- if (prototype === Object.prototype || prototype === null) return { ...object, [head]: updated };
2241
- const descriptor = Object.getOwnPropertyDescriptor(object, head);
2242
- if (descriptor?.get !== void 0 || descriptor?.set !== void 0) {
2243
- const className = constructorName(object.constructor);
2244
- throw new Error(
2245
- `opshot: scope found a state behind the accessor "${String(head)}" on ${className}, which can't survive substitution. Move the state to a plain container, or ignore() the ${className}.`
2246
- );
2247
1581
  }
2248
- const clone = Object.create(prototype);
2249
- Object.defineProperties(clone, Object.getOwnPropertyDescriptors(object));
2250
- clone[head] = updated;
2251
- return clone;
2252
- }
2253
- var sourcesKey = (sources) => `${sources.length}:${sources.map((source) => addressOf(source)).join(",")}`;
1582
+ return unique;
1583
+ };
2254
1584
  var arePropsEqual = (previous, next) => {
2255
1585
  const previousRecord = previous;
2256
1586
  const nextRecord = next;
@@ -2267,99 +1597,85 @@ var arePropsEqual = (previous, next) => {
2267
1597
  }
2268
1598
  return true;
2269
1599
  };
2270
- function scope(Component, options) {
2271
- const maxDepth = options?.maxDepth ?? 10;
1600
+ function scope(Component) {
2272
1601
  const Scoped = (props) => {
2273
- const boundaryRef = useRef(void 0);
2274
- boundaryRef.current ?? (boundaryRef.current = createBoundary());
2275
- const boundary = boundaryRef.current;
1602
+ const readTrackerRef = useRef(void 0);
1603
+ const currentHandlesRef = useRef([]);
1604
+ readTrackerRef.current ??= createReadTracker();
1605
+ const readTracker = readTrackerRef.current;
2276
1606
  const [, bump] = useReducer((value) => value + 1, 0);
2277
- boundary.resetReads();
2278
- const paths = findStatePaths(props, maxDepth);
2279
- const sources = [];
2280
- let nextProps = props;
2281
- let changed = false;
2282
- for (const path of paths) {
2283
- const value = getAtPath(props, path);
2284
- if (!isState(value)) continue;
2285
- const source = unwrapWrapper(value);
2286
- if (typeof source !== "object" || source === null) continue;
2287
- const wrapped = boundary.wrap(source);
2288
- if (!sources.includes(source)) sources.push(source);
2289
- if (wrapped !== value) {
2290
- nextProps = setAtPath(nextProps, path, wrapped);
2291
- changed = true;
2292
- }
2293
- }
2294
- const renderedProps = changed ? nextProps : props;
2295
- const versionsAtRender = sources.map((source) => getVersion(source));
2296
- useEffect(() => {
2297
- boundary.captureReads();
1607
+ readTracker.resetReads();
1608
+ const { props: renderedProps, sources } = substituteStates(props, (source) => readTracker.wrap(source));
1609
+ const uniqueHandles = uniqueHandlesOf(sources);
1610
+ useCommitEffect(() => {
1611
+ currentHandlesRef.current = uniqueHandles;
1612
+ readTracker.captureReads();
2298
1613
  });
2299
1614
  useEffect(() => {
2300
- const unsubscribes = sources.map(
2301
- (source) => subscribe(
2302
- source,
2303
- () => {
2304
- boundary.evictChangedTargets();
2305
- if (boundary.readsChanged(source)) bump();
2306
- else boundary.advanceBaselines();
2307
- },
2308
- true
2309
- )
1615
+ readTracker.retain();
1616
+ return () => readTracker.dispose();
1617
+ }, [readTracker]);
1618
+ useEffect(() => {
1619
+ let cancelled = false;
1620
+ const subscribedHandles = uniqueHandlesOf(sources);
1621
+ const unsubscribes = subscribedHandles.map(
1622
+ (handle) => subscribe(proxyOf(handle.root), () => {
1623
+ if (cancelled) return;
1624
+ const dirty = handle.lastDirty;
1625
+ if (dirty !== void 0 && readsIntersectDirty(readTracker, dirty)) bump();
1626
+ })
2310
1627
  );
1628
+ if (readsChanged(readTracker)) bump();
2311
1629
  return () => {
1630
+ cancelled = true;
2312
1631
  for (const unsubscribe of unsubscribes) unsubscribe();
2313
1632
  };
2314
- }, [sourcesKey(sources), boundary]);
2315
- useEffect(() => {
2316
- let shouldBump = false;
2317
- for (let index = 0; index < sources.length; index += 1) {
2318
- const source = sources[index];
2319
- const captured = versionsAtRender[index];
2320
- if (source === void 0 || captured === void 0) continue;
2321
- if (getVersion(source) !== captured) {
2322
- boundary.evictChangedTargets();
2323
- if (boundary.readsChanged(source)) shouldBump = true;
2324
- }
2325
- }
2326
- if (shouldBump) bump();
2327
- });
1633
+ }, [sourcesKey(sources), readTracker]);
2328
1634
  return createElement(Component, renderedProps);
2329
1635
  };
2330
1636
  const baseName = Component.displayName ?? Component.name;
2331
1637
  Scoped.displayName = `scope(${typeof baseName === "string" && baseName !== "" ? baseName : "Component"})`;
2332
1638
  return memo(Scoped, arePropsEqual);
2333
1639
  }
2334
- function useGroup() {
2335
- return useState(() => createGroup())[0];
2336
- }
2337
- function useMutableState(properties, group) {
2338
- const [{ proxy: proxy2, boundary }] = useState(() => ({
2339
- proxy: createMutableState(typeof properties === "function" ? properties() : properties, group),
2340
- boundary: createBoundary()
2341
- }));
1640
+ var isObjectLike3 = (value) => value !== null && (typeof value === "object" || typeof value === "function");
1641
+ function useMutableState(properties, options) {
1642
+ const [{ writeProxy, readTracker }] = useState(() => {
1643
+ const initial = typeof properties === "function" ? properties() : properties;
1644
+ return {
1645
+ writeProxy: createMutableState(initial, options),
1646
+ readTracker: createReadTracker()
1647
+ };
1648
+ });
2342
1649
  const [, bump] = useReducer((value) => value + 1, 0);
2343
- const versionAtRender = getVersion(proxy2);
2344
- boundary.resetReads();
2345
- const wrapper = boundary.wrap(proxy2);
2346
- useEffect(() => {
2347
- boundary.captureReads();
1650
+ const currentHandlesRef = useRef([]);
1651
+ const uniqueHandles = isObjectLike3(writeProxy) ? handlesOf(writeProxy) : [];
1652
+ readTracker.resetReads();
1653
+ const readProxy = isObjectLike3(writeProxy) ? readTracker.wrap(writeProxy) : writeProxy;
1654
+ useCommitEffect(() => {
1655
+ currentHandlesRef.current = uniqueHandles;
1656
+ readTracker.captureReads();
2348
1657
  });
2349
1658
  useEffect(() => {
2350
- const onSignal = () => {
2351
- boundary.evictChangedTargets();
2352
- if (boundary.readsChanged(proxy2)) bump();
2353
- else boundary.advanceBaselines();
2354
- };
2355
- return subscribe(proxy2, onSignal, true);
2356
- }, [proxy2, boundary]);
1659
+ readTracker.retain();
1660
+ return () => readTracker.dispose();
1661
+ }, [readTracker]);
2357
1662
  useEffect(() => {
2358
- if (getVersion(proxy2) === versionAtRender) return;
2359
- boundary.evictChangedTargets();
2360
- if (boundary.readsChanged(proxy2)) bump();
2361
- });
2362
- return wrapper;
1663
+ let cancelled = false;
1664
+ const subscribedHandles = isObjectLike3(writeProxy) ? handlesOf(writeProxy) : [];
1665
+ const unsubscribes = subscribedHandles.map(
1666
+ (handle) => subscribe(proxyOf(handle.root), () => {
1667
+ if (cancelled) return;
1668
+ const dirty = handle.lastDirty;
1669
+ if (dirty !== void 0 && readsIntersectDirty(readTracker, dirty)) bump();
1670
+ })
1671
+ );
1672
+ if (readsChanged(readTracker)) bump();
1673
+ return () => {
1674
+ cancelled = true;
1675
+ for (const unsubscribe of unsubscribes) unsubscribe();
1676
+ };
1677
+ }, [writeProxy, readTracker]);
1678
+ return readProxy;
2363
1679
  }
2364
1680
 
2365
- export { TrackedDate, TrackedMap, TrackedSet, applyOps, createChannel, createGroup, createMutableState, diffSnapshots, identify, ignore, isSameIdentity, isState, scope, subscribe2 as subscribe, transact, unsafeTrack, useGroup, useMutableState };
1681
+ export { TrackedDate, TrackedMap, TrackedSet, batch, createMutableState, identify, ignore, isSameIdentity, isState, scope, subscribe, unsafeTrack, useMutableState };