@ny4n/chord 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -2
- package/dist/api.d.ts +20 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +77 -0
- package/dist/api.js.map +1 -0
- package/dist/bundler.d.ts +6 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/bundler.js +3 -0
- package/dist/bundler.js.map +1 -0
- package/dist/context/index.d.ts +24 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +97 -0
- package/dist/context/index.js.map +1 -0
- package/dist/delta/apply-immutable-trusted.d.ts +4 -0
- package/dist/delta/apply-immutable-trusted.d.ts.map +1 -0
- package/dist/delta/apply-immutable-trusted.js +127 -0
- package/dist/delta/apply-immutable-trusted.js.map +1 -0
- package/dist/delta/core.d.ts +108 -0
- package/dist/delta/core.d.ts.map +1 -0
- package/dist/delta/core.js +645 -0
- package/dist/delta/core.js.map +1 -0
- package/dist/delta/diff.d.ts +5 -0
- package/dist/delta/diff.d.ts.map +1 -0
- package/dist/delta/diff.js +433 -0
- package/dist/delta/diff.js.map +1 -0
- package/dist/delta/draft.d.ts +5 -0
- package/dist/delta/draft.d.ts.map +1 -0
- package/dist/delta/draft.js +2 -0
- package/dist/delta/draft.js.map +1 -0
- package/dist/delta/index.d.ts +5 -0
- package/dist/delta/index.d.ts.map +1 -0
- package/dist/delta/index.js +6 -0
- package/dist/delta/index.js.map +1 -0
- package/dist/delta/revision-validator.d.ts +7 -0
- package/dist/delta/revision-validator.d.ts.map +1 -0
- package/dist/delta/revision-validator.js +71 -0
- package/dist/delta/revision-validator.js.map +1 -0
- package/dist/delta/tracker.d.ts +26 -0
- package/dist/delta/tracker.d.ts.map +1 -0
- package/dist/delta/tracker.js +2069 -0
- package/dist/delta/tracker.js.map +1 -0
- package/dist/facets/host.d.ts +12 -0
- package/dist/facets/host.d.ts.map +1 -0
- package/dist/facets/host.js +763 -0
- package/dist/facets/host.js.map +1 -0
- package/dist/facets/loader.d.ts +3 -0
- package/dist/facets/loader.d.ts.map +1 -0
- package/dist/facets/loader.js +5 -0
- package/dist/facets/loader.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +10 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +124 -0
- package/dist/json.js.map +1 -0
- package/dist/node/bundle-loader.d.ts +30 -0
- package/dist/node/bundle-loader.d.ts.map +1 -0
- package/dist/node/bundle-loader.js +377 -0
- package/dist/node/bundle-loader.js.map +1 -0
- package/dist/node/bundle.d.ts +26 -0
- package/dist/node/bundle.d.ts.map +1 -0
- package/dist/node/bundle.js +183 -0
- package/dist/node/bundle.js.map +1 -0
- package/dist/node/manifest.d.ts +36 -0
- package/dist/node/manifest.d.ts.map +1 -0
- package/dist/node/manifest.js +6 -0
- package/dist/node/manifest.js.map +1 -0
- package/dist/node/package.d.ts +15 -0
- package/dist/node/package.d.ts.map +1 -0
- package/dist/node/package.js +199 -0
- package/dist/node/package.js.map +1 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +3 -0
- package/dist/node.js.map +1 -0
- package/dist/services/consumer.d.ts +11 -0
- package/dist/services/consumer.d.ts.map +1 -0
- package/dist/services/consumer.js +585 -0
- package/dist/services/consumer.js.map +1 -0
- package/dist/services/errors.d.ts +8 -0
- package/dist/services/errors.d.ts.map +1 -0
- package/dist/services/errors.js +22 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/handle.d.ts +15 -0
- package/dist/services/handle.d.ts.map +1 -0
- package/dist/services/handle.js +101 -0
- package/dist/services/handle.js.map +1 -0
- package/dist/services/instances.d.ts +26 -0
- package/dist/services/instances.d.ts.map +1 -0
- package/dist/services/instances.js +140 -0
- package/dist/services/instances.js.map +1 -0
- package/dist/services/loopback.d.ts +5 -0
- package/dist/services/loopback.d.ts.map +1 -0
- package/dist/services/loopback.js +15 -0
- package/dist/services/loopback.js.map +1 -0
- package/dist/services/provider.d.ts +37 -0
- package/dist/services/provider.d.ts.map +1 -0
- package/dist/services/provider.js +521 -0
- package/dist/services/provider.js.map +1 -0
- package/dist/services/state-codec.d.ts +15 -0
- package/dist/services/state-codec.d.ts.map +1 -0
- package/dist/services/state-codec.js +133 -0
- package/dist/services/state-codec.js.map +1 -0
- package/dist/services/state-internals.d.ts +13 -0
- package/dist/services/state-internals.d.ts.map +1 -0
- package/dist/services/state-internals.js +10 -0
- package/dist/services/state-internals.js.map +1 -0
- package/dist/services/state.d.ts +28 -0
- package/dist/services/state.d.ts.map +1 -0
- package/dist/services/state.js +397 -0
- package/dist/services/state.js.map +1 -0
- package/dist/services/wire.d.ts +63 -0
- package/dist/services/wire.d.ts.map +1 -0
- package/dist/services/wire.js +186 -0
- package/dist/services/wire.js.map +1 -0
- package/dist/types.d.ts +277 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -5
- package/src/delta/README.md +191 -0
|
@@ -0,0 +1,2069 @@
|
|
|
1
|
+
import { copyJson } from "../json.js";
|
|
2
|
+
import { applyImmutableTrusted } from "./apply-immutable-trusted.js";
|
|
3
|
+
import { applyImmutable, overlap, RESERVED_SEGMENTS } from "./core.js";
|
|
4
|
+
const NODE = Symbol("chord.delta.overlay.node");
|
|
5
|
+
const PREPARED = new WeakMap();
|
|
6
|
+
const RELEASED = {};
|
|
7
|
+
const ARRAY_MUTATORS = new Set([
|
|
8
|
+
"push",
|
|
9
|
+
"pop",
|
|
10
|
+
"shift",
|
|
11
|
+
"unshift",
|
|
12
|
+
"splice",
|
|
13
|
+
"reverse",
|
|
14
|
+
"sort",
|
|
15
|
+
"fill",
|
|
16
|
+
"copyWithin",
|
|
17
|
+
]);
|
|
18
|
+
const MAX_DELTA_OPERATIONS = 4_096;
|
|
19
|
+
const MAX_SIMPLE_OBJECT_NODES = 128;
|
|
20
|
+
const DATA_DESCRIPTOR = {
|
|
21
|
+
value: undefined,
|
|
22
|
+
writable: true,
|
|
23
|
+
enumerable: true,
|
|
24
|
+
configurable: true,
|
|
25
|
+
};
|
|
26
|
+
class PreparedImpl {
|
|
27
|
+
#context;
|
|
28
|
+
value;
|
|
29
|
+
base;
|
|
30
|
+
ops;
|
|
31
|
+
constructor(context, value, operations) {
|
|
32
|
+
this.#context = context;
|
|
33
|
+
this.value = value;
|
|
34
|
+
this.base = context.baseValue;
|
|
35
|
+
this.ops = operations;
|
|
36
|
+
PREPARED.set(this, context);
|
|
37
|
+
}
|
|
38
|
+
get baseRevision() {
|
|
39
|
+
return this.#context.baseRevision;
|
|
40
|
+
}
|
|
41
|
+
abort() {
|
|
42
|
+
abortContext(this.#context);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
class ChangeImpl {
|
|
46
|
+
#context;
|
|
47
|
+
#preparedStatus;
|
|
48
|
+
#settled = false;
|
|
49
|
+
constructor(context) {
|
|
50
|
+
this.#context = context;
|
|
51
|
+
}
|
|
52
|
+
get state() {
|
|
53
|
+
const state = this.#context?.root?.proxy;
|
|
54
|
+
if (state === undefined)
|
|
55
|
+
throw new TypeError("Cannot use a settled overlay");
|
|
56
|
+
return state;
|
|
57
|
+
}
|
|
58
|
+
prepare() {
|
|
59
|
+
if (this.#settled)
|
|
60
|
+
throw new Error("Change has already been settled");
|
|
61
|
+
const context = this.#context;
|
|
62
|
+
assertWritable(context);
|
|
63
|
+
context.status.value = "prepared";
|
|
64
|
+
try {
|
|
65
|
+
if (!context.replacement)
|
|
66
|
+
context.ops = context.dirty.length === 0 ? [] : emitOperations(context);
|
|
67
|
+
const prepared = materializePrepared(context);
|
|
68
|
+
this.#preparedStatus = context.status;
|
|
69
|
+
this.#settled = true;
|
|
70
|
+
return prepared;
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
context.status.value = "aborted";
|
|
74
|
+
clearContext(context);
|
|
75
|
+
this.#settled = true;
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
this.#context = undefined;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
abort() {
|
|
83
|
+
if (this.#settled) {
|
|
84
|
+
if (this.#preparedStatus?.value === "prepared")
|
|
85
|
+
this.#preparedStatus.value = "aborted";
|
|
86
|
+
this.#preparedStatus = undefined;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.#settled = true;
|
|
90
|
+
abortContext(this.#context);
|
|
91
|
+
this.#context = undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
class TrackerImpl {
|
|
95
|
+
#owner = {};
|
|
96
|
+
#contexts = new Set();
|
|
97
|
+
#selfRef;
|
|
98
|
+
#value;
|
|
99
|
+
#revision = 0;
|
|
100
|
+
#pruneBudget = 256;
|
|
101
|
+
constructor(initial) {
|
|
102
|
+
this.#value = initial;
|
|
103
|
+
this.#selfRef = new WeakRef(this);
|
|
104
|
+
}
|
|
105
|
+
get value() {
|
|
106
|
+
return this.#value;
|
|
107
|
+
}
|
|
108
|
+
get revision() {
|
|
109
|
+
return this.#revision;
|
|
110
|
+
}
|
|
111
|
+
beginChange() {
|
|
112
|
+
const context = createContext(this.#selfRef, this.#owner, this.#revision, this.#value, false, this.#value);
|
|
113
|
+
this.#register(context);
|
|
114
|
+
return new ChangeImpl(context);
|
|
115
|
+
}
|
|
116
|
+
prepareReplace(value) {
|
|
117
|
+
const context = createContext(this.#selfRef, this.#owner, this.#revision, value, true, this.#value);
|
|
118
|
+
context.status.value = "prepared";
|
|
119
|
+
this.#register(context);
|
|
120
|
+
try {
|
|
121
|
+
return materializePrepared(context);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
context.status.value = "aborted";
|
|
125
|
+
clearContext(context);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
adopt(prepared) {
|
|
130
|
+
const context = PREPARED.get(prepared);
|
|
131
|
+
if (context?.owner !== this.#owner)
|
|
132
|
+
throw new Error("Prepared change belongs to a different tracker");
|
|
133
|
+
if (context.status.value === "consumed")
|
|
134
|
+
throw new Error("Prepared change has already been used");
|
|
135
|
+
if (context.status.value === "aborted")
|
|
136
|
+
throw new Error("Prepared change has been aborted");
|
|
137
|
+
if (context.status.value === "stale")
|
|
138
|
+
throw new Error("Prepared change is stale");
|
|
139
|
+
if (context.status.value !== "prepared")
|
|
140
|
+
throw new Error("Prepared change is not ready");
|
|
141
|
+
if (context.baseRevision !== this.#revision) {
|
|
142
|
+
context.status.value = "stale";
|
|
143
|
+
clearContext(context);
|
|
144
|
+
throw new Error("Prepared change is stale");
|
|
145
|
+
}
|
|
146
|
+
if (this.#value !== prepared.base) {
|
|
147
|
+
context.status.value = "stale";
|
|
148
|
+
throw new Error("Prepared change is stale");
|
|
149
|
+
}
|
|
150
|
+
// Materialization happened during prepare. Adoption is an infallible pointer
|
|
151
|
+
// swap so storage failure can discard the candidate without touching authority.
|
|
152
|
+
this.#value = prepared.value;
|
|
153
|
+
context.status.value = "consumed";
|
|
154
|
+
this.#revision += 1;
|
|
155
|
+
this.#invalidate(context);
|
|
156
|
+
}
|
|
157
|
+
releaseContext(context) {
|
|
158
|
+
if (context.registryRef !== undefined)
|
|
159
|
+
this.#contexts.delete(context.registryRef);
|
|
160
|
+
context.registryRef = undefined;
|
|
161
|
+
}
|
|
162
|
+
#register(context) {
|
|
163
|
+
const reference = new WeakRef(context);
|
|
164
|
+
context.registryRef = reference;
|
|
165
|
+
this.#contexts.add(reference);
|
|
166
|
+
this.#pruneBudget -= 1;
|
|
167
|
+
if (this.#pruneBudget === 0) {
|
|
168
|
+
this.#prune();
|
|
169
|
+
this.#pruneBudget = Math.max(256, this.#contexts.size);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
#prune() {
|
|
173
|
+
for (const reference of this.#contexts) {
|
|
174
|
+
if (reference.deref() === undefined)
|
|
175
|
+
this.#contexts.delete(reference);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
#invalidate(winner) {
|
|
179
|
+
for (const reference of this.#contexts) {
|
|
180
|
+
const context = reference.deref();
|
|
181
|
+
if (context === undefined || context === winner)
|
|
182
|
+
continue;
|
|
183
|
+
if (context.status.value === "open" || context.status.value === "prepared")
|
|
184
|
+
context.status.value = "stale";
|
|
185
|
+
// Adoption must not walk every node touched by a competing draft. Drop the
|
|
186
|
+
// context's strong overlay references in O(1); externally retained proxies
|
|
187
|
+
// still see the stale status and are reclaimed with their holders.
|
|
188
|
+
releaseOverlayReferences(context);
|
|
189
|
+
}
|
|
190
|
+
this.#contexts.clear();
|
|
191
|
+
this.#pruneBudget = 256;
|
|
192
|
+
}
|
|
193
|
+
replacementBase(context) {
|
|
194
|
+
return context.baseRevision === this.#revision ? this.#value : undefined;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Take immutable ownership of an alias-free strict-JSON root in O(1). */
|
|
198
|
+
export function track(initial) {
|
|
199
|
+
return new TrackerImpl(initial);
|
|
200
|
+
}
|
|
201
|
+
function materializePrepared(context) {
|
|
202
|
+
const operations = ensureOperations(context);
|
|
203
|
+
const base = context.baseValue;
|
|
204
|
+
// Operation payloads are already detached from the mutable overlay. The
|
|
205
|
+
// resulting revision and public batch intentionally share those payloads under
|
|
206
|
+
// the trusted immutable-transfer contract.
|
|
207
|
+
const value = operations.length === 0
|
|
208
|
+
? base
|
|
209
|
+
: context.simpleObjectMaterialization
|
|
210
|
+
? cloneNode(context.root)
|
|
211
|
+
: materializeOperations(base, operations);
|
|
212
|
+
const prepared = new PreparedImpl(context, value, operations);
|
|
213
|
+
if (context.nodes.length > 4_096)
|
|
214
|
+
releaseOverlayReferences(context);
|
|
215
|
+
else
|
|
216
|
+
clearContext(context);
|
|
217
|
+
return prepared;
|
|
218
|
+
}
|
|
219
|
+
function materializeOperations(base, operations) {
|
|
220
|
+
for (const operation of operations) {
|
|
221
|
+
// Native splice/permutation remains faster for large structural batches.
|
|
222
|
+
if (operation[0] === "p" || operation[0] === "m")
|
|
223
|
+
return applyImmutable(base, operations);
|
|
224
|
+
}
|
|
225
|
+
return applyImmutableTrusted(base, operations);
|
|
226
|
+
}
|
|
227
|
+
function createContext(tracker, owner, baseRevision, root, replacement, base) {
|
|
228
|
+
const context = {
|
|
229
|
+
owner,
|
|
230
|
+
tracker,
|
|
231
|
+
baseRevision,
|
|
232
|
+
status: { value: "open" },
|
|
233
|
+
root: undefined,
|
|
234
|
+
bases: [],
|
|
235
|
+
stored: [],
|
|
236
|
+
dirty: [],
|
|
237
|
+
nodes: [],
|
|
238
|
+
rawNodes: new WeakMap(),
|
|
239
|
+
ops: undefined,
|
|
240
|
+
replacement,
|
|
241
|
+
replacementNoop: false,
|
|
242
|
+
baseValue: base,
|
|
243
|
+
overlayReleased: false,
|
|
244
|
+
simpleObjectMaterialization: false,
|
|
245
|
+
registryRef: undefined,
|
|
246
|
+
};
|
|
247
|
+
context.root = createNode(context, root, undefined);
|
|
248
|
+
return context;
|
|
249
|
+
}
|
|
250
|
+
const sharedObjectHandler = {
|
|
251
|
+
deleteProperty(target, property) {
|
|
252
|
+
return deleteProperty(nodeForTarget(target), property);
|
|
253
|
+
},
|
|
254
|
+
defineProperty(target) {
|
|
255
|
+
assertWritable(nodeForTarget(target).context);
|
|
256
|
+
throw new TypeError("Defining overlay properties is not supported");
|
|
257
|
+
},
|
|
258
|
+
get(target, property) {
|
|
259
|
+
if (property === "then") {
|
|
260
|
+
const node = target[NODE];
|
|
261
|
+
if (node === undefined || isSettledContext(node.context))
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
const node = nodeForTarget(target);
|
|
265
|
+
if (property === NODE)
|
|
266
|
+
return node;
|
|
267
|
+
return getProperty(node, property);
|
|
268
|
+
},
|
|
269
|
+
getOwnPropertyDescriptor(target, property) {
|
|
270
|
+
return getDescriptor(nodeForTarget(target), property);
|
|
271
|
+
},
|
|
272
|
+
getPrototypeOf(target) {
|
|
273
|
+
const node = nodeForTarget(target);
|
|
274
|
+
assertReadable(node.context);
|
|
275
|
+
return Object.getPrototypeOf(nodeBase(node));
|
|
276
|
+
},
|
|
277
|
+
has(target, property) {
|
|
278
|
+
return hasProperty(nodeForTarget(target), property);
|
|
279
|
+
},
|
|
280
|
+
isExtensible(target) {
|
|
281
|
+
assertReadable(nodeForTarget(target).context);
|
|
282
|
+
return true;
|
|
283
|
+
},
|
|
284
|
+
ownKeys(target) {
|
|
285
|
+
return ownKeys(nodeForTarget(target));
|
|
286
|
+
},
|
|
287
|
+
preventExtensions(target) {
|
|
288
|
+
assertWritable(nodeForTarget(target).context);
|
|
289
|
+
throw new TypeError("Overlays cannot be made non-extensible");
|
|
290
|
+
},
|
|
291
|
+
set(target, property, value) {
|
|
292
|
+
return setProperty(nodeForTarget(target), property, value);
|
|
293
|
+
},
|
|
294
|
+
setPrototypeOf(target) {
|
|
295
|
+
assertWritable(nodeForTarget(target).context);
|
|
296
|
+
throw new TypeError("Changing an overlay prototype is not supported");
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
const sharedArrayHandler = sharedObjectHandler;
|
|
300
|
+
function createNode(context, base, parent, parentKind = 0, parentKey = "", parentSource = undefined, parentPlacement = false) {
|
|
301
|
+
const existing = context.rawNodes.get(base);
|
|
302
|
+
if (existing !== undefined)
|
|
303
|
+
return existing;
|
|
304
|
+
const target = Array.isArray(base) ? [] : {};
|
|
305
|
+
const baseIndex = context.bases.length;
|
|
306
|
+
const parentIndex = parent?.baseIndex ?? -1;
|
|
307
|
+
context.bases.push(base);
|
|
308
|
+
const node = {
|
|
309
|
+
context,
|
|
310
|
+
baseIndex,
|
|
311
|
+
parentIndex,
|
|
312
|
+
parentKind,
|
|
313
|
+
parentKey,
|
|
314
|
+
parentPlacement,
|
|
315
|
+
target,
|
|
316
|
+
proxy: target,
|
|
317
|
+
};
|
|
318
|
+
if (parentSource !== undefined)
|
|
319
|
+
node.parentSource = parentSource;
|
|
320
|
+
target[NODE] = node;
|
|
321
|
+
node.proxy = new Proxy(target, Array.isArray(base) ? sharedArrayHandler : sharedObjectHandler);
|
|
322
|
+
context.rawNodes.set(base, node);
|
|
323
|
+
context.nodes.push(node);
|
|
324
|
+
return node;
|
|
325
|
+
}
|
|
326
|
+
function nodeBase(node) {
|
|
327
|
+
return node.context.bases[node.baseIndex];
|
|
328
|
+
}
|
|
329
|
+
function nodeParent(node) {
|
|
330
|
+
return node.parentIndex < 0 ? undefined : node.context.nodes[node.parentIndex];
|
|
331
|
+
}
|
|
332
|
+
function storeValue(context, value) {
|
|
333
|
+
if (!isContainer(value))
|
|
334
|
+
return value;
|
|
335
|
+
const index = context.stored.length;
|
|
336
|
+
context.stored.push(value);
|
|
337
|
+
return { index };
|
|
338
|
+
}
|
|
339
|
+
function storedValue(context, reference) {
|
|
340
|
+
return typeof reference === "object" && reference !== null ? context.stored[reference.index] : reference;
|
|
341
|
+
}
|
|
342
|
+
function replaceStoredValue(context, reference, value) {
|
|
343
|
+
if (isContainer(value)) {
|
|
344
|
+
if (typeof reference === "object" && reference !== null) {
|
|
345
|
+
context.stored[reference.index] = value;
|
|
346
|
+
return reference;
|
|
347
|
+
}
|
|
348
|
+
return storeValue(context, value);
|
|
349
|
+
}
|
|
350
|
+
if (typeof reference === "object" && reference !== null)
|
|
351
|
+
context.stored[reference.index] = RELEASED;
|
|
352
|
+
return value;
|
|
353
|
+
}
|
|
354
|
+
function releaseStoredValue(context, reference) {
|
|
355
|
+
if (typeof reference === "object" && reference !== null)
|
|
356
|
+
context.stored[reference.index] = RELEASED;
|
|
357
|
+
}
|
|
358
|
+
function nodeForTarget(target) {
|
|
359
|
+
const node = target[NODE];
|
|
360
|
+
if (node === undefined)
|
|
361
|
+
throw new TypeError("Cannot use a settled overlay");
|
|
362
|
+
return node;
|
|
363
|
+
}
|
|
364
|
+
function isSettledContext(context) {
|
|
365
|
+
return (context.overlayReleased ||
|
|
366
|
+
context.status.value === "consumed" ||
|
|
367
|
+
context.status.value === "aborted" ||
|
|
368
|
+
context.status.value === "stale");
|
|
369
|
+
}
|
|
370
|
+
function assertReadable(context) {
|
|
371
|
+
if (isSettledContext(context))
|
|
372
|
+
throw new TypeError("Cannot use a settled overlay");
|
|
373
|
+
}
|
|
374
|
+
function assertWritable(context) {
|
|
375
|
+
assertReadable(context);
|
|
376
|
+
if (context.status.value !== "open")
|
|
377
|
+
throw new TypeError("Prepared overlays are read-only");
|
|
378
|
+
}
|
|
379
|
+
function getProperty(node, property) {
|
|
380
|
+
assertReadable(node.context);
|
|
381
|
+
if (Array.isArray(nodeBase(node))) {
|
|
382
|
+
const overlay = arrayOverlay(node);
|
|
383
|
+
if (property === "length")
|
|
384
|
+
return arrayLength(overlay);
|
|
385
|
+
if (ARRAY_MUTATORS.has(property))
|
|
386
|
+
return arrayMutators[property];
|
|
387
|
+
const index = arrayIndex(property);
|
|
388
|
+
if (index !== undefined)
|
|
389
|
+
return getArrayIndex(node, index);
|
|
390
|
+
return Reflect.get(Array.prototype, property, node.proxy);
|
|
391
|
+
}
|
|
392
|
+
if (typeof property === "symbol")
|
|
393
|
+
return Reflect.get(nodeBase(node), property, node.proxy);
|
|
394
|
+
const key = String(property);
|
|
395
|
+
if (!objectHas(node, key)) {
|
|
396
|
+
if (isObjectDeleted(node, key) || Object.hasOwn(nodeBase(node), key))
|
|
397
|
+
return undefined;
|
|
398
|
+
return Reflect.get(nodeBase(node), property, node.proxy);
|
|
399
|
+
}
|
|
400
|
+
const value = objectValue(node, key);
|
|
401
|
+
if (!isContainer(value))
|
|
402
|
+
return value;
|
|
403
|
+
return createNode(node.context, value, node, 0, key, undefined, hasObjectWrite(node, key)).proxy;
|
|
404
|
+
}
|
|
405
|
+
function getArrayIndex(node, index) {
|
|
406
|
+
const overlay = arrayOverlay(node);
|
|
407
|
+
if (index >= arrayLength(overlay))
|
|
408
|
+
return undefined;
|
|
409
|
+
const piece = locatePiece(overlay, index);
|
|
410
|
+
const sourceIndex = piece.start + piece.step * overlay.locatedOffset;
|
|
411
|
+
const value = entryValueAt(node, piece, sourceIndex);
|
|
412
|
+
if (!isContainer(value))
|
|
413
|
+
return value;
|
|
414
|
+
return createNode(node.context, value, node, piece.kind === "base" ? 1 : 2, sourceIndex, piece.kind === "insert" ? piece.source : undefined, piece.kind === "insert" || hasEntryOverrideAt(overlay, piece, sourceIndex)).proxy;
|
|
415
|
+
}
|
|
416
|
+
function setProperty(node, property, supplied) {
|
|
417
|
+
assertWritable(node.context);
|
|
418
|
+
if (typeof property === "symbol")
|
|
419
|
+
throw new TypeError("Symbol writes are not supported");
|
|
420
|
+
if (Array.isArray(nodeBase(node))) {
|
|
421
|
+
if (property === "length") {
|
|
422
|
+
setArrayLength(node, toArrayLength(supplied));
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
const index = arrayIndex(property);
|
|
426
|
+
if (index === undefined)
|
|
427
|
+
throw new TypeError("Only array indices and length can be written");
|
|
428
|
+
if (index > arrayLength(arrayOverlay(node)))
|
|
429
|
+
throw new TypeError("Overlay arrays cannot contain holes");
|
|
430
|
+
setArrayIndex(node, index, clonePlacement(supplied));
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
const key = String(property);
|
|
434
|
+
if (supplied === undefined)
|
|
435
|
+
return deleteProperty(node, key);
|
|
436
|
+
const stored = clonePlacement(supplied);
|
|
437
|
+
const current = objectValue(node, key);
|
|
438
|
+
const wasDeleted = isObjectDeleted(node, key);
|
|
439
|
+
if (!wasDeleted && !isContainer(stored) && current === stored)
|
|
440
|
+
return true;
|
|
441
|
+
setObjectWrite(node, key, stored);
|
|
442
|
+
if (wasDeleted && Object.hasOwn(nodeBase(node), key)) {
|
|
443
|
+
if (node.readded === undefined)
|
|
444
|
+
node.readded = new Set();
|
|
445
|
+
node.readded.add(key);
|
|
446
|
+
}
|
|
447
|
+
deleteObjectDeletion(node, key);
|
|
448
|
+
markDirty(node);
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
function deleteProperty(node, property) {
|
|
452
|
+
assertWritable(node.context);
|
|
453
|
+
if (typeof property === "symbol")
|
|
454
|
+
throw new TypeError("Symbol writes are not supported");
|
|
455
|
+
if (Array.isArray(nodeBase(node)))
|
|
456
|
+
throw new TypeError("Overlay arrays cannot contain holes");
|
|
457
|
+
const key = String(property);
|
|
458
|
+
if (!objectHas(node, key))
|
|
459
|
+
return true;
|
|
460
|
+
deleteObjectWrite(node, key);
|
|
461
|
+
node.readded?.delete(key);
|
|
462
|
+
setObjectDeletion(node, key);
|
|
463
|
+
markDirty(node);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
function hasProperty(node, property) {
|
|
467
|
+
assertReadable(node.context);
|
|
468
|
+
if (Array.isArray(nodeBase(node))) {
|
|
469
|
+
if (property === "length")
|
|
470
|
+
return true;
|
|
471
|
+
const index = arrayIndex(property);
|
|
472
|
+
if (index !== undefined)
|
|
473
|
+
return index < arrayLength(arrayOverlay(node));
|
|
474
|
+
return property in Array.prototype;
|
|
475
|
+
}
|
|
476
|
+
return typeof property === "symbol" ? property in nodeBase(node) : objectHas(node, String(property));
|
|
477
|
+
}
|
|
478
|
+
function ownKeys(node) {
|
|
479
|
+
assertReadable(node.context);
|
|
480
|
+
if (Array.isArray(nodeBase(node))) {
|
|
481
|
+
const length = arrayLength(arrayOverlay(node));
|
|
482
|
+
const keys = new Array(length + 1);
|
|
483
|
+
for (let index = 0; index < length; index++)
|
|
484
|
+
keys[index] = String(index);
|
|
485
|
+
keys[length] = "length";
|
|
486
|
+
return keys;
|
|
487
|
+
}
|
|
488
|
+
let existingKeysOnly = node.deleteKey === undefined &&
|
|
489
|
+
node.deletes === undefined &&
|
|
490
|
+
node.readded === undefined &&
|
|
491
|
+
(node.writeKey === undefined || Object.hasOwn(nodeBase(node), node.writeKey));
|
|
492
|
+
if (existingKeysOnly && node.writes !== undefined) {
|
|
493
|
+
for (const key of node.writes.keys()) {
|
|
494
|
+
if (!Object.hasOwn(nodeBase(node), key)) {
|
|
495
|
+
existingKeysOnly = false;
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (existingKeysOnly)
|
|
501
|
+
return Object.keys(nodeBase(node));
|
|
502
|
+
const keys = Object.keys(nodeBase(node)).filter((key) => !isObjectDeleted(node, key) && !node.readded?.has(key));
|
|
503
|
+
const seen = new Set(keys);
|
|
504
|
+
if (node.writeKey !== undefined && !seen.has(node.writeKey)) {
|
|
505
|
+
keys.push(node.writeKey);
|
|
506
|
+
seen.add(node.writeKey);
|
|
507
|
+
}
|
|
508
|
+
if (node.writes !== undefined) {
|
|
509
|
+
for (const key of node.writes.keys()) {
|
|
510
|
+
if (seen.has(key))
|
|
511
|
+
continue;
|
|
512
|
+
keys.push(key);
|
|
513
|
+
seen.add(key);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
const indices = [];
|
|
517
|
+
const strings = [];
|
|
518
|
+
for (const key of keys) {
|
|
519
|
+
const index = arrayIndex(key);
|
|
520
|
+
if (index === undefined)
|
|
521
|
+
strings.push(key);
|
|
522
|
+
else
|
|
523
|
+
indices.push(index);
|
|
524
|
+
}
|
|
525
|
+
indices.sort((left, right) => left - right);
|
|
526
|
+
return [...indices.map(String), ...strings];
|
|
527
|
+
}
|
|
528
|
+
function getDescriptor(node, property) {
|
|
529
|
+
assertReadable(node.context);
|
|
530
|
+
if (Array.isArray(nodeBase(node))) {
|
|
531
|
+
if (property === "length") {
|
|
532
|
+
const length = arrayLength(arrayOverlay(node));
|
|
533
|
+
node.target.length = length;
|
|
534
|
+
return Reflect.getOwnPropertyDescriptor(node.target, "length");
|
|
535
|
+
}
|
|
536
|
+
const index = arrayIndex(property);
|
|
537
|
+
if (index === undefined || index >= arrayLength(arrayOverlay(node)))
|
|
538
|
+
return undefined;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
if (typeof property === "symbol" || !objectHas(node, String(property)))
|
|
542
|
+
return undefined;
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
configurable: true,
|
|
546
|
+
enumerable: true,
|
|
547
|
+
writable: node.context.status.value === "open",
|
|
548
|
+
value: getProperty(node, property),
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function hasObjectWrite(node, key) {
|
|
552
|
+
return node.writeKey === key || (node.writes?.has(key) ?? false);
|
|
553
|
+
}
|
|
554
|
+
function setObjectWrite(node, key, value) {
|
|
555
|
+
if (node.writes !== undefined) {
|
|
556
|
+
const reference = node.writes.get(key);
|
|
557
|
+
node.writes.set(key, reference === undefined ? storeValue(node.context, value) : replaceStoredValue(node.context, reference, value));
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (node.writeKey === undefined || node.writeKey === key) {
|
|
561
|
+
node.writeKey = key;
|
|
562
|
+
node.writeValue =
|
|
563
|
+
node.writeValue === undefined
|
|
564
|
+
? storeValue(node.context, value)
|
|
565
|
+
: replaceStoredValue(node.context, node.writeValue, value);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
node.writes = new Map();
|
|
569
|
+
node.writes.set(node.writeKey, node.writeValue);
|
|
570
|
+
node.writes.set(key, storeValue(node.context, value));
|
|
571
|
+
node.writeKey = undefined;
|
|
572
|
+
node.writeValue = undefined;
|
|
573
|
+
}
|
|
574
|
+
function deleteObjectWrite(node, key) {
|
|
575
|
+
if (node.writeKey === key) {
|
|
576
|
+
node.writeKey = undefined;
|
|
577
|
+
releaseStoredValue(node.context, node.writeValue);
|
|
578
|
+
node.writeValue = undefined;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
const reference = node.writes?.get(key);
|
|
582
|
+
if (reference !== undefined)
|
|
583
|
+
releaseStoredValue(node.context, reference);
|
|
584
|
+
node.writes?.delete(key);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function isObjectDeleted(node, key) {
|
|
588
|
+
return node.deleteKey === key || (node.deletes?.has(key) ?? false);
|
|
589
|
+
}
|
|
590
|
+
function setObjectDeletion(node, key) {
|
|
591
|
+
if (node.deletes !== undefined) {
|
|
592
|
+
node.deletes.add(key);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (node.deleteKey === undefined || node.deleteKey === key) {
|
|
596
|
+
node.deleteKey = key;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
node.deletes = new Set();
|
|
600
|
+
node.deletes.add(node.deleteKey);
|
|
601
|
+
node.deletes.add(key);
|
|
602
|
+
node.deleteKey = undefined;
|
|
603
|
+
}
|
|
604
|
+
function deleteObjectDeletion(node, key) {
|
|
605
|
+
if (node.deleteKey === key)
|
|
606
|
+
node.deleteKey = undefined;
|
|
607
|
+
else
|
|
608
|
+
node.deletes?.delete(key);
|
|
609
|
+
}
|
|
610
|
+
function objectHas(node, key) {
|
|
611
|
+
if (isObjectDeleted(node, key))
|
|
612
|
+
return false;
|
|
613
|
+
return hasObjectWrite(node, key) || Object.hasOwn(nodeBase(node), key);
|
|
614
|
+
}
|
|
615
|
+
function objectValue(node, key) {
|
|
616
|
+
if (node.writeKey === key)
|
|
617
|
+
return storedValue(node.context, node.writeValue);
|
|
618
|
+
const reference = node.writes?.get(key);
|
|
619
|
+
if (reference !== undefined)
|
|
620
|
+
return storedValue(node.context, reference);
|
|
621
|
+
return nodeBase(node)[key];
|
|
622
|
+
}
|
|
623
|
+
function markDirty(node) {
|
|
624
|
+
if (node.dirty)
|
|
625
|
+
return;
|
|
626
|
+
node.dirty = true;
|
|
627
|
+
node.context.dirty.push(node);
|
|
628
|
+
for (let parent = nodeParent(node); parent !== undefined; parent = nodeParent(parent))
|
|
629
|
+
parent.subtreeDirty = true;
|
|
630
|
+
}
|
|
631
|
+
function arrayOverlay(node) {
|
|
632
|
+
if (node.array !== undefined)
|
|
633
|
+
return node.array;
|
|
634
|
+
const base = nodeBase(node);
|
|
635
|
+
const overlay = {
|
|
636
|
+
root: undefined,
|
|
637
|
+
pieces: undefined,
|
|
638
|
+
baseOverrides: undefined,
|
|
639
|
+
insertOverrides: undefined,
|
|
640
|
+
structural: false,
|
|
641
|
+
generation: 0,
|
|
642
|
+
plan: undefined,
|
|
643
|
+
seed: 0x9e3779b9,
|
|
644
|
+
locatedOffset: 0,
|
|
645
|
+
baseLocations: undefined,
|
|
646
|
+
insertLocations: undefined,
|
|
647
|
+
};
|
|
648
|
+
if (base.length > 0)
|
|
649
|
+
overlay.root = createPieceNode(overlay, { kind: "base", start: 0, length: base.length, step: 1 });
|
|
650
|
+
node.array = overlay;
|
|
651
|
+
return overlay;
|
|
652
|
+
}
|
|
653
|
+
function baseOverridesForWrite(overlay) {
|
|
654
|
+
if (overlay.baseOverrides === undefined)
|
|
655
|
+
overlay.baseOverrides = new Map();
|
|
656
|
+
return overlay.baseOverrides;
|
|
657
|
+
}
|
|
658
|
+
function insertOverridesForWrite(overlay) {
|
|
659
|
+
if (overlay.insertOverrides === undefined)
|
|
660
|
+
overlay.insertOverrides = new Map();
|
|
661
|
+
return overlay.insertOverrides;
|
|
662
|
+
}
|
|
663
|
+
function nextPiecePriority(overlay) {
|
|
664
|
+
let value = overlay.seed;
|
|
665
|
+
value ^= value << 13;
|
|
666
|
+
value ^= value >>> 17;
|
|
667
|
+
value ^= value << 5;
|
|
668
|
+
overlay.seed = value >>> 0;
|
|
669
|
+
return overlay.seed;
|
|
670
|
+
}
|
|
671
|
+
function treeElements(node) {
|
|
672
|
+
return node?.elements ?? 0;
|
|
673
|
+
}
|
|
674
|
+
function updatePieceNode(node) {
|
|
675
|
+
node.elements = treeElements(node.left) + node.piece.length + treeElements(node.right);
|
|
676
|
+
}
|
|
677
|
+
function createPieceNode(overlay, piece) {
|
|
678
|
+
return { piece, left: undefined, right: undefined, priority: nextPiecePriority(overlay), elements: piece.length };
|
|
679
|
+
}
|
|
680
|
+
function mergePieceTrees(left, right) {
|
|
681
|
+
if (left === undefined)
|
|
682
|
+
return right;
|
|
683
|
+
if (right === undefined)
|
|
684
|
+
return left;
|
|
685
|
+
if (left.priority >= right.priority) {
|
|
686
|
+
left.right = mergePieceTrees(left.right, right);
|
|
687
|
+
updatePieceNode(left);
|
|
688
|
+
return left;
|
|
689
|
+
}
|
|
690
|
+
right.left = mergePieceTrees(left, right.left);
|
|
691
|
+
updatePieceNode(right);
|
|
692
|
+
return right;
|
|
693
|
+
}
|
|
694
|
+
function splitPieceTree(overlay, root, index) {
|
|
695
|
+
if (root === undefined)
|
|
696
|
+
return [undefined, undefined];
|
|
697
|
+
const leftLength = treeElements(root.left);
|
|
698
|
+
if (index < leftLength) {
|
|
699
|
+
const [left, right] = splitPieceTree(overlay, root.left, index);
|
|
700
|
+
root.left = right;
|
|
701
|
+
updatePieceNode(root);
|
|
702
|
+
return [left, root];
|
|
703
|
+
}
|
|
704
|
+
const pieceEnd = leftLength + root.piece.length;
|
|
705
|
+
if (index > pieceEnd) {
|
|
706
|
+
const [left, right] = splitPieceTree(overlay, root.right, index - pieceEnd);
|
|
707
|
+
root.right = left;
|
|
708
|
+
updatePieceNode(root);
|
|
709
|
+
return [root, right];
|
|
710
|
+
}
|
|
711
|
+
if (index === leftLength) {
|
|
712
|
+
const left = root.left;
|
|
713
|
+
root.left = undefined;
|
|
714
|
+
updatePieceNode(root);
|
|
715
|
+
return [left, root];
|
|
716
|
+
}
|
|
717
|
+
if (index === pieceEnd) {
|
|
718
|
+
const right = root.right;
|
|
719
|
+
root.right = undefined;
|
|
720
|
+
updatePieceNode(root);
|
|
721
|
+
return [root, right];
|
|
722
|
+
}
|
|
723
|
+
const offset = index - leftLength;
|
|
724
|
+
const first = { ...root.piece, length: offset };
|
|
725
|
+
const second = {
|
|
726
|
+
...root.piece,
|
|
727
|
+
start: root.piece.start + root.piece.step * offset,
|
|
728
|
+
length: root.piece.length - offset,
|
|
729
|
+
};
|
|
730
|
+
return [
|
|
731
|
+
mergePieceTrees(root.left, createPieceNode(overlay, first)),
|
|
732
|
+
mergePieceTrees(createPieceNode(overlay, second), root.right),
|
|
733
|
+
];
|
|
734
|
+
}
|
|
735
|
+
function leftmostPieceNode(node) {
|
|
736
|
+
while (node.left !== undefined)
|
|
737
|
+
node = node.left;
|
|
738
|
+
return node;
|
|
739
|
+
}
|
|
740
|
+
function rightmostPieceNode(node) {
|
|
741
|
+
while (node.right !== undefined)
|
|
742
|
+
node = node.right;
|
|
743
|
+
return node;
|
|
744
|
+
}
|
|
745
|
+
function mergeablePieces(left, right) {
|
|
746
|
+
if (left.kind !== right.kind)
|
|
747
|
+
return false;
|
|
748
|
+
if (left.kind === "insert" && left.source !== right.source)
|
|
749
|
+
return false;
|
|
750
|
+
if (left.length === 1 && right.length === 1)
|
|
751
|
+
return Math.abs(right.start - left.start) === 1;
|
|
752
|
+
return left.step === right.step && left.start + left.step * left.length === right.start;
|
|
753
|
+
}
|
|
754
|
+
function joinNormalized(overlay, left, right) {
|
|
755
|
+
if (left === undefined)
|
|
756
|
+
return right;
|
|
757
|
+
if (right === undefined)
|
|
758
|
+
return left;
|
|
759
|
+
const leftPiece = rightmostPieceNode(left).piece;
|
|
760
|
+
const rightPiece = leftmostPieceNode(right).piece;
|
|
761
|
+
if (!mergeablePieces(leftPiece, rightPiece))
|
|
762
|
+
return mergePieceTrees(left, right);
|
|
763
|
+
const [leftRest] = splitPieceTree(overlay, left, treeElements(left) - leftPiece.length);
|
|
764
|
+
const [, rightRest] = splitPieceTree(overlay, right, rightPiece.length);
|
|
765
|
+
const step = leftPiece.length === 1 ? (rightPiece.start - leftPiece.start) : leftPiece.step;
|
|
766
|
+
const combined = leftPiece.kind === "base"
|
|
767
|
+
? { kind: "base", start: leftPiece.start, length: leftPiece.length + rightPiece.length, step }
|
|
768
|
+
: {
|
|
769
|
+
kind: "insert",
|
|
770
|
+
source: leftPiece.source,
|
|
771
|
+
start: leftPiece.start,
|
|
772
|
+
length: leftPiece.length + rightPiece.length,
|
|
773
|
+
step,
|
|
774
|
+
};
|
|
775
|
+
return joinNormalized(overlay, joinNormalized(overlay, leftRest, createPieceNode(overlay, combined)), rightRest);
|
|
776
|
+
}
|
|
777
|
+
function flattenPieceTree(node, output) {
|
|
778
|
+
if (node === undefined)
|
|
779
|
+
return;
|
|
780
|
+
flattenPieceTree(node.left, output);
|
|
781
|
+
output.push(node.piece);
|
|
782
|
+
flattenPieceTree(node.right, output);
|
|
783
|
+
}
|
|
784
|
+
function piecesOf(overlay) {
|
|
785
|
+
if (overlay.pieces === undefined) {
|
|
786
|
+
overlay.pieces = [];
|
|
787
|
+
flattenPieceTree(overlay.root, overlay.pieces);
|
|
788
|
+
}
|
|
789
|
+
return overlay.pieces;
|
|
790
|
+
}
|
|
791
|
+
function treeFromPieces(overlay, pieces) {
|
|
792
|
+
mergePieces(pieces);
|
|
793
|
+
let root;
|
|
794
|
+
for (const piece of pieces)
|
|
795
|
+
root = mergePieceTrees(root, createPieceNode(overlay, piece));
|
|
796
|
+
return root;
|
|
797
|
+
}
|
|
798
|
+
function replaceAllPieces(overlay, pieces) {
|
|
799
|
+
overlay.root = treeFromPieces(overlay, pieces);
|
|
800
|
+
overlay.pieces = undefined;
|
|
801
|
+
overlay.baseLocations = undefined;
|
|
802
|
+
overlay.insertLocations = undefined;
|
|
803
|
+
}
|
|
804
|
+
function arrayLength(overlay) {
|
|
805
|
+
return treeElements(overlay.root);
|
|
806
|
+
}
|
|
807
|
+
function locatePiece(overlay, index) {
|
|
808
|
+
let node = overlay.root;
|
|
809
|
+
while (node !== undefined) {
|
|
810
|
+
const leftLength = treeElements(node.left);
|
|
811
|
+
if (index < leftLength)
|
|
812
|
+
node = node.left;
|
|
813
|
+
else if (index >= leftLength + node.piece.length) {
|
|
814
|
+
index -= leftLength + node.piece.length;
|
|
815
|
+
node = node.right;
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
overlay.locatedOffset = index - leftLength;
|
|
819
|
+
return node.piece;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
throw new RangeError("Array overlay index is out of range");
|
|
823
|
+
}
|
|
824
|
+
function arrayIndex(property) {
|
|
825
|
+
if (typeof property !== "string" || property.length === 0 || property.length > 10)
|
|
826
|
+
return undefined;
|
|
827
|
+
if (property === "0")
|
|
828
|
+
return 0;
|
|
829
|
+
const first = property.charCodeAt(0);
|
|
830
|
+
if (first < 49 || first > 57)
|
|
831
|
+
return undefined;
|
|
832
|
+
let index = first - 48;
|
|
833
|
+
for (let offset = 1; offset < property.length; offset++) {
|
|
834
|
+
const digit = property.charCodeAt(offset) - 48;
|
|
835
|
+
if (digit < 0 || digit > 9)
|
|
836
|
+
return undefined;
|
|
837
|
+
index = index * 10 + digit;
|
|
838
|
+
if (index >= 4_294_967_295)
|
|
839
|
+
return undefined;
|
|
840
|
+
}
|
|
841
|
+
return index;
|
|
842
|
+
}
|
|
843
|
+
function entryValueAt(node, piece, sourceIndex) {
|
|
844
|
+
const overlay = arrayOverlay(node);
|
|
845
|
+
if (piece.kind === "base") {
|
|
846
|
+
const valueIndex = overlay.baseOverrides?.get(sourceIndex);
|
|
847
|
+
return valueIndex === undefined
|
|
848
|
+
? nodeBase(node)[sourceIndex]
|
|
849
|
+
: storedValue(node.context, valueIndex);
|
|
850
|
+
}
|
|
851
|
+
const overrides = overlay.insertOverrides?.get(piece.source);
|
|
852
|
+
const reference = overrides?.has(sourceIndex) ? overrides.get(sourceIndex) : piece.source.refs[sourceIndex];
|
|
853
|
+
return storedValue(node.context, reference);
|
|
854
|
+
}
|
|
855
|
+
function hasEntryOverrideAt(overlay, piece, sourceIndex) {
|
|
856
|
+
return piece.kind === "base"
|
|
857
|
+
? (overlay.baseOverrides?.has(sourceIndex) ?? false)
|
|
858
|
+
: (overlay.insertOverrides?.get(piece.source)?.has(sourceIndex) ?? false);
|
|
859
|
+
}
|
|
860
|
+
function extendRightmostPiece(node, amount) {
|
|
861
|
+
if (node.right !== undefined)
|
|
862
|
+
extendRightmostPiece(node.right, amount);
|
|
863
|
+
else
|
|
864
|
+
node.piece.length += amount;
|
|
865
|
+
updatePieceNode(node);
|
|
866
|
+
}
|
|
867
|
+
function invalidatePieceCaches(overlay) {
|
|
868
|
+
overlay.pieces = undefined;
|
|
869
|
+
overlay.baseLocations = undefined;
|
|
870
|
+
overlay.insertLocations = undefined;
|
|
871
|
+
overlay.plan = undefined;
|
|
872
|
+
}
|
|
873
|
+
function replacePieceRange(node, index, remove, inserted) {
|
|
874
|
+
if (remove === 0 && inserted.length === 0)
|
|
875
|
+
return;
|
|
876
|
+
const overlay = arrayOverlay(node);
|
|
877
|
+
if (remove === 0 && index === arrayLength(overlay) && inserted.length === 1 && overlay.root !== undefined) {
|
|
878
|
+
const addition = inserted[0];
|
|
879
|
+
const tail = rightmostPieceNode(overlay.root).piece;
|
|
880
|
+
if (addition.kind === "insert" &&
|
|
881
|
+
tail.kind === "insert" &&
|
|
882
|
+
tail.step === 1 &&
|
|
883
|
+
tail.start + tail.length === tail.source.refs.length) {
|
|
884
|
+
for (let offset = 0; offset < addition.length; offset++) {
|
|
885
|
+
tail.source.refs.push(addition.source.refs[addition.start + offset]);
|
|
886
|
+
}
|
|
887
|
+
extendRightmostPiece(overlay.root, addition.length);
|
|
888
|
+
invalidatePieceCaches(overlay);
|
|
889
|
+
overlay.structural = true;
|
|
890
|
+
overlay.generation += 1;
|
|
891
|
+
markDirty(node);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
const [left, rest] = splitPieceTree(overlay, overlay.root, index);
|
|
896
|
+
const [, right] = splitPieceTree(overlay, rest, remove);
|
|
897
|
+
const middle = treeFromPieces(overlay, inserted);
|
|
898
|
+
overlay.root = joinNormalized(overlay, joinNormalized(overlay, left, middle), right);
|
|
899
|
+
invalidatePieceCaches(overlay);
|
|
900
|
+
overlay.structural = true;
|
|
901
|
+
overlay.generation += 1;
|
|
902
|
+
markDirty(node);
|
|
903
|
+
}
|
|
904
|
+
function mergePieces(pieces) {
|
|
905
|
+
for (let index = 1; index < pieces.length;) {
|
|
906
|
+
const left = pieces[index - 1];
|
|
907
|
+
const right = pieces[index];
|
|
908
|
+
const sameSource = left.kind === right.kind && (left.kind === "base" || left.source === right.source);
|
|
909
|
+
if (sameSource && left.length === 1 && right.length === 1 && Math.abs(right.start - left.start) === 1) {
|
|
910
|
+
left.step = (right.start - left.start);
|
|
911
|
+
left.length = 2;
|
|
912
|
+
pieces.splice(index, 1);
|
|
913
|
+
}
|
|
914
|
+
else if (sameSource && left.step === right.step && left.start + left.step * left.length === right.start) {
|
|
915
|
+
left.length += right.length;
|
|
916
|
+
pieces.splice(index, 1);
|
|
917
|
+
}
|
|
918
|
+
else
|
|
919
|
+
index += 1;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
function insertPiece(node, items, start = 0) {
|
|
923
|
+
if (items.length === start)
|
|
924
|
+
return [];
|
|
925
|
+
const refs = [];
|
|
926
|
+
for (let index = start; index < items.length; index++)
|
|
927
|
+
refs.push(storeValue(node.context, items[index]));
|
|
928
|
+
return [{ kind: "insert", source: { refs }, start: 0, length: refs.length, step: 1 }];
|
|
929
|
+
}
|
|
930
|
+
function insertPlacementPiece(node, values, start = 0) {
|
|
931
|
+
if (values.length === start)
|
|
932
|
+
return [];
|
|
933
|
+
const storedLength = node.context.stored.length;
|
|
934
|
+
try {
|
|
935
|
+
for (let index = start; index < values.length; index++)
|
|
936
|
+
values[index] = storeValue(node.context, clonePlacement(values[index]));
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
node.context.stored.length = storedLength;
|
|
940
|
+
throw error;
|
|
941
|
+
}
|
|
942
|
+
return [
|
|
943
|
+
{
|
|
944
|
+
kind: "insert",
|
|
945
|
+
source: { refs: values },
|
|
946
|
+
start,
|
|
947
|
+
length: values.length - start,
|
|
948
|
+
step: 1,
|
|
949
|
+
},
|
|
950
|
+
];
|
|
951
|
+
}
|
|
952
|
+
function setArrayIndex(node, index, stored) {
|
|
953
|
+
const overlay = arrayOverlay(node);
|
|
954
|
+
const length = arrayLength(overlay);
|
|
955
|
+
if (index === length) {
|
|
956
|
+
replacePieceRange(node, length, 0, insertPiece(node, [stored]));
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
const piece = locatePiece(overlay, index);
|
|
960
|
+
const sourceIndex = piece.start + piece.step * overlay.locatedOffset;
|
|
961
|
+
const current = entryValueAt(node, piece, sourceIndex);
|
|
962
|
+
if (!isContainer(stored) && current === stored)
|
|
963
|
+
return;
|
|
964
|
+
if (piece.kind === "base") {
|
|
965
|
+
const existingIndex = overlay.baseOverrides?.get(sourceIndex);
|
|
966
|
+
if (!isContainer(stored) && stored === nodeBase(node)[sourceIndex]) {
|
|
967
|
+
if (existingIndex !== undefined)
|
|
968
|
+
releaseStoredValue(node.context, existingIndex);
|
|
969
|
+
overlay.baseOverrides?.delete(sourceIndex);
|
|
970
|
+
}
|
|
971
|
+
else if (existingIndex === undefined) {
|
|
972
|
+
baseOverridesForWrite(overlay).set(sourceIndex, storeValue(node.context, stored));
|
|
973
|
+
}
|
|
974
|
+
else
|
|
975
|
+
overlay.baseOverrides.set(sourceIndex, replaceStoredValue(node.context, existingIndex, stored));
|
|
976
|
+
}
|
|
977
|
+
else {
|
|
978
|
+
let overrides = overlay.insertOverrides?.get(piece.source);
|
|
979
|
+
const existingIndex = overrides?.get(sourceIndex);
|
|
980
|
+
if (!isContainer(stored) && stored === storedValue(node.context, piece.source.refs[sourceIndex])) {
|
|
981
|
+
if (existingIndex !== undefined)
|
|
982
|
+
releaseStoredValue(node.context, existingIndex);
|
|
983
|
+
overrides?.delete(sourceIndex);
|
|
984
|
+
if (overrides?.size === 0)
|
|
985
|
+
overlay.insertOverrides?.delete(piece.source);
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
if (overrides === undefined) {
|
|
989
|
+
overrides = new Map();
|
|
990
|
+
insertOverridesForWrite(overlay).set(piece.source, overrides);
|
|
991
|
+
}
|
|
992
|
+
if (existingIndex === undefined)
|
|
993
|
+
overrides.set(sourceIndex, storeValue(node.context, stored));
|
|
994
|
+
else
|
|
995
|
+
overrides.set(sourceIndex, replaceStoredValue(node.context, existingIndex, stored));
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
markDirty(node);
|
|
999
|
+
}
|
|
1000
|
+
function setArrayLength(node, next) {
|
|
1001
|
+
const current = arrayLength(arrayOverlay(node));
|
|
1002
|
+
if (next === current)
|
|
1003
|
+
return;
|
|
1004
|
+
if (next < current)
|
|
1005
|
+
replacePieceRange(node, next, current - next, []);
|
|
1006
|
+
else
|
|
1007
|
+
replacePieceRange(node, current, 0, insertPiece(node, Array.from({ length: next - current }, () => null)));
|
|
1008
|
+
}
|
|
1009
|
+
function toArrayLength(value) {
|
|
1010
|
+
const number = Number(value);
|
|
1011
|
+
if (!Number.isInteger(number) || number < 0 || number >= 4_294_967_296)
|
|
1012
|
+
throw new RangeError("Invalid array length");
|
|
1013
|
+
return number;
|
|
1014
|
+
}
|
|
1015
|
+
function toIntegerOrInfinity(value) {
|
|
1016
|
+
const number = Number(value);
|
|
1017
|
+
if (Number.isNaN(number) || number === 0)
|
|
1018
|
+
return 0;
|
|
1019
|
+
return Number.isFinite(number) ? Math.trunc(number) : number;
|
|
1020
|
+
}
|
|
1021
|
+
function clampIndex(value, length) {
|
|
1022
|
+
if (value === Number.NEGATIVE_INFINITY)
|
|
1023
|
+
return 0;
|
|
1024
|
+
if (value < 0)
|
|
1025
|
+
return Math.max(length + value, 0);
|
|
1026
|
+
return Math.min(value, length);
|
|
1027
|
+
}
|
|
1028
|
+
function mutatorNode(receiver) {
|
|
1029
|
+
if (!isContainer(receiver))
|
|
1030
|
+
return undefined;
|
|
1031
|
+
const node = Reflect.get(receiver, NODE);
|
|
1032
|
+
if (node === undefined)
|
|
1033
|
+
return undefined;
|
|
1034
|
+
if (!Array.isArray(nodeBase(node)))
|
|
1035
|
+
throw new TypeError("Array mutator called on incompatible receiver");
|
|
1036
|
+
assertWritable(node.context);
|
|
1037
|
+
return node;
|
|
1038
|
+
}
|
|
1039
|
+
const arrayMutators = {
|
|
1040
|
+
push(...items) {
|
|
1041
|
+
const node = mutatorNode(this);
|
|
1042
|
+
if (node === undefined)
|
|
1043
|
+
return Reflect.apply(Array.prototype.push, this, items);
|
|
1044
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1045
|
+
replacePieceRange(node, length, 0, insertPlacementPiece(node, items));
|
|
1046
|
+
return length + items.length;
|
|
1047
|
+
},
|
|
1048
|
+
pop() {
|
|
1049
|
+
const node = mutatorNode(this);
|
|
1050
|
+
if (node === undefined)
|
|
1051
|
+
return Reflect.apply(Array.prototype.pop, this, []);
|
|
1052
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1053
|
+
if (length === 0)
|
|
1054
|
+
return undefined;
|
|
1055
|
+
const value = getArrayIndex(node, length - 1);
|
|
1056
|
+
replacePieceRange(node, length - 1, 1, []);
|
|
1057
|
+
return value;
|
|
1058
|
+
},
|
|
1059
|
+
shift() {
|
|
1060
|
+
const node = mutatorNode(this);
|
|
1061
|
+
if (node === undefined)
|
|
1062
|
+
return Reflect.apply(Array.prototype.shift, this, []);
|
|
1063
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1064
|
+
if (length === 0)
|
|
1065
|
+
return undefined;
|
|
1066
|
+
const value = getArrayIndex(node, 0);
|
|
1067
|
+
replacePieceRange(node, 0, 1, []);
|
|
1068
|
+
return value;
|
|
1069
|
+
},
|
|
1070
|
+
unshift(...items) {
|
|
1071
|
+
const node = mutatorNode(this);
|
|
1072
|
+
if (node === undefined)
|
|
1073
|
+
return Reflect.apply(Array.prototype.unshift, this, items);
|
|
1074
|
+
replacePieceRange(node, 0, 0, insertPlacementPiece(node, items));
|
|
1075
|
+
return arrayLength(arrayOverlay(node));
|
|
1076
|
+
},
|
|
1077
|
+
splice(...args) {
|
|
1078
|
+
const node = mutatorNode(this);
|
|
1079
|
+
if (node === undefined)
|
|
1080
|
+
return Reflect.apply(Array.prototype.splice, this, args);
|
|
1081
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1082
|
+
const start = args.length === 0 ? 0 : clampIndex(toIntegerOrInfinity(args[0]), length);
|
|
1083
|
+
const remove = args.length === 0
|
|
1084
|
+
? 0
|
|
1085
|
+
: args.length === 1
|
|
1086
|
+
? length - start
|
|
1087
|
+
: Math.min(Math.max(toIntegerOrInfinity(args[1]), 0), length - start);
|
|
1088
|
+
const removed = Array.from({ length: remove }, (_, offset) => getArrayIndex(node, start + offset));
|
|
1089
|
+
const itemStart = Math.min(2, args.length);
|
|
1090
|
+
const itemCount = Math.max(args.length - 2, 0);
|
|
1091
|
+
replacePieceRange(node, start, remove, insertPlacementPiece(node, args, itemStart));
|
|
1092
|
+
setArrayLength(node, length - remove + itemCount);
|
|
1093
|
+
return removed;
|
|
1094
|
+
},
|
|
1095
|
+
reverse() {
|
|
1096
|
+
const node = mutatorNode(this);
|
|
1097
|
+
if (node === undefined)
|
|
1098
|
+
return Reflect.apply(Array.prototype.reverse, this, []);
|
|
1099
|
+
const overlay = arrayOverlay(node);
|
|
1100
|
+
if (arrayLength(overlay) < 2)
|
|
1101
|
+
return node.proxy;
|
|
1102
|
+
const pieces = [...piecesOf(overlay)].reverse();
|
|
1103
|
+
for (const piece of pieces) {
|
|
1104
|
+
piece.start += piece.step * (piece.length - 1);
|
|
1105
|
+
piece.step = piece.step === 1 ? -1 : 1;
|
|
1106
|
+
}
|
|
1107
|
+
replaceAllPieces(overlay, pieces);
|
|
1108
|
+
overlay.structural = true;
|
|
1109
|
+
overlay.generation += 1;
|
|
1110
|
+
overlay.plan = undefined;
|
|
1111
|
+
markDirty(node);
|
|
1112
|
+
return node.proxy;
|
|
1113
|
+
},
|
|
1114
|
+
sort(comparator) {
|
|
1115
|
+
const node = mutatorNode(this);
|
|
1116
|
+
if (node === undefined)
|
|
1117
|
+
return Reflect.apply(Array.prototype.sort, this, [comparator]);
|
|
1118
|
+
if (comparator !== undefined && typeof comparator !== "function")
|
|
1119
|
+
throw new TypeError("Comparator must be a function");
|
|
1120
|
+
const overlay = arrayOverlay(node);
|
|
1121
|
+
const insertedSources = [];
|
|
1122
|
+
const insertedIndices = [];
|
|
1123
|
+
const insertedValues = [];
|
|
1124
|
+
const baseValues = new Array(nodeBase(node).length);
|
|
1125
|
+
const order = [];
|
|
1126
|
+
for (const piece of piecesOf(overlay)) {
|
|
1127
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1128
|
+
const sourceIndex = piece.start + piece.step * offset;
|
|
1129
|
+
if (piece.kind === "base") {
|
|
1130
|
+
order.push(sourceIndex);
|
|
1131
|
+
baseValues[sourceIndex] = publicSortValue(node, sourceIndex, insertedSources, insertedIndices);
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
insertedSources.push(piece.source);
|
|
1135
|
+
insertedIndices.push(sourceIndex);
|
|
1136
|
+
const token = -insertedSources.length;
|
|
1137
|
+
order.push(token);
|
|
1138
|
+
insertedValues.push(publicSortValue(node, token, insertedSources, insertedIndices));
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
const baseSnapshot = new Map();
|
|
1143
|
+
for (const [index, valueIndex] of overlay.baseOverrides ?? [])
|
|
1144
|
+
baseSnapshot.set(index, storedValue(node.context, valueIndex));
|
|
1145
|
+
const insertSnapshots = new Map();
|
|
1146
|
+
for (const [source, overrides] of overlay.insertOverrides ?? []) {
|
|
1147
|
+
const snapshot = new Map();
|
|
1148
|
+
for (const [index, valueIndex] of overrides)
|
|
1149
|
+
snapshot.set(index, storedValue(node.context, valueIndex));
|
|
1150
|
+
insertSnapshots.set(source, snapshot);
|
|
1151
|
+
}
|
|
1152
|
+
const generation = overlay.generation;
|
|
1153
|
+
order.sort((left, right) => {
|
|
1154
|
+
const leftValue = left < 0 ? insertedValues[-left - 1] : baseValues[left];
|
|
1155
|
+
const rightValue = right < 0 ? insertedValues[-right - 1] : baseValues[right];
|
|
1156
|
+
if (typeof comparator === "function")
|
|
1157
|
+
return Number(comparator(leftValue, rightValue));
|
|
1158
|
+
const a = String(leftValue);
|
|
1159
|
+
const b = String(rightValue);
|
|
1160
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1161
|
+
});
|
|
1162
|
+
if (baseSnapshot.size > 0 ||
|
|
1163
|
+
insertSnapshots.size > 0 ||
|
|
1164
|
+
(overlay.baseOverrides?.size ?? 0) > 0 ||
|
|
1165
|
+
(overlay.insertOverrides?.size ?? 0) > 0) {
|
|
1166
|
+
for (const token of order) {
|
|
1167
|
+
restoreSortOverride(node, token, insertedSources, insertedIndices, baseSnapshot, insertSnapshots);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
const comparatorWasStructural = overlay.generation !== generation;
|
|
1171
|
+
const currentLength = arrayLength(overlay);
|
|
1172
|
+
const samePrefix = currentLength >= order.length &&
|
|
1173
|
+
order.every((token, index) => sameSortTokenAt(overlay, index, token, insertedSources, insertedIndices));
|
|
1174
|
+
if (!samePrefix) {
|
|
1175
|
+
replacePieceRange(node, 0, Math.min(order.length, currentLength), piecesFromSortOrder(order, insertedSources, insertedIndices));
|
|
1176
|
+
}
|
|
1177
|
+
if (comparatorWasStructural)
|
|
1178
|
+
deduplicateArrayEntries(node);
|
|
1179
|
+
return node.proxy;
|
|
1180
|
+
},
|
|
1181
|
+
fill(supplied, startArg, endArg) {
|
|
1182
|
+
const node = mutatorNode(this);
|
|
1183
|
+
if (node === undefined)
|
|
1184
|
+
return Reflect.apply(Array.prototype.fill, this, [supplied, startArg, endArg]);
|
|
1185
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1186
|
+
const start = startArg === undefined ? 0 : clampIndex(toIntegerOrInfinity(startArg), length);
|
|
1187
|
+
const end = endArg === undefined ? length : clampIndex(toIntegerOrInfinity(endArg), length);
|
|
1188
|
+
if (end <= start)
|
|
1189
|
+
return node.proxy;
|
|
1190
|
+
const items = Array.from({ length: end - start }, () => clonePlacement(supplied));
|
|
1191
|
+
replacePieceRange(node, start, end - start, insertPiece(node, items));
|
|
1192
|
+
return node.proxy;
|
|
1193
|
+
},
|
|
1194
|
+
copyWithin(targetArg, startArg, endArg) {
|
|
1195
|
+
const node = mutatorNode(this);
|
|
1196
|
+
if (node === undefined)
|
|
1197
|
+
return Reflect.apply(Array.prototype.copyWithin, this, [targetArg, startArg, endArg]);
|
|
1198
|
+
const length = arrayLength(arrayOverlay(node));
|
|
1199
|
+
const target = clampIndex(toIntegerOrInfinity(targetArg), length);
|
|
1200
|
+
const start = clampIndex(toIntegerOrInfinity(startArg), length);
|
|
1201
|
+
const end = endArg === undefined ? length : clampIndex(toIntegerOrInfinity(endArg), length);
|
|
1202
|
+
const count = Math.min(Math.max(end - start, 0), length - target);
|
|
1203
|
+
const values = Array.from({ length: count }, (_, offset) => clonePlacement(getArrayIndex(node, start + offset)));
|
|
1204
|
+
replacePieceRange(node, target, count, insertPiece(node, values));
|
|
1205
|
+
return node.proxy;
|
|
1206
|
+
},
|
|
1207
|
+
};
|
|
1208
|
+
function sortTokenSource(token, sources) {
|
|
1209
|
+
return token < 0 ? sources[-token - 1] : undefined;
|
|
1210
|
+
}
|
|
1211
|
+
function sortTokenIndex(token, indices) {
|
|
1212
|
+
return token < 0 ? indices[-token - 1] : token;
|
|
1213
|
+
}
|
|
1214
|
+
function publicSortValue(node, token, sources, indices) {
|
|
1215
|
+
const overlay = arrayOverlay(node);
|
|
1216
|
+
const source = sortTokenSource(token, sources);
|
|
1217
|
+
const sourceIndex = sortTokenIndex(token, indices);
|
|
1218
|
+
const overrideIndex = source === undefined
|
|
1219
|
+
? overlay.baseOverrides?.get(sourceIndex)
|
|
1220
|
+
: overlay.insertOverrides?.get(source)?.get(sourceIndex);
|
|
1221
|
+
const value = overrideIndex !== undefined
|
|
1222
|
+
? storedValue(node.context, overrideIndex)
|
|
1223
|
+
: source === undefined
|
|
1224
|
+
? nodeBase(node)[sourceIndex]
|
|
1225
|
+
: storedValue(node.context, source.refs[sourceIndex]);
|
|
1226
|
+
if (!isContainer(value))
|
|
1227
|
+
return value;
|
|
1228
|
+
return createNode(node.context, value, node, source === undefined ? 1 : 2, sourceIndex, source, source !== undefined || (overlay.baseOverrides?.has(sourceIndex) ?? false)).proxy;
|
|
1229
|
+
}
|
|
1230
|
+
function restoreSortOverride(node, token, sources, indices, baseSnapshot, insertSnapshots) {
|
|
1231
|
+
const overlay = arrayOverlay(node);
|
|
1232
|
+
const source = sortTokenSource(token, sources);
|
|
1233
|
+
const sourceIndex = sortTokenIndex(token, indices);
|
|
1234
|
+
if (source === undefined) {
|
|
1235
|
+
const existingIndex = overlay.baseOverrides?.get(sourceIndex);
|
|
1236
|
+
const snapshotValue = baseSnapshot.get(sourceIndex);
|
|
1237
|
+
if (snapshotValue !== undefined) {
|
|
1238
|
+
if (existingIndex === undefined)
|
|
1239
|
+
baseOverridesForWrite(overlay).set(sourceIndex, storeValue(node.context, snapshotValue));
|
|
1240
|
+
else
|
|
1241
|
+
overlay.baseOverrides.set(sourceIndex, replaceStoredValue(node.context, existingIndex, snapshotValue));
|
|
1242
|
+
}
|
|
1243
|
+
else {
|
|
1244
|
+
if (existingIndex !== undefined)
|
|
1245
|
+
releaseStoredValue(node.context, existingIndex);
|
|
1246
|
+
overlay.baseOverrides?.delete(sourceIndex);
|
|
1247
|
+
}
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
let overrides = overlay.insertOverrides?.get(source);
|
|
1251
|
+
const existingIndex = overrides?.get(sourceIndex);
|
|
1252
|
+
const snapshotValue = insertSnapshots.get(source)?.get(sourceIndex);
|
|
1253
|
+
if (snapshotValue !== undefined) {
|
|
1254
|
+
if (overrides === undefined) {
|
|
1255
|
+
overrides = new Map();
|
|
1256
|
+
insertOverridesForWrite(overlay).set(source, overrides);
|
|
1257
|
+
}
|
|
1258
|
+
if (existingIndex === undefined)
|
|
1259
|
+
overrides.set(sourceIndex, storeValue(node.context, snapshotValue));
|
|
1260
|
+
else
|
|
1261
|
+
overrides.set(sourceIndex, replaceStoredValue(node.context, existingIndex, snapshotValue));
|
|
1262
|
+
}
|
|
1263
|
+
else {
|
|
1264
|
+
if (existingIndex !== undefined)
|
|
1265
|
+
releaseStoredValue(node.context, existingIndex);
|
|
1266
|
+
overrides?.delete(sourceIndex);
|
|
1267
|
+
if (overrides?.size === 0)
|
|
1268
|
+
overlay.insertOverrides?.delete(source);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
function sameSortTokenAt(overlay, logicalIndex, token, sources, indices) {
|
|
1272
|
+
const piece = locatePiece(overlay, logicalIndex);
|
|
1273
|
+
const sourceIndex = piece.start + piece.step * overlay.locatedOffset;
|
|
1274
|
+
const tokenSource = sortTokenSource(token, sources);
|
|
1275
|
+
return (sourceIndex === sortTokenIndex(token, indices) &&
|
|
1276
|
+
((piece.kind === "base" && tokenSource === undefined) ||
|
|
1277
|
+
(piece.kind === "insert" && piece.source === tokenSource)));
|
|
1278
|
+
}
|
|
1279
|
+
function deduplicateArrayEntries(node) {
|
|
1280
|
+
const overlay = arrayOverlay(node);
|
|
1281
|
+
const seenBase = new Set();
|
|
1282
|
+
const seenInsert = new Map();
|
|
1283
|
+
const next = [];
|
|
1284
|
+
let duplicated = false;
|
|
1285
|
+
for (const piece of piecesOf(overlay)) {
|
|
1286
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1287
|
+
const sourceIndex = piece.start + piece.step * offset;
|
|
1288
|
+
let seen;
|
|
1289
|
+
if (piece.kind === "base") {
|
|
1290
|
+
seen = seenBase.has(sourceIndex);
|
|
1291
|
+
seenBase.add(sourceIndex);
|
|
1292
|
+
}
|
|
1293
|
+
else {
|
|
1294
|
+
let indices = seenInsert.get(piece.source);
|
|
1295
|
+
if (indices === undefined) {
|
|
1296
|
+
indices = new Set();
|
|
1297
|
+
seenInsert.set(piece.source, indices);
|
|
1298
|
+
}
|
|
1299
|
+
seen = indices.has(sourceIndex);
|
|
1300
|
+
indices.add(sourceIndex);
|
|
1301
|
+
}
|
|
1302
|
+
if (seen) {
|
|
1303
|
+
duplicated = true;
|
|
1304
|
+
appendMergedPiece(next, insertPiece(node, [clonePlacementStored(entryValueAt(node, piece, sourceIndex), node.context)])[0]);
|
|
1305
|
+
}
|
|
1306
|
+
else
|
|
1307
|
+
appendMergedPiece(next, singletonPiece(piece, sourceIndex));
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
if (!duplicated)
|
|
1311
|
+
return;
|
|
1312
|
+
replaceAllPieces(overlay, next);
|
|
1313
|
+
overlay.structural = true;
|
|
1314
|
+
overlay.generation += 1;
|
|
1315
|
+
overlay.plan = undefined;
|
|
1316
|
+
markDirty(node);
|
|
1317
|
+
}
|
|
1318
|
+
function piecesFromSortOrder(order, sources, indices) {
|
|
1319
|
+
const pieces = [];
|
|
1320
|
+
for (const token of order) {
|
|
1321
|
+
const source = token < 0 ? sources[-token - 1] : undefined;
|
|
1322
|
+
const sourceIndex = token < 0 ? indices[-token - 1] : token;
|
|
1323
|
+
const previous = pieces.at(-1);
|
|
1324
|
+
const sameSource = previous !== undefined &&
|
|
1325
|
+
((source === undefined && previous.kind === "base") ||
|
|
1326
|
+
(source !== undefined && previous.kind === "insert" && previous.source === source));
|
|
1327
|
+
if (previous !== undefined && sameSource) {
|
|
1328
|
+
if (previous.length === 1) {
|
|
1329
|
+
const step = sourceIndex - previous.start;
|
|
1330
|
+
if (step === 1 || step === -1) {
|
|
1331
|
+
previous.step = step;
|
|
1332
|
+
previous.length = 2;
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
else if (previous.start + previous.step * previous.length === sourceIndex) {
|
|
1337
|
+
previous.length += 1;
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
pieces.push(source === undefined
|
|
1342
|
+
? { kind: "base", start: sourceIndex, length: 1, step: 1 }
|
|
1343
|
+
: { kind: "insert", source, start: sourceIndex, length: 1, step: 1 });
|
|
1344
|
+
}
|
|
1345
|
+
return pieces;
|
|
1346
|
+
}
|
|
1347
|
+
function appendMergedPiece(pieces, piece) {
|
|
1348
|
+
const previous = pieces.at(-1);
|
|
1349
|
+
const sameSource = previous !== undefined &&
|
|
1350
|
+
previous.kind === piece.kind &&
|
|
1351
|
+
(previous.kind === "base" || previous.source === piece.source);
|
|
1352
|
+
if (previous !== undefined && sameSource && previous.length === 1 && piece.length === 1) {
|
|
1353
|
+
const step = piece.start - previous.start;
|
|
1354
|
+
if (step === 1 || step === -1) {
|
|
1355
|
+
previous.step = step;
|
|
1356
|
+
previous.length = 2;
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
if (previous !== undefined &&
|
|
1361
|
+
sameSource &&
|
|
1362
|
+
previous.step === piece.step &&
|
|
1363
|
+
previous.start + previous.step * previous.length === piece.start) {
|
|
1364
|
+
previous.length += piece.length;
|
|
1365
|
+
}
|
|
1366
|
+
else
|
|
1367
|
+
pieces.push(piece);
|
|
1368
|
+
}
|
|
1369
|
+
function singletonPiece(piece, sourceIndex) {
|
|
1370
|
+
return piece.kind === "base"
|
|
1371
|
+
? { kind: "base", start: sourceIndex, length: 1, step: 1 }
|
|
1372
|
+
: { kind: "insert", source: piece.source, start: sourceIndex, length: 1, step: 1 };
|
|
1373
|
+
}
|
|
1374
|
+
function clonePlacement(value) {
|
|
1375
|
+
const proxyNode = isContainer(value) ? Reflect.get(value, NODE) : undefined;
|
|
1376
|
+
return proxyNode === undefined ? copyJson(value) : clonePlacementNode(proxyNode);
|
|
1377
|
+
}
|
|
1378
|
+
function cloneNode(node) {
|
|
1379
|
+
assertReadable(node.context);
|
|
1380
|
+
if (Array.isArray(nodeBase(node))) {
|
|
1381
|
+
const overlay = arrayOverlay(node);
|
|
1382
|
+
const result = [];
|
|
1383
|
+
for (const piece of piecesOf(overlay)) {
|
|
1384
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1385
|
+
const sourceIndex = piece.start + piece.step * offset;
|
|
1386
|
+
result.push(cloneStored(entryValueAt(node, piece, sourceIndex), node.context));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return result;
|
|
1390
|
+
}
|
|
1391
|
+
const result = Object.create(Object.getPrototypeOf(nodeBase(node)));
|
|
1392
|
+
for (const key of ownKeys(node))
|
|
1393
|
+
defineData(result, key, cloneStored(objectValue(node, key), node.context));
|
|
1394
|
+
return result;
|
|
1395
|
+
}
|
|
1396
|
+
function cloneStored(value, context) {
|
|
1397
|
+
if (!isContainer(value))
|
|
1398
|
+
return value;
|
|
1399
|
+
const node = context.rawNodes.get(value);
|
|
1400
|
+
return node === undefined || (!node.dirty && !node.subtreeDirty) ? value : cloneNode(node);
|
|
1401
|
+
}
|
|
1402
|
+
function clonePlacementNode(node) {
|
|
1403
|
+
assertReadable(node.context);
|
|
1404
|
+
if (Array.isArray(nodeBase(node))) {
|
|
1405
|
+
const overlay = arrayOverlay(node);
|
|
1406
|
+
const result = [];
|
|
1407
|
+
for (const piece of piecesOf(overlay)) {
|
|
1408
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1409
|
+
const sourceIndex = piece.start + piece.step * offset;
|
|
1410
|
+
result.push(clonePlacementStored(entryValueAt(node, piece, sourceIndex), node.context));
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return result;
|
|
1414
|
+
}
|
|
1415
|
+
const result = Object.create(Object.getPrototypeOf(nodeBase(node)));
|
|
1416
|
+
for (const key of ownKeys(node))
|
|
1417
|
+
defineData(result, key, clonePlacementStored(objectValue(node, key), node.context));
|
|
1418
|
+
return result;
|
|
1419
|
+
}
|
|
1420
|
+
function clonePlacementStored(value, context) {
|
|
1421
|
+
if (!isContainer(value))
|
|
1422
|
+
return copyJson(value);
|
|
1423
|
+
const node = context.rawNodes.get(value);
|
|
1424
|
+
return node === undefined ? copyJson(value) : clonePlacementNode(node);
|
|
1425
|
+
}
|
|
1426
|
+
function defineData(target, key, value) {
|
|
1427
|
+
DATA_DESCRIPTOR.value = value;
|
|
1428
|
+
Object.defineProperty(target, key, DATA_DESCRIPTOR);
|
|
1429
|
+
DATA_DESCRIPTOR.value = undefined;
|
|
1430
|
+
}
|
|
1431
|
+
function emitOperations(context) {
|
|
1432
|
+
const simple = emitSimpleObjectOperations(context);
|
|
1433
|
+
if (simple !== undefined)
|
|
1434
|
+
return simple;
|
|
1435
|
+
const operations = [];
|
|
1436
|
+
const forcedFolds = new Map();
|
|
1437
|
+
const emissionPaths = new Map();
|
|
1438
|
+
const denseIndices = new Map();
|
|
1439
|
+
for (const node of context.dirty) {
|
|
1440
|
+
recordDenseArrayPosition(node, denseIndices);
|
|
1441
|
+
if (Array.isArray(nodeBase(node))) {
|
|
1442
|
+
const overlay = arrayOverlay(node);
|
|
1443
|
+
if (!overlay.structural && (overlay.baseOverrides?.size ?? 0) > 0) {
|
|
1444
|
+
let candidates = denseIndices.get(node);
|
|
1445
|
+
if (candidates === undefined) {
|
|
1446
|
+
candidates = { indices: [], bits: undefined, length: nodeBase(node).length };
|
|
1447
|
+
denseIndices.set(node, candidates);
|
|
1448
|
+
}
|
|
1449
|
+
for (const index of overlay.baseOverrides.keys())
|
|
1450
|
+
addDenseCandidate(candidates, index);
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
const denseRegions = new Map();
|
|
1455
|
+
for (const [array, candidates] of denseIndices) {
|
|
1456
|
+
const path = resolvePath(array);
|
|
1457
|
+
if (path === undefined || path.some((segment) => typeof segment === "string" && RESERVED_SEGMENTS.has(segment))) {
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
const regions = buildDenseRegions(candidates);
|
|
1461
|
+
if (regions.length > 0)
|
|
1462
|
+
denseRegions.set(array, regions);
|
|
1463
|
+
}
|
|
1464
|
+
for (const node of context.dirty) {
|
|
1465
|
+
const path = resolvePath(node);
|
|
1466
|
+
if (path === undefined)
|
|
1467
|
+
continue;
|
|
1468
|
+
if (hasCoveringDenseRegion(node, path, denseRegions))
|
|
1469
|
+
continue;
|
|
1470
|
+
emissionPaths.set(node, path);
|
|
1471
|
+
if (!Array.isArray(nodeBase(node)) && hasReservedMutation(node))
|
|
1472
|
+
forcedFolds.set(node, path);
|
|
1473
|
+
const reservedAt = path.findIndex((segment) => typeof segment === "string" && RESERVED_SEGMENTS.has(segment));
|
|
1474
|
+
if (reservedAt >= 0) {
|
|
1475
|
+
let ancestor = node;
|
|
1476
|
+
for (let depth = path.length; depth > reservedAt; depth--)
|
|
1477
|
+
ancestor = nodeParent(ancestor);
|
|
1478
|
+
forcedFolds.set(ancestor, path.slice(0, reservedAt));
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
for (const [node, path] of forcedFolds)
|
|
1482
|
+
emissionPaths.set(node, path);
|
|
1483
|
+
for (const node of denseRegions.keys()) {
|
|
1484
|
+
const path = resolvePath(node);
|
|
1485
|
+
if (path !== undefined && !hasCoveringDenseRegion(node, path, denseRegions))
|
|
1486
|
+
emissionPaths.set(node, path);
|
|
1487
|
+
}
|
|
1488
|
+
let maxDepth = 0;
|
|
1489
|
+
for (const path of emissionPaths.values())
|
|
1490
|
+
maxDepth = Math.max(maxDepth, path.length);
|
|
1491
|
+
const buckets = Array.from({ length: maxDepth + 1 }, () => []);
|
|
1492
|
+
for (const [node, path] of emissionPaths)
|
|
1493
|
+
buckets[path.length].push(node);
|
|
1494
|
+
const folded = new Set();
|
|
1495
|
+
for (const bucket of buckets) {
|
|
1496
|
+
for (const node of bucket) {
|
|
1497
|
+
const path = emissionPaths.get(node);
|
|
1498
|
+
if (hasPlacementAncestor(node) || hasFoldedAncestor(node, folded))
|
|
1499
|
+
continue;
|
|
1500
|
+
if (forcedFolds.has(node)) {
|
|
1501
|
+
emitSet(operations, path, cloneNode(node));
|
|
1502
|
+
folded.add(node);
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
if (Array.isArray(nodeBase(node)))
|
|
1506
|
+
emitArrayOperations(node, path, operations, denseRegions.get(node));
|
|
1507
|
+
else
|
|
1508
|
+
emitObjectOperations(node, path, operations);
|
|
1509
|
+
if (operations.length > MAX_DELTA_OPERATIONS) {
|
|
1510
|
+
locatedDenseArray = undefined;
|
|
1511
|
+
return [["r", cloneNode(context.root)]];
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
locatedDenseArray = undefined;
|
|
1516
|
+
return operations;
|
|
1517
|
+
}
|
|
1518
|
+
function emitSimpleObjectOperations(context) {
|
|
1519
|
+
const nodes = [];
|
|
1520
|
+
for (const node of context.dirty) {
|
|
1521
|
+
if (Array.isArray(nodeBase(node)) || hasReservedMutation(node) || hasPlacementAncestor(node))
|
|
1522
|
+
return undefined;
|
|
1523
|
+
for (let parent = nodeParent(node); parent !== undefined; parent = nodeParent(parent)) {
|
|
1524
|
+
if (Array.isArray(nodeBase(parent)))
|
|
1525
|
+
return undefined;
|
|
1526
|
+
}
|
|
1527
|
+
const path = resolvePath(node);
|
|
1528
|
+
if (path === undefined)
|
|
1529
|
+
continue;
|
|
1530
|
+
for (const segment of path) {
|
|
1531
|
+
if (typeof segment === "string" && RESERVED_SEGMENTS.has(segment))
|
|
1532
|
+
return undefined;
|
|
1533
|
+
}
|
|
1534
|
+
nodes.push(node);
|
|
1535
|
+
if (nodes.length > MAX_SIMPLE_OBJECT_NODES)
|
|
1536
|
+
return undefined;
|
|
1537
|
+
}
|
|
1538
|
+
// Stable insertion sort avoids comparator/bucket allocations for the usual
|
|
1539
|
+
// handful of dirty object nodes.
|
|
1540
|
+
for (let index = 1; index < nodes.length; index++) {
|
|
1541
|
+
const node = nodes[index];
|
|
1542
|
+
const depth = node.preparedPath.length;
|
|
1543
|
+
let at = index;
|
|
1544
|
+
while (at > 0 && nodes[at - 1].preparedPath.length > depth) {
|
|
1545
|
+
nodes[at] = nodes[at - 1];
|
|
1546
|
+
at -= 1;
|
|
1547
|
+
}
|
|
1548
|
+
nodes[at] = node;
|
|
1549
|
+
}
|
|
1550
|
+
const operations = [];
|
|
1551
|
+
let canMaterializeDirectly = true;
|
|
1552
|
+
for (const node of nodes) {
|
|
1553
|
+
if (emitObjectOperations(node, node.preparedPath, operations))
|
|
1554
|
+
canMaterializeDirectly = false;
|
|
1555
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1556
|
+
return [["r", cloneNode(context.root)]];
|
|
1557
|
+
}
|
|
1558
|
+
context.simpleObjectMaterialization = canMaterializeDirectly;
|
|
1559
|
+
return operations;
|
|
1560
|
+
}
|
|
1561
|
+
let locatedDenseArray;
|
|
1562
|
+
let locatedDenseIndex = 0;
|
|
1563
|
+
function locateDenseArrayPosition(node) {
|
|
1564
|
+
let child = node;
|
|
1565
|
+
for (let parent = nodeParent(child); parent !== undefined; parent = nodeParent(child)) {
|
|
1566
|
+
if (Array.isArray(nodeBase(parent))) {
|
|
1567
|
+
const overlay = arrayOverlay(parent);
|
|
1568
|
+
if (child.parentKind !== 1 || overlay.structural)
|
|
1569
|
+
return false;
|
|
1570
|
+
const sourceIndex = child.parentKey;
|
|
1571
|
+
const valueIndex = overlay.baseOverrides?.get(sourceIndex);
|
|
1572
|
+
const current = valueIndex === undefined
|
|
1573
|
+
? nodeBase(parent)[sourceIndex]
|
|
1574
|
+
: storedValue(parent.context, valueIndex);
|
|
1575
|
+
if (current !== nodeBase(child))
|
|
1576
|
+
return false;
|
|
1577
|
+
locatedDenseArray = parent;
|
|
1578
|
+
locatedDenseIndex = sourceIndex;
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
child = parent;
|
|
1582
|
+
}
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1585
|
+
function hasCoveringDenseRegion(node, path, denseRegions) {
|
|
1586
|
+
for (let parent = nodeParent(node); parent !== undefined; parent = nodeParent(parent)) {
|
|
1587
|
+
const regions = denseRegions.get(parent);
|
|
1588
|
+
if (regions === undefined)
|
|
1589
|
+
continue;
|
|
1590
|
+
const parentPath = resolvePath(parent);
|
|
1591
|
+
if (parentPath === undefined || path.length <= parentPath.length)
|
|
1592
|
+
continue;
|
|
1593
|
+
let matches = true;
|
|
1594
|
+
for (let index = 0; index < parentPath.length; index++) {
|
|
1595
|
+
if (path[index] !== parentPath[index]) {
|
|
1596
|
+
matches = false;
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if (!matches)
|
|
1601
|
+
continue;
|
|
1602
|
+
const index = path[parentPath.length];
|
|
1603
|
+
if (typeof index === "number" && regionContaining(regions, index))
|
|
1604
|
+
return true;
|
|
1605
|
+
}
|
|
1606
|
+
return false;
|
|
1607
|
+
}
|
|
1608
|
+
function addDenseCandidate(candidates, index) {
|
|
1609
|
+
if (candidates.bits !== undefined) {
|
|
1610
|
+
candidates.bits[index] = 1;
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1613
|
+
candidates.indices.push(index);
|
|
1614
|
+
if (candidates.indices.length < 256)
|
|
1615
|
+
return;
|
|
1616
|
+
candidates.bits = new Uint8Array(candidates.length);
|
|
1617
|
+
for (const existing of candidates.indices)
|
|
1618
|
+
candidates.bits[existing] = 1;
|
|
1619
|
+
candidates.indices.length = 0;
|
|
1620
|
+
}
|
|
1621
|
+
function recordDenseArrayPosition(node, groups) {
|
|
1622
|
+
if (!locateDenseArrayPosition(node))
|
|
1623
|
+
return;
|
|
1624
|
+
let candidates = groups.get(locatedDenseArray);
|
|
1625
|
+
if (candidates === undefined) {
|
|
1626
|
+
candidates = { indices: [], bits: undefined, length: nodeBase(locatedDenseArray).length };
|
|
1627
|
+
groups.set(locatedDenseArray, candidates);
|
|
1628
|
+
}
|
|
1629
|
+
addDenseCandidate(candidates, locatedDenseIndex);
|
|
1630
|
+
}
|
|
1631
|
+
function buildDenseRegions(candidates) {
|
|
1632
|
+
if (candidates.bits === undefined)
|
|
1633
|
+
return [];
|
|
1634
|
+
const regions = [];
|
|
1635
|
+
const bits = candidates.bits;
|
|
1636
|
+
for (let at = 0; at < bits.length;) {
|
|
1637
|
+
while (at < bits.length && bits[at] === 0)
|
|
1638
|
+
at += 1;
|
|
1639
|
+
if (at === bits.length)
|
|
1640
|
+
break;
|
|
1641
|
+
const start = at;
|
|
1642
|
+
let end = at;
|
|
1643
|
+
let count = 0;
|
|
1644
|
+
let gap = 0;
|
|
1645
|
+
while (at < bits.length) {
|
|
1646
|
+
if (bits[at] !== 0) {
|
|
1647
|
+
count += 1;
|
|
1648
|
+
end = at;
|
|
1649
|
+
gap = 0;
|
|
1650
|
+
}
|
|
1651
|
+
else if (++gap > 1)
|
|
1652
|
+
break;
|
|
1653
|
+
at += 1;
|
|
1654
|
+
}
|
|
1655
|
+
const length = end - start + 1;
|
|
1656
|
+
if (count >= 256 && count * 2 >= length)
|
|
1657
|
+
regions.push({ start, length });
|
|
1658
|
+
}
|
|
1659
|
+
return regions;
|
|
1660
|
+
}
|
|
1661
|
+
function regionContaining(regions, index) {
|
|
1662
|
+
for (const region of regions)
|
|
1663
|
+
if (index >= region.start && index < region.start + region.length)
|
|
1664
|
+
return true;
|
|
1665
|
+
return false;
|
|
1666
|
+
}
|
|
1667
|
+
function hasReservedMutation(node) {
|
|
1668
|
+
if (node.writeKey !== undefined && RESERVED_SEGMENTS.has(node.writeKey))
|
|
1669
|
+
return true;
|
|
1670
|
+
if (node.deleteKey !== undefined && RESERVED_SEGMENTS.has(node.deleteKey))
|
|
1671
|
+
return true;
|
|
1672
|
+
if (node.writes !== undefined) {
|
|
1673
|
+
for (const key of node.writes.keys())
|
|
1674
|
+
if (RESERVED_SEGMENTS.has(key))
|
|
1675
|
+
return true;
|
|
1676
|
+
}
|
|
1677
|
+
if (node.deletes !== undefined) {
|
|
1678
|
+
for (const key of node.deletes)
|
|
1679
|
+
if (RESERVED_SEGMENTS.has(key))
|
|
1680
|
+
return true;
|
|
1681
|
+
}
|
|
1682
|
+
return false;
|
|
1683
|
+
}
|
|
1684
|
+
function hasFoldedAncestor(node, folded) {
|
|
1685
|
+
for (let parent = nodeParent(node); parent !== undefined; parent = nodeParent(parent))
|
|
1686
|
+
if (folded.has(parent))
|
|
1687
|
+
return true;
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
function hasPlacementAncestor(node) {
|
|
1691
|
+
for (let current = node; current !== undefined; current = nodeParent(current)) {
|
|
1692
|
+
if (nodeParent(current) !== undefined && current.parentPlacement)
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
function emitObjectWrite(node, path, operations, key, value) {
|
|
1698
|
+
const nextPath = [...path, key];
|
|
1699
|
+
const before = node.readded?.has(key) || !Object.hasOwn(nodeBase(node), key)
|
|
1700
|
+
? undefined
|
|
1701
|
+
: nodeBase(node)[key];
|
|
1702
|
+
const after = cloneStored(value, node.context);
|
|
1703
|
+
const emitted = emitChangedValue(operations, nextPath, before, after);
|
|
1704
|
+
return isContainer(before) && isContainer(after) && !emitted;
|
|
1705
|
+
}
|
|
1706
|
+
function emitObjectOperations(node, path, operations) {
|
|
1707
|
+
// Immutable replay must encode delete-and-readd explicitly so string-key
|
|
1708
|
+
// insertion order matches the draft.
|
|
1709
|
+
if (node.readded !== undefined) {
|
|
1710
|
+
for (const key of node.readded) {
|
|
1711
|
+
if (Object.hasOwn(nodeBase(node), key))
|
|
1712
|
+
operations.push(["d", [...path, key]]);
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
let normalizedContainerWrite = false;
|
|
1716
|
+
if (node.writeKey !== undefined)
|
|
1717
|
+
normalizedContainerWrite = emitObjectWrite(node, path, operations, node.writeKey, objectValue(node, node.writeKey));
|
|
1718
|
+
if (node.writes !== undefined) {
|
|
1719
|
+
for (const [key, valueIndex] of node.writes) {
|
|
1720
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1721
|
+
return normalizedContainerWrite;
|
|
1722
|
+
if (emitObjectWrite(node, path, operations, key, storedValue(node.context, valueIndex)))
|
|
1723
|
+
normalizedContainerWrite = true;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
if (node.deleteKey !== undefined && Object.hasOwn(nodeBase(node), node.deleteKey)) {
|
|
1727
|
+
operations.push(["d", [...path, node.deleteKey]]);
|
|
1728
|
+
}
|
|
1729
|
+
if (node.deletes !== undefined) {
|
|
1730
|
+
for (const key of node.deletes) {
|
|
1731
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1732
|
+
return normalizedContainerWrite;
|
|
1733
|
+
if (Object.hasOwn(nodeBase(node), key))
|
|
1734
|
+
operations.push(["d", [...path, key]]);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return normalizedContainerWrite;
|
|
1738
|
+
}
|
|
1739
|
+
function buildArrayPlan(node) {
|
|
1740
|
+
const overlay = arrayOverlay(node);
|
|
1741
|
+
if (overlay.plan !== undefined)
|
|
1742
|
+
return overlay.plan;
|
|
1743
|
+
const base = nodeBase(node);
|
|
1744
|
+
const pieces = piecesOf(overlay);
|
|
1745
|
+
const retained = new Uint8Array(base.length);
|
|
1746
|
+
const targetBase = [];
|
|
1747
|
+
for (const piece of pieces) {
|
|
1748
|
+
if (piece.kind !== "base")
|
|
1749
|
+
continue;
|
|
1750
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1751
|
+
const index = piece.start + piece.step * offset;
|
|
1752
|
+
retained[index] = 1;
|
|
1753
|
+
targetBase.push(index);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
const removeRuns = [];
|
|
1757
|
+
for (let end = base.length; end > 0;) {
|
|
1758
|
+
if (retained[end - 1] !== 0) {
|
|
1759
|
+
end -= 1;
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
let start = end - 1;
|
|
1763
|
+
while (start > 0 && retained[start - 1] === 0)
|
|
1764
|
+
start -= 1;
|
|
1765
|
+
removeRuns.push(start, end - start);
|
|
1766
|
+
end = start;
|
|
1767
|
+
}
|
|
1768
|
+
const retainedBase = [];
|
|
1769
|
+
for (let index = 0; index < retained.length; index++)
|
|
1770
|
+
if (retained[index] !== 0)
|
|
1771
|
+
retainedBase.push(index);
|
|
1772
|
+
let permutation;
|
|
1773
|
+
if (targetBase.some((value, index) => value !== retainedBase[index])) {
|
|
1774
|
+
const positions = new Map(retainedBase.map((value, index) => [value, index]));
|
|
1775
|
+
permutation = targetBase.map((value) => positions.get(value));
|
|
1776
|
+
}
|
|
1777
|
+
const insertRuns = [];
|
|
1778
|
+
let logicalIndex = 0;
|
|
1779
|
+
for (let pieceIndex = 0; pieceIndex < pieces.length;) {
|
|
1780
|
+
const piece = pieces[pieceIndex];
|
|
1781
|
+
if (piece.kind === "base") {
|
|
1782
|
+
logicalIndex += piece.length;
|
|
1783
|
+
pieceIndex += 1;
|
|
1784
|
+
continue;
|
|
1785
|
+
}
|
|
1786
|
+
const startPiece = pieceIndex;
|
|
1787
|
+
while (pieceIndex < pieces.length && pieces[pieceIndex].kind === "insert") {
|
|
1788
|
+
logicalIndex += pieces[pieceIndex].length;
|
|
1789
|
+
pieceIndex += 1;
|
|
1790
|
+
}
|
|
1791
|
+
insertRuns.push(logicalIndex - rangeLength(pieces, startPiece, pieceIndex), startPiece, pieceIndex);
|
|
1792
|
+
}
|
|
1793
|
+
overlay.plan = { removeRuns, permutation, insertRuns };
|
|
1794
|
+
return overlay.plan;
|
|
1795
|
+
}
|
|
1796
|
+
function rangeLength(pieces, start, end) {
|
|
1797
|
+
let length = 0;
|
|
1798
|
+
for (let index = start; index < end; index++)
|
|
1799
|
+
length += pieces[index].length;
|
|
1800
|
+
return length;
|
|
1801
|
+
}
|
|
1802
|
+
function emitArrayOperations(node, path, operations, denseRegions) {
|
|
1803
|
+
const overlay = arrayOverlay(node);
|
|
1804
|
+
const base = nodeBase(node);
|
|
1805
|
+
if (denseRegions !== undefined) {
|
|
1806
|
+
for (const region of denseRegions) {
|
|
1807
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1808
|
+
return;
|
|
1809
|
+
operations.push(["p", path, region.start, region.length, cloneArrayRegion(node, region)]);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
if (overlay.structural) {
|
|
1813
|
+
const plan = buildArrayPlan(node);
|
|
1814
|
+
for (let index = 0; index < plan.removeRuns.length; index += 2) {
|
|
1815
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1816
|
+
return;
|
|
1817
|
+
operations.push(["p", path, plan.removeRuns[index], plan.removeRuns[index + 1], []]);
|
|
1818
|
+
}
|
|
1819
|
+
if (plan.permutation !== undefined)
|
|
1820
|
+
operations.push(["m", path, plan.permutation]);
|
|
1821
|
+
const pieces = piecesOf(overlay);
|
|
1822
|
+
for (let run = 0; run < plan.insertRuns.length; run += 3) {
|
|
1823
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1824
|
+
return;
|
|
1825
|
+
const logicalIndex = plan.insertRuns[run];
|
|
1826
|
+
const items = [];
|
|
1827
|
+
for (let pieceIndex = plan.insertRuns[run + 1]; pieceIndex < plan.insertRuns[run + 2]; pieceIndex++) {
|
|
1828
|
+
const piece = pieces[pieceIndex];
|
|
1829
|
+
for (let offset = 0; offset < piece.length; offset++) {
|
|
1830
|
+
const sourceIndex = piece.start + piece.step * offset;
|
|
1831
|
+
items.push(cloneStored(entryValueAt(node, piece, sourceIndex), node.context));
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
operations.push(["p", path, logicalIndex, 0, items]);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
for (const [baseIndex, valueIndex] of overlay.baseOverrides ?? []) {
|
|
1838
|
+
if (operations.length > MAX_DELTA_OPERATIONS)
|
|
1839
|
+
return;
|
|
1840
|
+
const index = findEntryIndex(overlay, 1, baseIndex, undefined);
|
|
1841
|
+
if (index === undefined || (denseRegions !== undefined && regionContaining(denseRegions, index)))
|
|
1842
|
+
continue;
|
|
1843
|
+
emitChangedValue(operations, [...path, index], base[baseIndex], cloneStored(storedValue(node.context, valueIndex), node.context));
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
function cloneArrayRegion(node, region) {
|
|
1847
|
+
const overlay = arrayOverlay(node);
|
|
1848
|
+
const result = [];
|
|
1849
|
+
for (let index = region.start; index < region.start + region.length; index++) {
|
|
1850
|
+
const piece = locatePiece(overlay, index);
|
|
1851
|
+
const sourceIndex = piece.start + piece.step * overlay.locatedOffset;
|
|
1852
|
+
result.push(cloneStored(entryValueAt(node, piece, sourceIndex), node.context));
|
|
1853
|
+
}
|
|
1854
|
+
return result;
|
|
1855
|
+
}
|
|
1856
|
+
function emitChangedValue(operations, path, before, after) {
|
|
1857
|
+
if (!isContainer(after) && before === after)
|
|
1858
|
+
return false;
|
|
1859
|
+
if (isContainer(before) && isContainer(after) && equalTrustedJson(before, after))
|
|
1860
|
+
return false;
|
|
1861
|
+
if (typeof before === "string" && typeof after === "string") {
|
|
1862
|
+
// Slice/equality stays on V8's rope fast path; startsWith scans the entire
|
|
1863
|
+
// accumulated prefix on every append.
|
|
1864
|
+
if (after.length > before.length && after.slice(0, before.length) === before) {
|
|
1865
|
+
operations.push(["a", path, after.slice(before.length)]);
|
|
1866
|
+
return true;
|
|
1867
|
+
}
|
|
1868
|
+
const shared = overlap(before, after, 65_536);
|
|
1869
|
+
if (shared > 0) {
|
|
1870
|
+
operations.push(["t", path, before.length - shared]);
|
|
1871
|
+
if (after.length > shared)
|
|
1872
|
+
operations.push(["a", path, after.slice(shared)]);
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
operations.push(["s", path, after]);
|
|
1877
|
+
return true;
|
|
1878
|
+
}
|
|
1879
|
+
function emitSet(operations, path, value) {
|
|
1880
|
+
if (path.length === 0)
|
|
1881
|
+
operations.push(["r", value]);
|
|
1882
|
+
else
|
|
1883
|
+
operations.push(["s", path, value]);
|
|
1884
|
+
}
|
|
1885
|
+
function resolvePath(node) {
|
|
1886
|
+
if (node.preparedPath !== undefined)
|
|
1887
|
+
return node.preparedPath;
|
|
1888
|
+
const parent = nodeParent(node);
|
|
1889
|
+
if (parent === undefined) {
|
|
1890
|
+
node.preparedPath = [];
|
|
1891
|
+
return node.preparedPath;
|
|
1892
|
+
}
|
|
1893
|
+
const parentPath = resolvePath(parent);
|
|
1894
|
+
if (parentPath === undefined)
|
|
1895
|
+
return undefined;
|
|
1896
|
+
if (node.parentKind === 0) {
|
|
1897
|
+
const key = node.parentKey;
|
|
1898
|
+
if (!objectHas(parent, key) || objectValue(parent, key) !== nodeBase(node))
|
|
1899
|
+
return undefined;
|
|
1900
|
+
node.preparedPath = [...parentPath, key];
|
|
1901
|
+
return node.preparedPath;
|
|
1902
|
+
}
|
|
1903
|
+
const overlay = arrayOverlay(parent);
|
|
1904
|
+
const index = findEntryIndex(overlay, node.parentKind, node.parentKey, node.parentSource);
|
|
1905
|
+
if (index === undefined)
|
|
1906
|
+
return undefined;
|
|
1907
|
+
const piece = locatePiece(overlay, index);
|
|
1908
|
+
if (entryValueAt(parent, piece, node.parentKey) !== nodeBase(node))
|
|
1909
|
+
return undefined;
|
|
1910
|
+
node.preparedPath = [...parentPath, index];
|
|
1911
|
+
return node.preparedPath;
|
|
1912
|
+
}
|
|
1913
|
+
function ensurePieceLocations(overlay) {
|
|
1914
|
+
if (overlay.baseLocations !== undefined)
|
|
1915
|
+
return;
|
|
1916
|
+
overlay.baseLocations = [];
|
|
1917
|
+
overlay.insertLocations = new Map();
|
|
1918
|
+
let logicalStart = 0;
|
|
1919
|
+
for (const piece of piecesOf(overlay)) {
|
|
1920
|
+
const last = piece.start + piece.step * (piece.length - 1);
|
|
1921
|
+
const location = {
|
|
1922
|
+
piece,
|
|
1923
|
+
logicalStart,
|
|
1924
|
+
minimum: Math.min(piece.start, last),
|
|
1925
|
+
maximum: Math.max(piece.start, last),
|
|
1926
|
+
};
|
|
1927
|
+
if (piece.kind === "base")
|
|
1928
|
+
overlay.baseLocations.push(location);
|
|
1929
|
+
else {
|
|
1930
|
+
let locations = overlay.insertLocations.get(piece.source);
|
|
1931
|
+
if (locations === undefined) {
|
|
1932
|
+
locations = [];
|
|
1933
|
+
overlay.insertLocations.set(piece.source, locations);
|
|
1934
|
+
}
|
|
1935
|
+
locations.push(location);
|
|
1936
|
+
}
|
|
1937
|
+
logicalStart += piece.length;
|
|
1938
|
+
}
|
|
1939
|
+
const byMinimum = (left, right) => left.minimum - right.minimum;
|
|
1940
|
+
overlay.baseLocations.sort(byMinimum);
|
|
1941
|
+
for (const locations of overlay.insertLocations.values())
|
|
1942
|
+
locations.sort(byMinimum);
|
|
1943
|
+
}
|
|
1944
|
+
function findEntryIndex(overlay, kind, sourceIndex, source) {
|
|
1945
|
+
if (kind === 1 && !overlay.structural)
|
|
1946
|
+
return sourceIndex;
|
|
1947
|
+
ensurePieceLocations(overlay);
|
|
1948
|
+
const locations = kind === 1 ? overlay.baseLocations : overlay.insertLocations.get(source);
|
|
1949
|
+
if (locations === undefined)
|
|
1950
|
+
return undefined;
|
|
1951
|
+
let low = 0;
|
|
1952
|
+
let high = locations.length;
|
|
1953
|
+
while (low < high) {
|
|
1954
|
+
const middle = (low + high) >>> 1;
|
|
1955
|
+
if (locations[middle].minimum <= sourceIndex)
|
|
1956
|
+
low = middle + 1;
|
|
1957
|
+
else
|
|
1958
|
+
high = middle;
|
|
1959
|
+
}
|
|
1960
|
+
const location = locations[low - 1];
|
|
1961
|
+
if (location === undefined || sourceIndex > location.maximum)
|
|
1962
|
+
return undefined;
|
|
1963
|
+
const offset = (sourceIndex - location.piece.start) / location.piece.step;
|
|
1964
|
+
return offset >= 0 && offset < location.piece.length ? location.logicalStart + offset : undefined;
|
|
1965
|
+
}
|
|
1966
|
+
function ensureOperations(context) {
|
|
1967
|
+
if (context.ops !== undefined)
|
|
1968
|
+
return context.ops;
|
|
1969
|
+
assertReadable(context);
|
|
1970
|
+
if (context.replacement) {
|
|
1971
|
+
const base = context.tracker.deref()?.replacementBase(context);
|
|
1972
|
+
context.replacementNoop =
|
|
1973
|
+
base !== undefined && equalTrustedJson(base, nodeBase(context.root));
|
|
1974
|
+
context.ops = context.replacementNoop ? [] : [["r", nodeBase(context.root)]];
|
|
1975
|
+
}
|
|
1976
|
+
else
|
|
1977
|
+
context.ops = emitOperations(context);
|
|
1978
|
+
return context.ops;
|
|
1979
|
+
}
|
|
1980
|
+
function equalTrustedJson(left, right) {
|
|
1981
|
+
if (left === right)
|
|
1982
|
+
return true;
|
|
1983
|
+
if (!isContainer(left) || !isContainer(right) || Array.isArray(left) !== Array.isArray(right))
|
|
1984
|
+
return false;
|
|
1985
|
+
if (Array.isArray(left)) {
|
|
1986
|
+
const other = right;
|
|
1987
|
+
if (left.length !== other.length)
|
|
1988
|
+
return false;
|
|
1989
|
+
for (let index = 0; index < left.length; index++)
|
|
1990
|
+
if (!equalTrustedJson(left[index], other[index]))
|
|
1991
|
+
return false;
|
|
1992
|
+
return true;
|
|
1993
|
+
}
|
|
1994
|
+
const other = right;
|
|
1995
|
+
const keys = Object.keys(left);
|
|
1996
|
+
if (keys.length !== Object.keys(other).length)
|
|
1997
|
+
return false;
|
|
1998
|
+
for (const key of keys)
|
|
1999
|
+
if (!Object.hasOwn(other, key) || !equalTrustedJson(left[key], other[key]))
|
|
2000
|
+
return false;
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
function abortContext(context) {
|
|
2004
|
+
if (context.status.value === "aborted" || context.status.value === "consumed" || context.status.value === "stale")
|
|
2005
|
+
return;
|
|
2006
|
+
context.status.value = "aborted";
|
|
2007
|
+
clearContext(context);
|
|
2008
|
+
}
|
|
2009
|
+
function releaseOverlayReferences(context) {
|
|
2010
|
+
context.tracker.deref()?.releaseContext(context);
|
|
2011
|
+
if (locatedDenseArray?.context === context)
|
|
2012
|
+
locatedDenseArray = undefined;
|
|
2013
|
+
context.overlayReleased = true;
|
|
2014
|
+
context.dirty.length = 0;
|
|
2015
|
+
context.nodes.length = 0;
|
|
2016
|
+
context.rawNodes = undefined;
|
|
2017
|
+
context.ops = undefined;
|
|
2018
|
+
context.root = undefined;
|
|
2019
|
+
context.bases = undefined;
|
|
2020
|
+
context.stored = undefined;
|
|
2021
|
+
context.baseValue = undefined;
|
|
2022
|
+
}
|
|
2023
|
+
function clearContext(context) {
|
|
2024
|
+
context.tracker.deref()?.releaseContext(context);
|
|
2025
|
+
if (locatedDenseArray?.context === context)
|
|
2026
|
+
locatedDenseArray = undefined;
|
|
2027
|
+
for (const node of context.nodes) {
|
|
2028
|
+
if (Array.isArray(node.target))
|
|
2029
|
+
node.target.length = 0;
|
|
2030
|
+
Reflect.deleteProperty(node.target, NODE);
|
|
2031
|
+
node.writes?.clear();
|
|
2032
|
+
node.deletes?.clear();
|
|
2033
|
+
node.readded?.clear();
|
|
2034
|
+
if (node.array?.pieces !== undefined)
|
|
2035
|
+
node.array.pieces.length = 0;
|
|
2036
|
+
if (node.array !== undefined) {
|
|
2037
|
+
node.array.root = undefined;
|
|
2038
|
+
node.array.baseLocations = undefined;
|
|
2039
|
+
node.array.insertLocations = undefined;
|
|
2040
|
+
}
|
|
2041
|
+
node.array?.baseOverrides?.clear();
|
|
2042
|
+
node.array?.insertOverrides?.clear();
|
|
2043
|
+
node.parentSource = undefined;
|
|
2044
|
+
node.target = RELEASED;
|
|
2045
|
+
node.proxy = RELEASED;
|
|
2046
|
+
node.writeKey = undefined;
|
|
2047
|
+
node.writeValue = undefined;
|
|
2048
|
+
node.writes = undefined;
|
|
2049
|
+
node.deleteKey = undefined;
|
|
2050
|
+
node.deletes = undefined;
|
|
2051
|
+
node.readded = undefined;
|
|
2052
|
+
node.array = undefined;
|
|
2053
|
+
node.subtreeDirty = undefined;
|
|
2054
|
+
node.preparedPath = undefined;
|
|
2055
|
+
}
|
|
2056
|
+
context.dirty.length = 0;
|
|
2057
|
+
context.nodes.length = 0;
|
|
2058
|
+
context.rawNodes = undefined;
|
|
2059
|
+
context.ops = undefined;
|
|
2060
|
+
context.root = undefined;
|
|
2061
|
+
context.bases = undefined;
|
|
2062
|
+
context.stored = undefined;
|
|
2063
|
+
context.baseValue = undefined;
|
|
2064
|
+
context.overlayReleased = true;
|
|
2065
|
+
}
|
|
2066
|
+
function isContainer(value) {
|
|
2067
|
+
return value !== null && typeof value === "object";
|
|
2068
|
+
}
|
|
2069
|
+
//# sourceMappingURL=tracker.js.map
|