opshot 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -24
- package/dist/chunk-4JZIOHSP.js +768 -0
- package/dist/createMutableState-DJ9dmpR-.d.ts +36 -0
- package/dist/index.d.ts +52 -76
- package/dist/index.js +7 -1254
- package/dist/react.d.ts +23 -0
- package/dist/react.js +517 -0
- package/package.json +10 -1
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
// src/readProxyRegistry.ts
|
|
2
|
+
var readProxyTargets = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
var registerReadProxyTarget = (readProxy, target) => {
|
|
4
|
+
readProxyTargets.set(readProxy, target);
|
|
5
|
+
};
|
|
6
|
+
var getRegisteredReadProxyTarget = (readProxy) => readProxyTargets.get(readProxy);
|
|
7
|
+
|
|
8
|
+
// src/utils/predicates.ts
|
|
9
|
+
var isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
|
|
10
|
+
|
|
11
|
+
// src/peelReadProxy.ts
|
|
12
|
+
function peelReadProxy(value) {
|
|
13
|
+
if (!isObjectLike(value)) return value;
|
|
14
|
+
let current = value;
|
|
15
|
+
while (isObjectLike(current)) {
|
|
16
|
+
const registeredTarget = getRegisteredReadProxyTarget(current);
|
|
17
|
+
if (registeredTarget === void 0) break;
|
|
18
|
+
current = registeredTarget;
|
|
19
|
+
}
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/node.ts
|
|
24
|
+
var byRaw = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
var byProxy = /* @__PURE__ */ new WeakMap();
|
|
26
|
+
var versions = /* @__PURE__ */ new WeakMap();
|
|
27
|
+
var versionOf = (raw) => versions.get(raw) ?? 0;
|
|
28
|
+
var bumpVersion = (raw) => {
|
|
29
|
+
versions.set(raw, versionOf(raw) + 1);
|
|
30
|
+
};
|
|
31
|
+
var proxyHandler;
|
|
32
|
+
function installProxyHandler(handler2) {
|
|
33
|
+
proxyHandler = handler2;
|
|
34
|
+
}
|
|
35
|
+
function recordOf(value) {
|
|
36
|
+
const peeled = peelReadProxy(value);
|
|
37
|
+
return byRaw.get(peeled) ?? byProxy.get(peeled);
|
|
38
|
+
}
|
|
39
|
+
function rawOf(value) {
|
|
40
|
+
return recordOf(value)?.raw ?? peelReadProxy(value);
|
|
41
|
+
}
|
|
42
|
+
function proxyOf(raw) {
|
|
43
|
+
const existing = recordOf(raw);
|
|
44
|
+
if (existing !== void 0) return existing.proxy;
|
|
45
|
+
if (proxyHandler === void 0) throw new Error("opshot: proxy handler is not installed");
|
|
46
|
+
const proxy = new Proxy(raw, proxyHandler);
|
|
47
|
+
const record = { raw, proxy, memberships: /* @__PURE__ */ new Map() };
|
|
48
|
+
byRaw.set(raw, record);
|
|
49
|
+
byProxy.set(proxy, record);
|
|
50
|
+
return proxy;
|
|
51
|
+
}
|
|
52
|
+
function handlesOf(node) {
|
|
53
|
+
return membershipsOf(node).map(([handle]) => handle);
|
|
54
|
+
}
|
|
55
|
+
function membershipsOf(node) {
|
|
56
|
+
const record = recordOf(rawOf(node));
|
|
57
|
+
if (record === void 0) return [];
|
|
58
|
+
const memberships = new Array();
|
|
59
|
+
for (const [handle, membership] of record.memberships) {
|
|
60
|
+
if (membership.edges > 0) memberships.push([handle, membership]);
|
|
61
|
+
}
|
|
62
|
+
return memberships;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/ignore.ts
|
|
66
|
+
var ignored = /* @__PURE__ */ new WeakSet();
|
|
67
|
+
function ignore(value, on = true) {
|
|
68
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
69
|
+
if (on) ignored.add(rawOf(value));
|
|
70
|
+
else ignored.delete(rawOf(value));
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
var isIgnored = (value) => ignored.has(rawOf(value));
|
|
74
|
+
|
|
75
|
+
// src/unsafeTrack.ts
|
|
76
|
+
var unsafeMarked = /* @__PURE__ */ new WeakSet();
|
|
77
|
+
function unsafeTrack(value, on = true) {
|
|
78
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return value;
|
|
79
|
+
if (on) unsafeMarked.add(rawOf(value));
|
|
80
|
+
else unsafeMarked.delete(rawOf(value));
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
var isUnsafeMarked = (value) => unsafeMarked.has(rawOf(value));
|
|
84
|
+
|
|
85
|
+
// src/batch.ts
|
|
86
|
+
var metaStack = [];
|
|
87
|
+
function currentMeta() {
|
|
88
|
+
return metaStack.length === 0 ? void 0 : metaStack[metaStack.length - 1];
|
|
89
|
+
}
|
|
90
|
+
var isThenable = (value) => isObjectLike(value) && "then" in value && typeof value.then === "function";
|
|
91
|
+
function batch(callback, meta) {
|
|
92
|
+
metaStack.push(meta);
|
|
93
|
+
let result;
|
|
94
|
+
try {
|
|
95
|
+
result = callback();
|
|
96
|
+
} finally {
|
|
97
|
+
metaStack.pop();
|
|
98
|
+
}
|
|
99
|
+
if (isThenable(result))
|
|
100
|
+
throw new TypeError("opshot: batch requires a synchronous callback; writes after an await would carry no meta");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/utils/constructorName.ts
|
|
104
|
+
var constructorName = (candidate) => typeof candidate === "function" && candidate.name !== "" ? candidate.name : "Object";
|
|
105
|
+
|
|
106
|
+
// src/boundaryErrors.ts
|
|
107
|
+
var ignoreOption = "ignore(value) to store it by reference, untracked";
|
|
108
|
+
var unsafeTrackDataOption = "unsafeTrack(value) to track its data anyway";
|
|
109
|
+
var unsafeTrackPrivateOption = "unsafeTrack(value) tracks public fields while private methods stay untracked";
|
|
110
|
+
var unsafeTrackSlotOption = "unsafeTrack(value) tracks public fields while slot methods stay untracked";
|
|
111
|
+
var unsafeTrackLossyOption = "unsafeTrack(value) to track it lossily";
|
|
112
|
+
var locationClause = (path) => path === void 0 || path.length === 0 ? "" : ` at /${path.join("/")}`;
|
|
113
|
+
var boundaryError = (className, reason, options, path) => new Error(
|
|
114
|
+
`opshot: ${className}${locationClause(path)} cannot be tracked (${reason}). Options:
|
|
115
|
+
${options.map((option) => `- ${option}`).join("\n")}`
|
|
116
|
+
);
|
|
117
|
+
var slotContainerError = (className, trackedName, path) => boundaryError(
|
|
118
|
+
className,
|
|
119
|
+
"its state lives in internal slots",
|
|
120
|
+
[`use ${trackedName} for a tracked equivalent`, unsafeTrackLossyOption, ignoreOption],
|
|
121
|
+
path
|
|
122
|
+
);
|
|
123
|
+
var arraySubclassError = (className, path) => boundaryError(className, "array subclasses are not plain arrays", [unsafeTrackDataOption, ignoreOption], path);
|
|
124
|
+
var cleanClassError = (className, path) => boundaryError(className, "arrow-method writes won't be tracked", [unsafeTrackDataOption, ignoreOption], path);
|
|
125
|
+
var privateClassError = (className, path) => boundaryError(className, "its state is hidden in private fields", [unsafeTrackPrivateOption, ignoreOption], path);
|
|
126
|
+
var nativeClassError = (className, path) => boundaryError(className, "its state is hidden in internal slots", [unsafeTrackSlotOption, ignoreOption], path);
|
|
127
|
+
var nonWritablePropertyError = (value, path) => boundaryError(
|
|
128
|
+
constructorName(value.constructor),
|
|
129
|
+
"a non-writable property's interior is silently mutable and untracked",
|
|
130
|
+
["make the property writable", "ignore(value) to declare the escape"],
|
|
131
|
+
path
|
|
132
|
+
);
|
|
133
|
+
var inheritsFromPrototype = (value, prototype) => {
|
|
134
|
+
for (let current = Reflect.getPrototypeOf(value); current !== null; current = Reflect.getPrototypeOf(current))
|
|
135
|
+
if (current === prototype) return true;
|
|
136
|
+
return false;
|
|
137
|
+
};
|
|
138
|
+
var rejectionError = (value, kind, path) => {
|
|
139
|
+
const className = constructorName(value.constructor);
|
|
140
|
+
if (inheritsFromPrototype(value, Map.prototype)) return slotContainerError(className, "TrackedMap", path);
|
|
141
|
+
if (inheritsFromPrototype(value, Set.prototype)) return slotContainerError(className, "TrackedSet", path);
|
|
142
|
+
if (inheritsFromPrototype(value, Date.prototype)) return slotContainerError(className, "TrackedDate", path);
|
|
143
|
+
switch (kind) {
|
|
144
|
+
case "arraySubclass":
|
|
145
|
+
return arraySubclassError(className, path);
|
|
146
|
+
case "cleanClass":
|
|
147
|
+
return cleanClassError(className, path);
|
|
148
|
+
case "privateClass":
|
|
149
|
+
return privateClassError(className, path);
|
|
150
|
+
case "nativeClass":
|
|
151
|
+
return nativeClassError(className, path);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/classify.ts
|
|
156
|
+
var isDangerousKind = (kind) => kind !== "plain" && kind !== "plainArray" && kind !== "cleanClass";
|
|
157
|
+
var sourceCache = /* @__PURE__ */ new WeakMap();
|
|
158
|
+
var kindCache = /* @__PURE__ */ new WeakMap();
|
|
159
|
+
var readSource = (constructor) => {
|
|
160
|
+
const cached = sourceCache.get(constructor);
|
|
161
|
+
if (cached !== void 0) return cached;
|
|
162
|
+
const source = Function.prototype.toString.call(constructor);
|
|
163
|
+
sourceCache.set(constructor, source);
|
|
164
|
+
return source;
|
|
165
|
+
};
|
|
166
|
+
var privateNameAccess = /\.\s*#[A-Za-z_$]/;
|
|
167
|
+
var privateNameDeclaration = new RegExp(
|
|
168
|
+
String.raw`[{;}](?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n|static\b|async\b|get\b|set\b|\*)*` + String.raw`#[A-Za-z_$][\w$]*` + String.raw`(?:[ \t]*(?:\/\*[\s\S]*?\*\/[ \t]*)*[(=;}]|[ \t]*(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/[ \t]*)?(?:\r?\n|$))`,
|
|
169
|
+
"m"
|
|
170
|
+
);
|
|
171
|
+
var hasPrivateName = (source) => privateNameAccess.test(source) || privateNameDeclaration.test(source);
|
|
172
|
+
var classifyChain = (initialConstructor) => {
|
|
173
|
+
if (typeof initialConstructor !== "function") return "cleanClass";
|
|
174
|
+
const cached = kindCache.get(initialConstructor);
|
|
175
|
+
if (cached !== void 0) return cached;
|
|
176
|
+
let sawNativeSource = false;
|
|
177
|
+
let current = initialConstructor;
|
|
178
|
+
let kind = "cleanClass";
|
|
179
|
+
while (typeof current === "function" && current !== Object && current !== Array && current !== Function.prototype) {
|
|
180
|
+
const source = readSource(current);
|
|
181
|
+
if (hasPrivateName(source)) {
|
|
182
|
+
kind = "privateClass";
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
if (source.includes("[native code]")) sawNativeSource = true;
|
|
186
|
+
current = Reflect.getPrototypeOf(current);
|
|
187
|
+
}
|
|
188
|
+
if (kind !== "privateClass") kind = sawNativeSource ? "nativeClass" : "cleanClass";
|
|
189
|
+
kindCache.set(initialConstructor, kind);
|
|
190
|
+
return kind;
|
|
191
|
+
};
|
|
192
|
+
function classifyValue(value) {
|
|
193
|
+
const prototype = Object.getPrototypeOf(value);
|
|
194
|
+
if (Array.isArray(value))
|
|
195
|
+
return prototype === Array.prototype || prototype === null ? "plainArray" : "arraySubclass";
|
|
196
|
+
if (prototype === Object.prototype || prototype === null) return "plain";
|
|
197
|
+
return classifyChain(value.constructor);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/utils/dataEntries.ts
|
|
201
|
+
var walkDataEntries = (value) => {
|
|
202
|
+
const entries = new Array();
|
|
203
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
204
|
+
if (typeof key !== "string" || key === "__proto__") continue;
|
|
205
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
|
|
206
|
+
if (descriptor === void 0 || !descriptor.enumerable || !("value" in descriptor)) continue;
|
|
207
|
+
entries.push({ key, value: descriptor.value, writable: descriptor.writable === true });
|
|
208
|
+
}
|
|
209
|
+
return entries;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/edges.ts
|
|
213
|
+
var isTrackedEntry = (value, writable) => writable && typeof value === "object" && value !== null && !isIgnored(value) && !Object.isFrozen(value);
|
|
214
|
+
var checkNode = (node, entries, route) => {
|
|
215
|
+
const kind = classifyValue(node);
|
|
216
|
+
if (isDangerousKind(kind)) throw rejectionError(node, kind, route);
|
|
217
|
+
for (const entry of entries) {
|
|
218
|
+
if (typeof entry.value === "function") {
|
|
219
|
+
if (kind === "cleanClass") throw rejectionError(node, "cleanClass", [...route, entry.key]);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (typeof entry.value !== "object" || entry.value === null) continue;
|
|
223
|
+
if (isIgnored(entry.value) || Object.isFrozen(entry.value)) continue;
|
|
224
|
+
if (!entry.writable) throw nonWritablePropertyError(node, [...route, entry.key]);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
var descend = (handle, node, route, checked) => {
|
|
228
|
+
const entries = walkDataEntries(node);
|
|
229
|
+
if (checked) checkNode(node, entries, route);
|
|
230
|
+
for (const entry of entries) {
|
|
231
|
+
if (isTrackedEntry(entry.value, entry.writable))
|
|
232
|
+
attach(handle, node, entry.key, entry.value, [...route, entry.key]);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
var flip = (handle, node, route) => {
|
|
236
|
+
const membership = recordOf(node)?.memberships.get(handle);
|
|
237
|
+
if (!membership?.exempt) return;
|
|
238
|
+
membership.exempt = false;
|
|
239
|
+
const entries = walkDataEntries(node);
|
|
240
|
+
checkNode(node, entries, route);
|
|
241
|
+
for (const entry of entries) {
|
|
242
|
+
if (!isTrackedEntry(entry.value, entry.writable)) continue;
|
|
243
|
+
const child = rawOf(entry.value);
|
|
244
|
+
if (recordOf(child)?.memberships.has(handle) === true) flip(handle, child, [...route, entry.key]);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var cascade = (handle, node, keys) => {
|
|
248
|
+
for (const key of keys) {
|
|
249
|
+
const value = Reflect.get(node, key);
|
|
250
|
+
if (typeof value !== "object" || value === null) continue;
|
|
251
|
+
const child = rawOf(value);
|
|
252
|
+
if (recordOf(child)?.memberships.has(handle) === true) detach(handle, node, child);
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
function attach(handle, parent, key, child, route) {
|
|
256
|
+
const parentMembership = recordOf(parent)?.memberships.get(handle);
|
|
257
|
+
if (parentMembership === void 0) return;
|
|
258
|
+
parentMembership.keys.add(key);
|
|
259
|
+
const rawChild = rawOf(child);
|
|
260
|
+
const rawParent = rawOf(parent);
|
|
261
|
+
proxyOf(rawChild);
|
|
262
|
+
const record = recordOf(rawChild);
|
|
263
|
+
if (record === void 0) return;
|
|
264
|
+
const edgeExempt = parentMembership.exempt || isUnsafeMarked(rawChild);
|
|
265
|
+
const membership = record.memberships.get(handle);
|
|
266
|
+
if (membership === void 0) {
|
|
267
|
+
record.memberships.set(handle, {
|
|
268
|
+
edges: 1,
|
|
269
|
+
exempt: edgeExempt,
|
|
270
|
+
keys: /* @__PURE__ */ new Set(),
|
|
271
|
+
parents: /* @__PURE__ */ new Map([[rawParent, 1]])
|
|
272
|
+
});
|
|
273
|
+
descend(handle, rawChild, route, !edgeExempt);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
membership.edges += 1;
|
|
277
|
+
membership.parents.set(rawParent, (membership.parents.get(rawParent) ?? 0) + 1);
|
|
278
|
+
if (membership.exempt && !edgeExempt) flip(handle, rawChild, route);
|
|
279
|
+
}
|
|
280
|
+
function attachRoot(handle, root, exempt) {
|
|
281
|
+
const record = recordOf(root);
|
|
282
|
+
if (record === void 0) return;
|
|
283
|
+
record.memberships.set(handle, { edges: 1, exempt, keys: /* @__PURE__ */ new Set(), parents: /* @__PURE__ */ new Map() });
|
|
284
|
+
try {
|
|
285
|
+
descend(handle, root, [], !exempt);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
evict(handle, root);
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function detach(handle, parent, child) {
|
|
292
|
+
const rawChild = rawOf(child);
|
|
293
|
+
const record = recordOf(rawChild);
|
|
294
|
+
const membership = record?.memberships.get(handle);
|
|
295
|
+
if (record === void 0 || membership === void 0) return;
|
|
296
|
+
membership.edges -= 1;
|
|
297
|
+
const rawParent = rawOf(parent);
|
|
298
|
+
const parentEdges = membership.parents.get(rawParent);
|
|
299
|
+
if (parentEdges === 1) membership.parents.delete(rawParent);
|
|
300
|
+
else if (parentEdges !== void 0) membership.parents.set(rawParent, parentEdges - 1);
|
|
301
|
+
if (membership.edges > 0) return;
|
|
302
|
+
record.memberships.delete(handle);
|
|
303
|
+
cascade(handle, rawChild, membership.keys);
|
|
304
|
+
}
|
|
305
|
+
function evict(handle, node) {
|
|
306
|
+
const rawNode = rawOf(node);
|
|
307
|
+
const record = recordOf(rawNode);
|
|
308
|
+
const membership = record?.memberships.get(handle);
|
|
309
|
+
if (record === void 0 || membership === void 0) return;
|
|
310
|
+
record.memberships.delete(handle);
|
|
311
|
+
cascade(handle, rawNode, membership.keys);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/handle.ts
|
|
315
|
+
function handleOf(state) {
|
|
316
|
+
const raw = rawOf(state);
|
|
317
|
+
const record = recordOf(raw);
|
|
318
|
+
if (record === void 0) return void 0;
|
|
319
|
+
for (const handle of record.memberships.keys()) {
|
|
320
|
+
if (handle.root === raw) return handle;
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function requireHandle(state, message) {
|
|
325
|
+
const handle = handleOf(state);
|
|
326
|
+
if (handle === void 0) throw new Error(message);
|
|
327
|
+
return handle;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/emit/emitterDeliver.ts
|
|
331
|
+
var runDelivery = (pending, failures) => {
|
|
332
|
+
pending.handle.lastDirty = pending.dirty;
|
|
333
|
+
for (const deliver of pending.deliveries) {
|
|
334
|
+
try {
|
|
335
|
+
deliver(pending.operations);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
failures.push(error);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
var raiseFailures = (failures) => {
|
|
342
|
+
if (failures.length === 0) return;
|
|
343
|
+
if (failures.length > 1) throw new AggregateError(failures, "opshot: listeners failed during delivery");
|
|
344
|
+
throw failures[0];
|
|
345
|
+
};
|
|
346
|
+
var queuedDeliveries = [];
|
|
347
|
+
var deliveryFailures = [];
|
|
348
|
+
var isDraining = false;
|
|
349
|
+
var prepareDelivery = (handle, operations, dirty) => ({
|
|
350
|
+
deliveries: [...handle.subscribers.values()],
|
|
351
|
+
operations,
|
|
352
|
+
handle,
|
|
353
|
+
dirty
|
|
354
|
+
});
|
|
355
|
+
var enqueueDelivery = (pending) => {
|
|
356
|
+
queuedDeliveries.push(pending);
|
|
357
|
+
};
|
|
358
|
+
var drainDeliveries = () => {
|
|
359
|
+
if (isDraining) return;
|
|
360
|
+
isDraining = true;
|
|
361
|
+
let failures = [];
|
|
362
|
+
try {
|
|
363
|
+
while (queuedDeliveries.length > 0) {
|
|
364
|
+
for (const queued of queuedDeliveries.splice(0, queuedDeliveries.length)) {
|
|
365
|
+
runDelivery(queued, deliveryFailures);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} finally {
|
|
369
|
+
isDraining = false;
|
|
370
|
+
failures = deliveryFailures.splice(0, deliveryFailures.length);
|
|
371
|
+
}
|
|
372
|
+
raiseFailures(failures);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/emit/window.ts
|
|
376
|
+
function recordOperation(handle, raw, pending) {
|
|
377
|
+
let byKey = handle.pendingIndex.get(raw);
|
|
378
|
+
if (byKey === void 0) {
|
|
379
|
+
byKey = /* @__PURE__ */ new Map();
|
|
380
|
+
handle.pendingIndex.set(raw, byKey);
|
|
381
|
+
}
|
|
382
|
+
const index = byKey.get(pending.key);
|
|
383
|
+
const existing = index === void 0 ? void 0 : handle.pending[index];
|
|
384
|
+
if (existing !== void 0 && Object.is(existing.meta, pending.meta)) {
|
|
385
|
+
existing.after = pending.after;
|
|
386
|
+
existing.hasAfter = pending.hasAfter;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
handle.pending.push(pending);
|
|
390
|
+
byKey.set(pending.key, handle.pending.length - 1);
|
|
391
|
+
if (handle.scheduledFlush !== void 0) return;
|
|
392
|
+
const run = () => {
|
|
393
|
+
if (handle.scheduledFlush !== run) return;
|
|
394
|
+
flushWindow(handle);
|
|
395
|
+
};
|
|
396
|
+
handle.scheduledFlush = run;
|
|
397
|
+
void Promise.resolve().then(() => {
|
|
398
|
+
if (handle.scheduledFlush !== run) return;
|
|
399
|
+
const emitOn = handle.emitOn;
|
|
400
|
+
if (emitOn === void 0) run();
|
|
401
|
+
else emitOn(run);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
function flushWindow(handle) {
|
|
405
|
+
handle.scheduledFlush = void 0;
|
|
406
|
+
const pending = handle.pending.splice(0);
|
|
407
|
+
handle.pendingIndex.clear();
|
|
408
|
+
const operations = new Array();
|
|
409
|
+
for (const item of pending) {
|
|
410
|
+
if (item.hasBefore === item.hasAfter && (!item.hasBefore || Object.is(item.before, item.after))) continue;
|
|
411
|
+
const node = item.node;
|
|
412
|
+
const operation = item.hasBefore ? item.hasAfter ? { node, key: item.key, meta: item.meta, kind: "change", before: item.before, after: item.after } : { node, key: item.key, meta: item.meta, kind: "delete", before: item.before } : { node, key: item.key, meta: item.meta, kind: "add", after: item.after };
|
|
413
|
+
operations.push(Object.freeze(operation));
|
|
414
|
+
}
|
|
415
|
+
if (operations.length === 0) return;
|
|
416
|
+
const edges = /* @__PURE__ */ new Map();
|
|
417
|
+
const nodes = /* @__PURE__ */ new Set();
|
|
418
|
+
for (const operation of operations) {
|
|
419
|
+
const raw = rawOf(operation.node);
|
|
420
|
+
let keys = edges.get(raw);
|
|
421
|
+
if (keys === void 0) {
|
|
422
|
+
keys = /* @__PURE__ */ new Set();
|
|
423
|
+
edges.set(raw, keys);
|
|
424
|
+
}
|
|
425
|
+
keys.add(operation.key);
|
|
426
|
+
nodes.add(raw);
|
|
427
|
+
}
|
|
428
|
+
const dirty = { edges, nodes };
|
|
429
|
+
const pendingNodes = [...dirty.nodes];
|
|
430
|
+
const visited = /* @__PURE__ */ new Set();
|
|
431
|
+
while (pendingNodes.length > 0) {
|
|
432
|
+
const raw = pendingNodes.pop();
|
|
433
|
+
if (raw === void 0 || visited.has(raw)) continue;
|
|
434
|
+
visited.add(raw);
|
|
435
|
+
bumpVersion(raw);
|
|
436
|
+
const parents = recordOf(raw)?.memberships.get(handle)?.parents;
|
|
437
|
+
if (parents !== void 0) pendingNodes.push(...parents.keys());
|
|
438
|
+
}
|
|
439
|
+
enqueueDelivery(prepareDelivery(handle, operations, dirty));
|
|
440
|
+
drainDeliveries();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/proxy.ts
|
|
444
|
+
var prototypeFunctionOf = (target, key) => {
|
|
445
|
+
for (let holder = Reflect.getPrototypeOf(target); holder !== null; holder = Reflect.getPrototypeOf(holder)) {
|
|
446
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(holder, key);
|
|
447
|
+
if (descriptor === void 0) continue;
|
|
448
|
+
const method = "value" in descriptor ? descriptor.value : void 0;
|
|
449
|
+
return typeof method === "function" ? method : void 0;
|
|
450
|
+
}
|
|
451
|
+
return void 0;
|
|
452
|
+
};
|
|
453
|
+
var isRideAlongKey = (target, key) => {
|
|
454
|
+
if (key === "__proto__") return true;
|
|
455
|
+
if (key === "length") return false;
|
|
456
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
457
|
+
return descriptor?.enumerable === false;
|
|
458
|
+
};
|
|
459
|
+
var writesThroughAccessor = (target, property) => {
|
|
460
|
+
let holder = target;
|
|
461
|
+
while (holder !== null) {
|
|
462
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(holder, property);
|
|
463
|
+
if (descriptor !== void 0) return !("value" in descriptor);
|
|
464
|
+
holder = Reflect.getPrototypeOf(holder);
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
467
|
+
};
|
|
468
|
+
var proxied = (value) => isObjectLike(value) ? recordOf(value)?.proxy ?? value : value;
|
|
469
|
+
var truncatedOwnEntriesOf = (target, next) => {
|
|
470
|
+
if (!Array.isArray(target)) return [];
|
|
471
|
+
const coercible = next === null || typeof next !== "object" && typeof next !== "function";
|
|
472
|
+
const newLength = coercible ? Number(next) : Number.NaN;
|
|
473
|
+
if (!Number.isInteger(newLength) || newLength < 0 || newLength >= target.length) return [];
|
|
474
|
+
const truncated = new Array();
|
|
475
|
+
for (let index = newLength; index < target.length; index += 1) {
|
|
476
|
+
const key = String(index);
|
|
477
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
478
|
+
if (descriptor === void 0 || !("value" in descriptor)) continue;
|
|
479
|
+
truncated.push({
|
|
480
|
+
key,
|
|
481
|
+
value: descriptor.value,
|
|
482
|
+
writable: descriptor.writable === true
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
return truncated;
|
|
486
|
+
};
|
|
487
|
+
var ownDataDescriptor = (target, key) => {
|
|
488
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
489
|
+
if (descriptor === void 0 || !("value" in descriptor)) return { hadPrevious: false, previous: void 0 };
|
|
490
|
+
return { hadPrevious: true, previous: descriptor.value };
|
|
491
|
+
};
|
|
492
|
+
var handler = {
|
|
493
|
+
get(target, key, receiver) {
|
|
494
|
+
const value = Reflect.get(target, key, receiver);
|
|
495
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
496
|
+
const locked = descriptor?.writable === false && descriptor.configurable === false;
|
|
497
|
+
if (locked || !isObjectLike(value)) return value;
|
|
498
|
+
if (typeof value === "function") {
|
|
499
|
+
const method = prototypeFunctionOf(target, key);
|
|
500
|
+
const kind = classifyValue(target);
|
|
501
|
+
if (method !== void 0 && value === method && (kind === "nativeClass" || kind === "privateClass")) {
|
|
502
|
+
const bound = Function.prototype.bind.call(method, target);
|
|
503
|
+
return typeof bound === "function" ? bound : value;
|
|
504
|
+
}
|
|
505
|
+
return value;
|
|
506
|
+
}
|
|
507
|
+
return recordOf(value)?.proxy ?? value;
|
|
508
|
+
},
|
|
509
|
+
set(target, key, value, receiver) {
|
|
510
|
+
if (typeof key !== "string" || writesThroughAccessor(target, key) || isRideAlongKey(target, key)) {
|
|
511
|
+
return Reflect.set(target, key, value, receiver);
|
|
512
|
+
}
|
|
513
|
+
const { hadPrevious, previous } = ownDataDescriptor(target, key);
|
|
514
|
+
const resolved = isObjectLike(value) ? rawOf(value) : value;
|
|
515
|
+
const memberships = membershipsOf(target).map(([handle, membership]) => ({
|
|
516
|
+
handle,
|
|
517
|
+
membership,
|
|
518
|
+
hadKey: membership.keys.has(key)
|
|
519
|
+
}));
|
|
520
|
+
const truncated = key === "length" ? truncatedOwnEntriesOf(target, resolved) : [];
|
|
521
|
+
const previousLength = Array.isArray(target) ? target.length : void 0;
|
|
522
|
+
if (typeof resolved === "function" && memberships.some(({ membership }) => !membership.exempt)) {
|
|
523
|
+
const kind = classifyValue(target);
|
|
524
|
+
if (kind !== "plain" && kind !== "plainArray") throw rejectionError(target, kind, [key]);
|
|
525
|
+
}
|
|
526
|
+
const incoming = resolved !== previous && isTrackedEntry(resolved, true) ? resolved : void 0;
|
|
527
|
+
let rollBack;
|
|
528
|
+
if (incoming !== void 0) {
|
|
529
|
+
const started = new Array();
|
|
530
|
+
rollBack = () => {
|
|
531
|
+
for (const { handle, membership, hadKey } of started) {
|
|
532
|
+
detach(handle, target, incoming);
|
|
533
|
+
if (!hadKey) membership.keys.delete(key);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
try {
|
|
537
|
+
for (const write of memberships) {
|
|
538
|
+
started.push(write);
|
|
539
|
+
attach(write.handle, target, key, incoming, [key]);
|
|
540
|
+
}
|
|
541
|
+
} catch (error) {
|
|
542
|
+
rollBack();
|
|
543
|
+
throw error;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const result = Reflect.set(target, key, resolved, receiver);
|
|
547
|
+
if (!result) {
|
|
548
|
+
rollBack?.();
|
|
549
|
+
return result;
|
|
550
|
+
}
|
|
551
|
+
const meta = currentMeta();
|
|
552
|
+
const node = proxyOf(target);
|
|
553
|
+
for (const { handle, membership, hadKey } of memberships) {
|
|
554
|
+
for (const entry of truncated) {
|
|
555
|
+
if (membership.keys.delete(entry.key) && isObjectLike(entry.value)) detach(handle, target, entry.value);
|
|
556
|
+
recordOperation(handle, target, {
|
|
557
|
+
node,
|
|
558
|
+
key: entry.key,
|
|
559
|
+
meta,
|
|
560
|
+
before: proxied(entry.value),
|
|
561
|
+
hasBefore: true,
|
|
562
|
+
hasAfter: false
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (hadKey && previous !== resolved) {
|
|
566
|
+
if (isObjectLike(previous)) detach(handle, target, previous);
|
|
567
|
+
if (incoming === void 0) membership.keys.delete(key);
|
|
568
|
+
}
|
|
569
|
+
recordOperation(handle, target, {
|
|
570
|
+
node,
|
|
571
|
+
key,
|
|
572
|
+
meta,
|
|
573
|
+
before: proxied(previous),
|
|
574
|
+
after: proxied(resolved),
|
|
575
|
+
hasBefore: hadPrevious,
|
|
576
|
+
hasAfter: true
|
|
577
|
+
});
|
|
578
|
+
if (key !== "length" && Array.isArray(target) && previousLength !== target.length) {
|
|
579
|
+
recordOperation(handle, target, {
|
|
580
|
+
node,
|
|
581
|
+
key: "length",
|
|
582
|
+
meta,
|
|
583
|
+
before: previousLength,
|
|
584
|
+
after: target.length,
|
|
585
|
+
hasBefore: true,
|
|
586
|
+
hasAfter: true
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return result;
|
|
591
|
+
},
|
|
592
|
+
deleteProperty(target, key) {
|
|
593
|
+
if (typeof key !== "string" || isRideAlongKey(target, key)) return Reflect.deleteProperty(target, key);
|
|
594
|
+
const { hadPrevious, previous } = ownDataDescriptor(target, key);
|
|
595
|
+
const result = Reflect.deleteProperty(target, key);
|
|
596
|
+
if (!result || !hadPrevious) return result;
|
|
597
|
+
const meta = currentMeta();
|
|
598
|
+
const node = proxyOf(target);
|
|
599
|
+
for (const [handle, membership] of membershipsOf(target)) {
|
|
600
|
+
if (membership.keys.delete(key) && isObjectLike(previous)) detach(handle, target, previous);
|
|
601
|
+
recordOperation(handle, target, {
|
|
602
|
+
node,
|
|
603
|
+
key,
|
|
604
|
+
meta,
|
|
605
|
+
before: proxied(previous),
|
|
606
|
+
hasBefore: true,
|
|
607
|
+
hasAfter: false
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
return result;
|
|
611
|
+
},
|
|
612
|
+
preventExtensions(target) {
|
|
613
|
+
for (const [handle] of membershipsOf(target)) evict(handle, target);
|
|
614
|
+
return Reflect.preventExtensions(target);
|
|
615
|
+
},
|
|
616
|
+
defineProperty(target, key, descriptor) {
|
|
617
|
+
return Reflect.defineProperty(target, key, descriptor);
|
|
618
|
+
},
|
|
619
|
+
setPrototypeOf(target, prototype) {
|
|
620
|
+
return Reflect.setPrototypeOf(target, prototype);
|
|
621
|
+
},
|
|
622
|
+
has(target, key) {
|
|
623
|
+
return Reflect.has(target, key);
|
|
624
|
+
},
|
|
625
|
+
ownKeys(target) {
|
|
626
|
+
return Reflect.ownKeys(target);
|
|
627
|
+
},
|
|
628
|
+
getOwnPropertyDescriptor(target, key) {
|
|
629
|
+
return Reflect.getOwnPropertyDescriptor(target, key);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// src/createMutableState.ts
|
|
634
|
+
installProxyHandler(handler);
|
|
635
|
+
function createMutableState(properties, options) {
|
|
636
|
+
const incoming = peelReadProxy(properties);
|
|
637
|
+
if (typeof incoming !== "object" || incoming === null) return properties;
|
|
638
|
+
if (isIgnored(incoming)) return properties;
|
|
639
|
+
if (Object.isFrozen(incoming)) return properties;
|
|
640
|
+
const root = rawOf(incoming);
|
|
641
|
+
if (handleOf(incoming) !== void 0) return proxyOf(incoming);
|
|
642
|
+
const strict = options?.strict !== false;
|
|
643
|
+
const exempt = !strict || isUnsafeMarked(root);
|
|
644
|
+
const handle = {
|
|
645
|
+
root,
|
|
646
|
+
strict,
|
|
647
|
+
emitOn: options?.emitOn,
|
|
648
|
+
subscribers: /* @__PURE__ */ new Map(),
|
|
649
|
+
pending: [],
|
|
650
|
+
pendingIndex: /* @__PURE__ */ new Map()
|
|
651
|
+
};
|
|
652
|
+
const proxy = proxyOf(root);
|
|
653
|
+
attachRoot(handle, root, exempt);
|
|
654
|
+
return proxy;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/identity.ts
|
|
658
|
+
var identityRecords = /* @__PURE__ */ new WeakMap();
|
|
659
|
+
var nextInternId = 0;
|
|
660
|
+
function resolveIdentity(value) {
|
|
661
|
+
let current = value;
|
|
662
|
+
while (isObjectLike(current)) {
|
|
663
|
+
const readTarget = getRegisteredReadProxyTarget(current);
|
|
664
|
+
if (readTarget !== void 0 && readTarget !== current) {
|
|
665
|
+
current = readTarget;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const raw = rawOf(current);
|
|
669
|
+
if (raw !== current) {
|
|
670
|
+
current = raw;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
return current;
|
|
676
|
+
}
|
|
677
|
+
var recordFor = (key) => {
|
|
678
|
+
let record = identityRecords.get(key);
|
|
679
|
+
if (record === void 0) {
|
|
680
|
+
record = {};
|
|
681
|
+
identityRecords.set(key, record);
|
|
682
|
+
}
|
|
683
|
+
return record;
|
|
684
|
+
};
|
|
685
|
+
function identify(value) {
|
|
686
|
+
const target = resolveIdentity(value);
|
|
687
|
+
if (!isObjectLike(target)) return value;
|
|
688
|
+
const record = recordFor(target);
|
|
689
|
+
if (record.token !== void 0) return record.token;
|
|
690
|
+
const token = Object.freeze({});
|
|
691
|
+
record.token = token;
|
|
692
|
+
return token;
|
|
693
|
+
}
|
|
694
|
+
var internIdentity = (key) => {
|
|
695
|
+
const resolved = resolveIdentity(key);
|
|
696
|
+
const record = recordFor(resolved);
|
|
697
|
+
if (record.id !== void 0) return record.id;
|
|
698
|
+
const id = nextInternId;
|
|
699
|
+
nextInternId += 1;
|
|
700
|
+
record.id = id;
|
|
701
|
+
return id;
|
|
702
|
+
};
|
|
703
|
+
function isSameIdentity(first, second) {
|
|
704
|
+
const resolvedFirst = resolveIdentity(first);
|
|
705
|
+
const resolvedSecond = resolveIdentity(second);
|
|
706
|
+
return resolvedFirst === resolvedSecond;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/isState.ts
|
|
710
|
+
function isState(value) {
|
|
711
|
+
const resolved = peelReadProxy(value);
|
|
712
|
+
if (typeof resolved !== "object" || resolved === null) return false;
|
|
713
|
+
return recordOf(resolved)?.proxy === resolved;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/emit/emitterListeners.ts
|
|
717
|
+
var bindings = /* @__PURE__ */ new WeakMap();
|
|
718
|
+
function addStateListener(state, listener, deliver) {
|
|
719
|
+
const handle = requireHandle(state, "opshot: subscribe requires a state");
|
|
720
|
+
handle.subscribers.set(listener, deliver);
|
|
721
|
+
const unsubscribe = () => {
|
|
722
|
+
const held = bindings.get(unsubscribe);
|
|
723
|
+
if (held === void 0) return;
|
|
724
|
+
held.handle.subscribers.delete(held.listener);
|
|
725
|
+
bindings.delete(unsubscribe);
|
|
726
|
+
};
|
|
727
|
+
bindings.set(unsubscribe, { handle, listener });
|
|
728
|
+
return unsubscribe;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/subscribe.ts
|
|
732
|
+
function subscribe(state, listener) {
|
|
733
|
+
return addStateListener(state, listener, listener);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// src/tracked/address.ts
|
|
737
|
+
var UnsupportedKeyTypeError = class extends Error {
|
|
738
|
+
constructor(key) {
|
|
739
|
+
super(`opshot: addressOf received unsupported key type ${typeof key}`);
|
|
740
|
+
this.name = "UnsupportedKeyTypeError";
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
var addressOf = (key) => {
|
|
744
|
+
if (key === null) return "z";
|
|
745
|
+
if (key === void 0) return "u";
|
|
746
|
+
switch (typeof key) {
|
|
747
|
+
case "string":
|
|
748
|
+
return `s${key}`;
|
|
749
|
+
case "number":
|
|
750
|
+
return `n${String(key)}`;
|
|
751
|
+
case "bigint":
|
|
752
|
+
return `i${String(key)}`;
|
|
753
|
+
case "boolean":
|
|
754
|
+
return key ? "b1" : "b0";
|
|
755
|
+
case "symbol": {
|
|
756
|
+
const registered = Symbol.keyFor(key);
|
|
757
|
+
if (registered !== void 0) return `r${registered}`;
|
|
758
|
+
return `o${internIdentity(key)}`;
|
|
759
|
+
}
|
|
760
|
+
case "object":
|
|
761
|
+
case "function":
|
|
762
|
+
return `o${internIdentity(key)}`;
|
|
763
|
+
default:
|
|
764
|
+
throw new UnsupportedKeyTypeError(key);
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
export { addressOf, batch, classifyValue, createMutableState, flushWindow, handlesOf, identify, ignore, isDangerousKind, isObjectLike, isSameIdentity, isState, isTrackedEntry, peelReadProxy, proxyOf, rawOf, recordOf, registerReadProxyTarget, requireHandle, subscribe, unsafeTrack, versionOf, walkDataEntries };
|