@ng-org/alien-deepsignals 0.1.2-alpha.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/README.md +476 -0
- package/dist/contents.d.ts +6 -0
- package/dist/contents.d.ts.map +1 -0
- package/dist/contents.js +18 -0
- package/dist/core.d.ts +75 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +113 -0
- package/dist/deepSignal.d.ts +26 -0
- package/dist/deepSignal.d.ts.map +1 -0
- package/dist/deepSignal.js +916 -0
- package/dist/effect.d.ts +5 -0
- package/dist/effect.d.ts.map +1 -0
- package/dist/effect.js +35 -0
- package/dist/hooks/react/index.d.ts +3 -0
- package/dist/hooks/react/index.d.ts.map +1 -0
- package/dist/hooks/react/index.js +8 -0
- package/dist/hooks/react/useDeepSignal.d.ts +14 -0
- package/dist/hooks/react/useDeepSignal.d.ts.map +1 -0
- package/dist/hooks/react/useDeepSignal.js +39 -0
- package/dist/hooks/svelte/index.d.ts +4 -0
- package/dist/hooks/svelte/index.d.ts.map +1 -0
- package/dist/hooks/svelte/index.js +8 -0
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +31 -0
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -0
- package/dist/hooks/svelte/useDeepSignal.svelte.js +74 -0
- package/dist/hooks/vue/index.d.ts +3 -0
- package/dist/hooks/vue/index.d.ts.map +1 -0
- package/dist/hooks/vue/index.js +8 -0
- package/dist/hooks/vue/useDeepSignal.d.ts +15 -0
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -0
- package/dist/hooks/vue/useDeepSignal.js +73 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/iteratorHelpers.d.ts +3 -0
- package/dist/iteratorHelpers.d.ts.map +1 -0
- package/dist/iteratorHelpers.js +38 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +47 -0
- package/dist/watch.d.ts +27 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +72 -0
- package/package.json +84 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
|
|
3
|
+
// All rights reserved.
|
|
4
|
+
// Licensed under the Apache License, Version 2.0
|
|
5
|
+
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
|
|
6
|
+
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
|
|
7
|
+
// at your option. All files in the project carrying such
|
|
8
|
+
// notice may not be copied, modified, or distributed except
|
|
9
|
+
// according to those terms.
|
|
10
|
+
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.isDeepSignal = isDeepSignal;
|
|
13
|
+
exports.deepSignal = deepSignal;
|
|
14
|
+
exports.subscribeDeepMutations = subscribeDeepMutations;
|
|
15
|
+
exports.getDeepSignalRootId = getDeepSignalRootId;
|
|
16
|
+
exports.getDeepSignalVersion = getDeepSignalVersion;
|
|
17
|
+
exports.shallow = shallow;
|
|
18
|
+
exports.setSetEntrySyntheticId = setSetEntrySyntheticId;
|
|
19
|
+
exports.addWithId = addWithId;
|
|
20
|
+
const core_1 = require("./core");
|
|
21
|
+
const iteratorHelpers_1 = require("./iteratorHelpers");
|
|
22
|
+
const rawToProxy = new WeakMap();
|
|
23
|
+
const proxyToMeta = new WeakMap();
|
|
24
|
+
const proxySignals = new WeakMap();
|
|
25
|
+
const iterableSignals = new WeakMap();
|
|
26
|
+
const ignored = new WeakSet();
|
|
27
|
+
const rootStates = new Map();
|
|
28
|
+
const pendingRoots = new Set();
|
|
29
|
+
const supported = new Set([Object, Array, Set]);
|
|
30
|
+
const descriptor = Object.getOwnPropertyDescriptor;
|
|
31
|
+
let blankNodeCounter = 0;
|
|
32
|
+
const wellKnownSymbols = new Set([
|
|
33
|
+
Symbol.asyncDispose,
|
|
34
|
+
Symbol.asyncIterator,
|
|
35
|
+
Symbol.dispose,
|
|
36
|
+
Symbol.hasInstance,
|
|
37
|
+
Symbol.iterator,
|
|
38
|
+
Symbol.isConcatSpreadable,
|
|
39
|
+
Symbol.match,
|
|
40
|
+
Symbol.matchAll,
|
|
41
|
+
Symbol.metadata,
|
|
42
|
+
Symbol.replace,
|
|
43
|
+
Symbol.search,
|
|
44
|
+
Symbol.species,
|
|
45
|
+
Symbol.split,
|
|
46
|
+
Symbol.toPrimitive,
|
|
47
|
+
Symbol.toStringTag,
|
|
48
|
+
Symbol.unscopables,
|
|
49
|
+
]);
|
|
50
|
+
const forcedSyntheticIds = new WeakMap();
|
|
51
|
+
const META_KEY = "__meta__";
|
|
52
|
+
const RAW_KEY = "__raw__";
|
|
53
|
+
const DEFAULT_SYNTHETIC_ID_PROPERTY_NAME = "@id";
|
|
54
|
+
/**
|
|
55
|
+
* Lookup the metadata record backing a proxy (if the value is proxied).
|
|
56
|
+
* Returns undefined for non-object inputs or values that never went through deepSignal().
|
|
57
|
+
*/
|
|
58
|
+
function getMeta(target) {
|
|
59
|
+
if (!target || typeof target !== "object")
|
|
60
|
+
return undefined;
|
|
61
|
+
return proxyToMeta.get(target);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the raw underlying object for a possibly proxied value.
|
|
65
|
+
* Falls back to the value itself when no proxy metadata is attached.
|
|
66
|
+
*/
|
|
67
|
+
function getRaw(value) {
|
|
68
|
+
const meta = getMeta(value);
|
|
69
|
+
return meta?.raw ?? value;
|
|
70
|
+
}
|
|
71
|
+
/** Returns `true` if `value` is an object, array or set and is not in `ignored`. */
|
|
72
|
+
function shouldProxy(value) {
|
|
73
|
+
return (!!value &&
|
|
74
|
+
typeof value === "object" &&
|
|
75
|
+
supported.has(value.constructor) &&
|
|
76
|
+
!ignored.has(value));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Get or create the map in `proxySignals` for key `proxy`.
|
|
80
|
+
*/
|
|
81
|
+
function ensureSignalMap(proxy) {
|
|
82
|
+
if (!proxySignals.has(proxy))
|
|
83
|
+
proxySignals.set(proxy, new Map());
|
|
84
|
+
return proxySignals.get(proxy);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Write a new value into a cached signal, creating it if needed.
|
|
88
|
+
* Does nothing computed signals.
|
|
89
|
+
*/
|
|
90
|
+
function setSignalValue(signals, key, value) {
|
|
91
|
+
if (!signals.has(key)) {
|
|
92
|
+
signals.set(key, (0, core_1.signal)(value));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const existing = signals.get(key);
|
|
96
|
+
if (typeof existing.set === "function") {
|
|
97
|
+
existing.set(value);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Track mutations that affect object iteration order/length. */
|
|
101
|
+
function ensureIterableSignal(target) {
|
|
102
|
+
if (!iterableSignals.has(target))
|
|
103
|
+
iterableSignals.set(target, (0, core_1.signal)(0));
|
|
104
|
+
return iterableSignals.get(target);
|
|
105
|
+
}
|
|
106
|
+
/** Notify all iteration-based subscribers that the container changed shape. */
|
|
107
|
+
function touchIterable(target) {
|
|
108
|
+
if (!iterableSignals.has(target))
|
|
109
|
+
return;
|
|
110
|
+
const sig = iterableSignals.get(target);
|
|
111
|
+
sig.set(sig() + 1);
|
|
112
|
+
}
|
|
113
|
+
/** True when the target has a getter defined for the provided key. */
|
|
114
|
+
function hasGetter(target, key) {
|
|
115
|
+
return typeof descriptor(target, key)?.get === "function";
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Ensure that `signals` has a computed signal for the property with `key` on target.
|
|
119
|
+
* TODO: Why is this necessary?
|
|
120
|
+
*/
|
|
121
|
+
function ensureComputed(signals, target, key, receiver) {
|
|
122
|
+
if (!signals.has(key) && hasGetter(target, key)) {
|
|
123
|
+
signals.set(key, (0, core_1.computed)(() => Reflect.get(target, key, receiver)));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Escape JSON-pointer-like path segments so patches remain unambiguous. */
|
|
127
|
+
function escapePathSegment(segment) {
|
|
128
|
+
return segment
|
|
129
|
+
.replace(/~/g, "~0")
|
|
130
|
+
.replace(/\//g, "~1")
|
|
131
|
+
.replace(/\|/g, "~2");
|
|
132
|
+
}
|
|
133
|
+
/** Filter out well-known symbols that should bypass reactivity. */
|
|
134
|
+
function isReactiveSymbol(key) {
|
|
135
|
+
return typeof key === "symbol" && !wellKnownSymbols.has(key);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Walk the metadata chain to build the patch path for a property access.
|
|
139
|
+
* Handles numbers, symbols (using description) and synthetic ID markers.
|
|
140
|
+
*/
|
|
141
|
+
function buildPath(meta, key, skipEscape = false) {
|
|
142
|
+
const path = [];
|
|
143
|
+
const format = (segment) => {
|
|
144
|
+
if (typeof segment === "symbol") {
|
|
145
|
+
return segment.description ?? segment.toString();
|
|
146
|
+
}
|
|
147
|
+
return segment;
|
|
148
|
+
};
|
|
149
|
+
const push = (segment, synthetic = false) => {
|
|
150
|
+
if (typeof segment === "number") {
|
|
151
|
+
path.unshift(segment);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const normalized = format(segment);
|
|
155
|
+
path.unshift(synthetic || skipEscape
|
|
156
|
+
? normalized
|
|
157
|
+
: escapePathSegment(String(normalized)));
|
|
158
|
+
};
|
|
159
|
+
push(key, skipEscape);
|
|
160
|
+
let cursor = meta;
|
|
161
|
+
while (cursor && cursor.parent && cursor.key !== undefined) {
|
|
162
|
+
push(cursor.key, !!cursor.isSyntheticId);
|
|
163
|
+
cursor = getMeta(cursor.parent);
|
|
164
|
+
}
|
|
165
|
+
return path;
|
|
166
|
+
}
|
|
167
|
+
/** Resolve the path for a container itself (without the child key appended). */
|
|
168
|
+
function resolveContainerPath(meta) {
|
|
169
|
+
if (!meta || !meta.parent || meta.key === undefined)
|
|
170
|
+
return [];
|
|
171
|
+
const parentMeta = getMeta(meta.parent);
|
|
172
|
+
return buildPath(parentMeta, meta.key, !!meta.isSyntheticId);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Build and enqueue patches for a mutation, only if listeners exist on the root.
|
|
176
|
+
*/
|
|
177
|
+
function schedulePatch(meta, build) {
|
|
178
|
+
if (!meta)
|
|
179
|
+
return;
|
|
180
|
+
const state = rootStates.get(meta.root);
|
|
181
|
+
const hasListeners = state &&
|
|
182
|
+
(state.listeners.size > 0 || state.justInTimeListeners.size > 0);
|
|
183
|
+
if (!hasListeners)
|
|
184
|
+
return;
|
|
185
|
+
const result = build();
|
|
186
|
+
if (!result)
|
|
187
|
+
return;
|
|
188
|
+
const patches = Array.isArray(result) ? result : [result];
|
|
189
|
+
if (!patches.length)
|
|
190
|
+
return;
|
|
191
|
+
state.pendingPatches.push(...patches);
|
|
192
|
+
// Notify justInTimeListeners immediately (without version, since batch hasn't finalized).
|
|
193
|
+
state.justInTimeListeners.forEach((cb) => cb({ patches }));
|
|
194
|
+
// Schedule a microtask flush for batched listeners if it hasn't been from previous calls.
|
|
195
|
+
if (state.listeners.size > 0 && !pendingRoots.has(meta.root)) {
|
|
196
|
+
pendingRoots.add(meta.root);
|
|
197
|
+
queueMicrotask(() => {
|
|
198
|
+
pendingRoots.delete(meta.root);
|
|
199
|
+
const state = rootStates.get(meta.root);
|
|
200
|
+
if (!state)
|
|
201
|
+
return;
|
|
202
|
+
if (!state.pendingPatches.length || state.listeners.size === 0) {
|
|
203
|
+
state.pendingPatches.length = 0;
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
state.version += 1;
|
|
207
|
+
const batch = {
|
|
208
|
+
version: state.version,
|
|
209
|
+
patches: state.pendingPatches.slice(),
|
|
210
|
+
};
|
|
211
|
+
state.pendingPatches.length = 0;
|
|
212
|
+
state.listeners.forEach((cb) => cb(batch));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Apply the user-provided propGenerator result, injecting synthetic IDs and extra props.
|
|
218
|
+
*/
|
|
219
|
+
function applyPropGeneratorResult(meta, value, basePath, inSet) {
|
|
220
|
+
if (!value ||
|
|
221
|
+
typeof value !== "object" ||
|
|
222
|
+
value.constructor !== Object ||
|
|
223
|
+
!meta.options?.propGenerator) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const result = meta.options.propGenerator({
|
|
227
|
+
path: basePath,
|
|
228
|
+
inSet,
|
|
229
|
+
object: value,
|
|
230
|
+
});
|
|
231
|
+
if (result.extraProps) {
|
|
232
|
+
Object.entries(result.extraProps).forEach(([k, v]) => {
|
|
233
|
+
value[k] = v;
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (result.syntheticId !== undefined &&
|
|
237
|
+
meta.options.syntheticIdPropertyName &&
|
|
238
|
+
!(meta.options.syntheticIdPropertyName in value)) {
|
|
239
|
+
Object.defineProperty(value, meta.options.syntheticIdPropertyName, {
|
|
240
|
+
value: result.syntheticId,
|
|
241
|
+
enumerable: true,
|
|
242
|
+
configurable: false,
|
|
243
|
+
writable: false,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/** Recursively add synthetic IDs/extra props from prop generator. */
|
|
248
|
+
function initializeObjectTree(meta, value, basePath, inSet) {
|
|
249
|
+
if (!meta.options?.propGenerator)
|
|
250
|
+
return;
|
|
251
|
+
if (!value || typeof value !== "object")
|
|
252
|
+
return;
|
|
253
|
+
if (Array.isArray(value)) {
|
|
254
|
+
value.forEach((entry, idx) => {
|
|
255
|
+
if (entry && typeof entry === "object") {
|
|
256
|
+
initializeObjectTree(meta, entry, [...basePath, idx], false);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (value instanceof Set) {
|
|
262
|
+
for (const entry of value) {
|
|
263
|
+
if (entry && typeof entry === "object") {
|
|
264
|
+
const synthetic = assignSyntheticId(meta, entry, basePath, true);
|
|
265
|
+
initializeObjectTree(meta, entry, [...basePath, synthetic], true);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
if (value.constructor !== Object)
|
|
271
|
+
return;
|
|
272
|
+
applyPropGeneratorResult(meta, value, basePath, inSet);
|
|
273
|
+
Object.keys(value).forEach((childKey) => {
|
|
274
|
+
if (childKey === meta.options.syntheticIdPropertyName)
|
|
275
|
+
return;
|
|
276
|
+
const child = value[childKey];
|
|
277
|
+
if (child && typeof child === "object") {
|
|
278
|
+
initializeObjectTree(meta, child, [...basePath, childKey], false);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/** Apply prop generator side-effects only when the root has no live subscribers. */
|
|
283
|
+
function initializeObjectTreeIfNoListeners(meta, basePath, value, inSet) {
|
|
284
|
+
if (!meta || !meta.options?.propGenerator)
|
|
285
|
+
return;
|
|
286
|
+
if (!value || typeof value !== "object")
|
|
287
|
+
return;
|
|
288
|
+
const state = rootStates.get(meta.root);
|
|
289
|
+
if (state &&
|
|
290
|
+
(state.listeners.size > 0 || state.justInTimeListeners.size > 0))
|
|
291
|
+
return;
|
|
292
|
+
initializeObjectTree(meta, value, basePath ?? [], inSet);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Return (or create) a proxy for a nested value.
|
|
296
|
+
* Ensures the linkage between parent and child in metadata.
|
|
297
|
+
* Does not proxy and returns `value` if @see shouldProxy returns false.
|
|
298
|
+
* Returns value if parent has no metadata record.
|
|
299
|
+
*/
|
|
300
|
+
function ensureChildProxy(value, parentProxy, key, isSyntheticId = false) {
|
|
301
|
+
if (!shouldProxy(value))
|
|
302
|
+
return value;
|
|
303
|
+
if (rawToProxy.has(value)) {
|
|
304
|
+
const proxied = rawToProxy.get(value);
|
|
305
|
+
const proxiedMeta = getMeta(proxied);
|
|
306
|
+
if (proxiedMeta) {
|
|
307
|
+
proxiedMeta.parent = parentProxy;
|
|
308
|
+
proxiedMeta.key = key;
|
|
309
|
+
proxiedMeta.isSyntheticId = isSyntheticId;
|
|
310
|
+
}
|
|
311
|
+
return proxied;
|
|
312
|
+
}
|
|
313
|
+
const parentMeta = getMeta(parentProxy);
|
|
314
|
+
if (!parentMeta)
|
|
315
|
+
return value;
|
|
316
|
+
// Create proxy if none exists yet.
|
|
317
|
+
const proxy = createProxy(value, parentMeta.root, parentMeta.options, parentProxy, key, isSyntheticId);
|
|
318
|
+
return proxy;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Sets `idForObject: new WeakMap(), objectForId: new Map()`
|
|
322
|
+
* to `meta.setInfo` if it does not exist yet.
|
|
323
|
+
*/
|
|
324
|
+
function ensureSetInfo(meta) {
|
|
325
|
+
if (!meta.setInfo) {
|
|
326
|
+
meta.setInfo = {
|
|
327
|
+
idForObject: new WeakMap(),
|
|
328
|
+
objectForId: new Map(),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
return meta.setInfo;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Assign (or reuse) a synthetic identifier for a Set entry, respecting user options:
|
|
335
|
+
* - Use user-provided propGenerator for synthetic ids and add add returned extra properties
|
|
336
|
+
* - Check if the object has a property of `syntheticIdPropertyName` (default `@id`)
|
|
337
|
+
* - Use a blank node id as a fallback.
|
|
338
|
+
* - Add object and id to `idForObject` and `objectForId` maps.
|
|
339
|
+
*/
|
|
340
|
+
function assignSyntheticId(meta, entry, path, inSet) {
|
|
341
|
+
const rawEntry = getRaw(entry);
|
|
342
|
+
if (!rawEntry || typeof rawEntry !== "object") {
|
|
343
|
+
return rawEntry;
|
|
344
|
+
}
|
|
345
|
+
const info = ensureSetInfo(meta);
|
|
346
|
+
if (info.idForObject.has(rawEntry))
|
|
347
|
+
return info.idForObject.get(rawEntry);
|
|
348
|
+
let synthetic = forcedSyntheticIds.get(rawEntry);
|
|
349
|
+
const generatorValue = meta.options?.propGenerator?.({
|
|
350
|
+
path,
|
|
351
|
+
inSet,
|
|
352
|
+
object: rawEntry,
|
|
353
|
+
});
|
|
354
|
+
// If the propGenerator returned a syntheticId, use it.
|
|
355
|
+
if (synthetic === undefined && generatorValue?.syntheticId !== undefined) {
|
|
356
|
+
synthetic = generatorValue.syntheticId;
|
|
357
|
+
}
|
|
358
|
+
// Add extra props from propGenerator (if present).
|
|
359
|
+
if (generatorValue?.extraProps &&
|
|
360
|
+
rawEntry &&
|
|
361
|
+
typeof rawEntry === "object") {
|
|
362
|
+
Object.entries(generatorValue.extraProps).forEach(([k, v]) => {
|
|
363
|
+
rawEntry[k] = v;
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
const idPropName = meta.options?.syntheticIdPropertyName;
|
|
367
|
+
// If synthetic id is still undefined, try to get it
|
|
368
|
+
// from `syntheticIdPropertyName` (default `@id` property).
|
|
369
|
+
if (synthetic === undefined &&
|
|
370
|
+
idPropName &&
|
|
371
|
+
rawEntry &&
|
|
372
|
+
typeof rawEntry === "object" &&
|
|
373
|
+
rawEntry[idPropName] !== undefined) {
|
|
374
|
+
synthetic = rawEntry[idPropName];
|
|
375
|
+
}
|
|
376
|
+
// If `synthetic` still undefined, add a blank node id.
|
|
377
|
+
if (synthetic === undefined) {
|
|
378
|
+
synthetic = `_s${++blankNodeCounter}`;
|
|
379
|
+
}
|
|
380
|
+
const idString = String(synthetic);
|
|
381
|
+
// Add mappings for `id -> object` and `object -> id`.
|
|
382
|
+
info.idForObject.set(rawEntry, idString);
|
|
383
|
+
info.objectForId.set(idString, rawEntry);
|
|
384
|
+
// Add synthetic id to `idPropertyName` property (default `@id`)
|
|
385
|
+
// if not set.
|
|
386
|
+
if (idPropName &&
|
|
387
|
+
rawEntry &&
|
|
388
|
+
typeof rawEntry === "object" &&
|
|
389
|
+
!(idPropName in rawEntry)) {
|
|
390
|
+
Object.defineProperty(rawEntry, idPropName, {
|
|
391
|
+
value: idString,
|
|
392
|
+
enumerable: true,
|
|
393
|
+
configurable: false,
|
|
394
|
+
writable: false,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
return idString;
|
|
398
|
+
}
|
|
399
|
+
/** Create the appropriate proxy (object vs Set) and track its metadata. */
|
|
400
|
+
function createProxy(target, root, options, parent, key, isSyntheticId) {
|
|
401
|
+
const handlers = target instanceof Set ? setHandlers : objectHandlers;
|
|
402
|
+
const proxy = new Proxy(target, handlers);
|
|
403
|
+
const meta = {
|
|
404
|
+
raw: target,
|
|
405
|
+
parent,
|
|
406
|
+
key,
|
|
407
|
+
isSyntheticId,
|
|
408
|
+
root,
|
|
409
|
+
options,
|
|
410
|
+
};
|
|
411
|
+
proxyToMeta.set(proxy, meta);
|
|
412
|
+
proxySignals.set(proxy, new Map());
|
|
413
|
+
rawToProxy.set(target, proxy);
|
|
414
|
+
return proxy;
|
|
415
|
+
}
|
|
416
|
+
/** Normalize a value prior to writes, ensuring nested objects are proxied. */
|
|
417
|
+
function ensureValueForWrite(value, receiver, key) {
|
|
418
|
+
const rawValue = getRaw(value);
|
|
419
|
+
const proxied = shouldProxy(rawValue)
|
|
420
|
+
? ensureChildProxy(rawValue, receiver, key)
|
|
421
|
+
: rawValue;
|
|
422
|
+
return { raw: rawValue, proxied };
|
|
423
|
+
}
|
|
424
|
+
/** Return primitive literals (string/number/boolean) for patch serialization. */
|
|
425
|
+
function snapshotLiteral(value) {
|
|
426
|
+
if (typeof value === "string" ||
|
|
427
|
+
typeof value === "number" ||
|
|
428
|
+
typeof value === "boolean") {
|
|
429
|
+
return value;
|
|
430
|
+
}
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Emit a recursive patch sequence for a whole object, array, or set
|
|
435
|
+
* that was added to the deepSignal object.
|
|
436
|
+
*
|
|
437
|
+
*/
|
|
438
|
+
function emitPatchesForNew(value, meta, basePath, inSet = false) {
|
|
439
|
+
applyPropGeneratorResult(meta, value, basePath, inSet);
|
|
440
|
+
if (value === null || value === undefined || typeof value !== "object") {
|
|
441
|
+
const literal = snapshotLiteral(value);
|
|
442
|
+
if (literal === undefined)
|
|
443
|
+
return [];
|
|
444
|
+
return [
|
|
445
|
+
{
|
|
446
|
+
path: basePath,
|
|
447
|
+
op: "add",
|
|
448
|
+
value: literal,
|
|
449
|
+
},
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
const patches = [
|
|
453
|
+
{
|
|
454
|
+
path: basePath,
|
|
455
|
+
op: "add",
|
|
456
|
+
type: value instanceof Set ? "set" : "object",
|
|
457
|
+
value: value instanceof Set ? [] : undefined,
|
|
458
|
+
},
|
|
459
|
+
];
|
|
460
|
+
// The id property name, usually `@id`
|
|
461
|
+
const idPropName = meta.options.syntheticIdPropertyName;
|
|
462
|
+
if (idPropName in value) {
|
|
463
|
+
const literal = snapshotLiteral(value[idPropName]);
|
|
464
|
+
if (literal !== undefined) {
|
|
465
|
+
patches.push({
|
|
466
|
+
path: [...basePath, idPropName],
|
|
467
|
+
op: "add",
|
|
468
|
+
value: literal,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// For array, recurse
|
|
473
|
+
if (Array.isArray(value)) {
|
|
474
|
+
value.forEach((entry, idx) => {
|
|
475
|
+
patches.push(...emitPatchesForNew(entry, meta, [...basePath, idx]));
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
else if (value instanceof Set) {
|
|
479
|
+
const setMeta = ensureSetInfo(meta);
|
|
480
|
+
for (const entry of value) {
|
|
481
|
+
if (entry && typeof entry === "object") {
|
|
482
|
+
const synthetic = assignSyntheticId(meta, entry, basePath, true);
|
|
483
|
+
setMeta.objectForId.set(String(synthetic), entry);
|
|
484
|
+
patches.push(...emitPatchesForNew(entry, meta, [...basePath, synthetic], true));
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
const literal = snapshotLiteral(entry);
|
|
488
|
+
if (literal !== undefined) {
|
|
489
|
+
patches.push({
|
|
490
|
+
path: basePath,
|
|
491
|
+
op: "add",
|
|
492
|
+
type: "set",
|
|
493
|
+
value: [literal],
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
Object.keys(value).forEach((childKey) => {
|
|
501
|
+
if (childKey === idPropName)
|
|
502
|
+
return;
|
|
503
|
+
patches.push(...emitPatchesForNew(value[childKey], meta, [
|
|
504
|
+
...basePath,
|
|
505
|
+
childKey,
|
|
506
|
+
]));
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
return patches;
|
|
510
|
+
}
|
|
511
|
+
/** Proxy handler driving reactivity for plain objects and arrays. */
|
|
512
|
+
const objectHandlers = {
|
|
513
|
+
get(target, key, receiver) {
|
|
514
|
+
// Handle meta keys
|
|
515
|
+
if (key === RAW_KEY)
|
|
516
|
+
return getMeta(receiver)?.raw ?? target;
|
|
517
|
+
if (key === META_KEY)
|
|
518
|
+
return getMeta(receiver);
|
|
519
|
+
// TODO: Why are we doing this?
|
|
520
|
+
if (typeof key === "symbol") {
|
|
521
|
+
if (key === Symbol.iterator) {
|
|
522
|
+
const iterableSig = ensureIterableSignal(target);
|
|
523
|
+
iterableSig();
|
|
524
|
+
}
|
|
525
|
+
if (!isReactiveSymbol(key))
|
|
526
|
+
return Reflect.get(target, key, receiver);
|
|
527
|
+
}
|
|
528
|
+
// Get object map from key to signal.
|
|
529
|
+
const signals = ensureSignalMap(receiver);
|
|
530
|
+
// TODO: Why are we doing this?
|
|
531
|
+
// Ensure that target object is signal.
|
|
532
|
+
ensureComputed(signals, target, key, receiver);
|
|
533
|
+
// Add signal if it does not exist already and did not have a getter.
|
|
534
|
+
if (!signals.has(key)) {
|
|
535
|
+
let rawValue = Reflect.get(target, key, receiver);
|
|
536
|
+
if (typeof rawValue === "function")
|
|
537
|
+
return rawValue.bind(receiver ?? target);
|
|
538
|
+
rawValue = shouldProxy(rawValue)
|
|
539
|
+
? ensureChildProxy(rawValue, receiver, key)
|
|
540
|
+
: rawValue;
|
|
541
|
+
signals.set(key, (0, core_1.signal)(rawValue));
|
|
542
|
+
}
|
|
543
|
+
// Call and return signal
|
|
544
|
+
const sig = signals.get(key);
|
|
545
|
+
return sig();
|
|
546
|
+
},
|
|
547
|
+
set(target, key, value, receiver) {
|
|
548
|
+
// Skip reactivity for symbols.
|
|
549
|
+
if (typeof key === "symbol" && !isReactiveSymbol(key))
|
|
550
|
+
return Reflect.set(target, key, value, receiver);
|
|
551
|
+
const meta = getMeta(receiver);
|
|
552
|
+
if (meta?.options?.readOnlyProps?.includes(String(key))) {
|
|
553
|
+
throw new Error(`Cannot modify readonly property '${String(key)}'`);
|
|
554
|
+
}
|
|
555
|
+
const path = meta ? buildPath(meta, key) : undefined;
|
|
556
|
+
const desc = descriptor(target, key);
|
|
557
|
+
const hasAccessor = !!desc &&
|
|
558
|
+
(typeof desc.get === "function" || typeof desc.set === "function");
|
|
559
|
+
const { raw, proxied } = ensureValueForWrite(value, receiver, key);
|
|
560
|
+
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
561
|
+
const previous = hadKey ? target[key] : undefined;
|
|
562
|
+
const result = Reflect.set(target, key, raw, receiver);
|
|
563
|
+
if (!hasAccessor) {
|
|
564
|
+
const signals = ensureSignalMap(receiver);
|
|
565
|
+
setSignalValue(signals, key, proxied);
|
|
566
|
+
}
|
|
567
|
+
if (!hadKey)
|
|
568
|
+
touchIterable(target);
|
|
569
|
+
if (meta && path && typeof raw === "object") {
|
|
570
|
+
initializeObjectTreeIfNoListeners(meta, path, raw, false);
|
|
571
|
+
}
|
|
572
|
+
schedulePatch(meta, () => {
|
|
573
|
+
const resolvedPath = path ?? buildPath(meta, key);
|
|
574
|
+
if (!hadKey || typeof raw === "object") {
|
|
575
|
+
return emitPatchesForNew(raw, meta, resolvedPath);
|
|
576
|
+
}
|
|
577
|
+
if (snapshotLiteral(raw) === undefined)
|
|
578
|
+
return undefined;
|
|
579
|
+
return {
|
|
580
|
+
path: resolvedPath,
|
|
581
|
+
op: "add",
|
|
582
|
+
value: raw,
|
|
583
|
+
};
|
|
584
|
+
});
|
|
585
|
+
return result;
|
|
586
|
+
},
|
|
587
|
+
deleteProperty(target, key) {
|
|
588
|
+
if (typeof key === "symbol" && !isReactiveSymbol(key))
|
|
589
|
+
return Reflect.deleteProperty(target, key);
|
|
590
|
+
const receiver = rawToProxy.get(target);
|
|
591
|
+
const meta = receiver ? getMeta(receiver) : undefined;
|
|
592
|
+
const hadKey = Object.prototype.hasOwnProperty.call(target, key);
|
|
593
|
+
const result = Reflect.deleteProperty(target, key);
|
|
594
|
+
if (hadKey) {
|
|
595
|
+
if (receiver && proxySignals.has(receiver)) {
|
|
596
|
+
const signals = proxySignals.get(receiver);
|
|
597
|
+
const existing = signals.get(key);
|
|
598
|
+
if (existing &&
|
|
599
|
+
typeof existing.set === "function") {
|
|
600
|
+
existing.set(undefined);
|
|
601
|
+
}
|
|
602
|
+
signals.delete(key);
|
|
603
|
+
}
|
|
604
|
+
touchIterable(target);
|
|
605
|
+
schedulePatch(meta, () => ({
|
|
606
|
+
path: buildPath(meta, key),
|
|
607
|
+
op: "remove",
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
610
|
+
return result;
|
|
611
|
+
},
|
|
612
|
+
ownKeys(target) {
|
|
613
|
+
const sig = ensureIterableSignal(target);
|
|
614
|
+
sig();
|
|
615
|
+
return Reflect.ownKeys(target);
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
/**
|
|
619
|
+
* Guarantee Set iteration always surfaces proxies, even when raw values were stored.
|
|
620
|
+
*/
|
|
621
|
+
function ensureEntryProxy(receiver, entry, syntheticKey, meta) {
|
|
622
|
+
return shouldProxy(entry)
|
|
623
|
+
? ensureChildProxy(entry, receiver, syntheticKey, true)
|
|
624
|
+
: entry;
|
|
625
|
+
}
|
|
626
|
+
/** Wrap the underlying Set iterator so each value is proxied before leaving the trap. */
|
|
627
|
+
function createSetIterator(target, receiver, mapValue) {
|
|
628
|
+
const iterator = target.values();
|
|
629
|
+
const iterableSignal = ensureIterableSignal(target);
|
|
630
|
+
iterableSignal();
|
|
631
|
+
return (0, iteratorHelpers_1.createIteratorWithHelpers)(() => {
|
|
632
|
+
const next = iterator.next();
|
|
633
|
+
if (next.done)
|
|
634
|
+
return next;
|
|
635
|
+
const meta = getMeta(receiver);
|
|
636
|
+
const proxied = ensureEntryProxy(receiver, next.value, assignSyntheticId(meta, next.value, [], true), meta);
|
|
637
|
+
return {
|
|
638
|
+
value: mapValue(proxied),
|
|
639
|
+
done: false,
|
|
640
|
+
};
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
/** Proxy handler providing deep-signal semantics for native Set instances. */
|
|
644
|
+
const setHandlers = {
|
|
645
|
+
get(target, key, receiver) {
|
|
646
|
+
if (key === RAW_KEY)
|
|
647
|
+
return getMeta(receiver)?.raw ?? target;
|
|
648
|
+
if (key === META_KEY)
|
|
649
|
+
return getMeta(receiver);
|
|
650
|
+
if (key === "size") {
|
|
651
|
+
const sig = ensureIterableSignal(target);
|
|
652
|
+
sig();
|
|
653
|
+
return target.size;
|
|
654
|
+
}
|
|
655
|
+
if (key === "first") {
|
|
656
|
+
return function first() {
|
|
657
|
+
const iterableSig = ensureIterableSignal(target);
|
|
658
|
+
iterableSig();
|
|
659
|
+
const iterator = target.values().next();
|
|
660
|
+
if (iterator.done)
|
|
661
|
+
return undefined;
|
|
662
|
+
const meta = getMeta(receiver);
|
|
663
|
+
return ensureEntryProxy(receiver, iterator.value, assignSyntheticId(meta, iterator.value, [], true), meta);
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
if (key === "getById") {
|
|
667
|
+
return function getById(id) {
|
|
668
|
+
const iterableSig = ensureIterableSignal(target);
|
|
669
|
+
iterableSig();
|
|
670
|
+
const meta = getMeta(receiver);
|
|
671
|
+
if (!meta?.setInfo)
|
|
672
|
+
return undefined;
|
|
673
|
+
const entry = meta.setInfo.objectForId.get(String(id));
|
|
674
|
+
if (!entry)
|
|
675
|
+
return undefined;
|
|
676
|
+
return ensureEntryProxy(receiver, entry, String(id), meta);
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
if (key === "getBy") {
|
|
680
|
+
return function getBy(graphIri, subjectIri) {
|
|
681
|
+
const iterableSig = ensureIterableSignal(target);
|
|
682
|
+
iterableSig();
|
|
683
|
+
return this.getById(`${graphIri}|${subjectIri}`);
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
if (key === "add") {
|
|
687
|
+
return function add(value) {
|
|
688
|
+
const meta = getMeta(receiver);
|
|
689
|
+
const containerPath = resolveContainerPath(meta);
|
|
690
|
+
const rawValue = getRaw(value);
|
|
691
|
+
const sizeBefore = target.size;
|
|
692
|
+
const result = target.add(rawValue);
|
|
693
|
+
if (target.size !== sizeBefore) {
|
|
694
|
+
touchIterable(target);
|
|
695
|
+
if (rawValue && typeof rawValue === "object") {
|
|
696
|
+
const synthetic = assignSyntheticId(meta, rawValue, containerPath, true);
|
|
697
|
+
initializeObjectTreeIfNoListeners(meta, [...containerPath, synthetic], rawValue, true);
|
|
698
|
+
ensureEntryProxy(receiver, rawValue, synthetic, meta);
|
|
699
|
+
schedulePatch(meta, () => emitPatchesForNew(rawValue, meta, [...containerPath, synthetic], true));
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
const literal = snapshotLiteral(rawValue);
|
|
703
|
+
if (literal !== undefined) {
|
|
704
|
+
schedulePatch(meta, () => ({
|
|
705
|
+
path: containerPath,
|
|
706
|
+
op: "add",
|
|
707
|
+
type: "set",
|
|
708
|
+
value: [literal],
|
|
709
|
+
}));
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return receiver;
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
if (key === "delete") {
|
|
717
|
+
return function deleteEntry(value) {
|
|
718
|
+
const meta = getMeta(receiver);
|
|
719
|
+
const containerPath = resolveContainerPath(meta);
|
|
720
|
+
const rawValue = getRaw(value);
|
|
721
|
+
const synthetic = rawValue && typeof rawValue === "object"
|
|
722
|
+
? ensureSetInfo(meta).idForObject.get(rawValue)
|
|
723
|
+
: rawValue;
|
|
724
|
+
const existed = target.delete(rawValue);
|
|
725
|
+
if (existed && synthetic !== undefined) {
|
|
726
|
+
touchIterable(target);
|
|
727
|
+
if (rawValue && typeof rawValue === "object") {
|
|
728
|
+
schedulePatch(meta, () => ({
|
|
729
|
+
path: [...containerPath, synthetic],
|
|
730
|
+
op: "remove",
|
|
731
|
+
}));
|
|
732
|
+
if (meta.setInfo) {
|
|
733
|
+
meta.setInfo.objectForId.delete(String(synthetic));
|
|
734
|
+
meta.setInfo.idForObject.delete(rawValue);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
schedulePatch(meta, () => ({
|
|
739
|
+
path: containerPath,
|
|
740
|
+
op: "remove",
|
|
741
|
+
type: "set",
|
|
742
|
+
value: rawValue,
|
|
743
|
+
}));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return existed;
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
if (key === "clear") {
|
|
750
|
+
return function clear() {
|
|
751
|
+
const meta = getMeta(receiver);
|
|
752
|
+
const containerPath = resolveContainerPath(meta);
|
|
753
|
+
if (meta.setInfo) {
|
|
754
|
+
meta.setInfo.objectForId.clear();
|
|
755
|
+
meta.setInfo.idForObject = new WeakMap();
|
|
756
|
+
}
|
|
757
|
+
target.clear();
|
|
758
|
+
touchIterable(target);
|
|
759
|
+
schedulePatch(meta, () => ({
|
|
760
|
+
path: containerPath,
|
|
761
|
+
op: "add",
|
|
762
|
+
type: "set",
|
|
763
|
+
value: [],
|
|
764
|
+
}));
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
if (key === Symbol.iterator) {
|
|
768
|
+
return function iterator() {
|
|
769
|
+
return createSetIterator(target, receiver, (value) => value);
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
if (key === "values" || key === "keys") {
|
|
773
|
+
return function values() {
|
|
774
|
+
return createSetIterator(target, receiver, (value) => value);
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (key === "entries") {
|
|
778
|
+
return function entries() {
|
|
779
|
+
return createSetIterator(target, receiver, (value) => [
|
|
780
|
+
value,
|
|
781
|
+
value,
|
|
782
|
+
]);
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
if (typeof key === "string" && iteratorHelpers_1.iteratorHelperKeys.has(key)) {
|
|
786
|
+
return function iteratorHelper(...args) {
|
|
787
|
+
const iterator = createSetIterator(target, receiver, (value) => value);
|
|
788
|
+
const helper = iterator[key];
|
|
789
|
+
if (typeof helper !== "function") {
|
|
790
|
+
throw new TypeError(`Iterator helper '${String(key)}' is not available`);
|
|
791
|
+
}
|
|
792
|
+
return helper.apply(iterator, args);
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
if (key === "forEach") {
|
|
796
|
+
return function forEach(callback, thisArg) {
|
|
797
|
+
const iterableSig = ensureIterableSignal(target);
|
|
798
|
+
iterableSig();
|
|
799
|
+
const meta = getMeta(receiver);
|
|
800
|
+
let index = 0;
|
|
801
|
+
const expectsIteratorSignature = callback.length <= 2;
|
|
802
|
+
const iteratorCallback = callback;
|
|
803
|
+
target.forEach((entry) => {
|
|
804
|
+
const proxied = ensureEntryProxy(receiver, entry, assignSyntheticId(meta, entry, [], true), meta);
|
|
805
|
+
if (expectsIteratorSignature) {
|
|
806
|
+
iteratorCallback.call(thisArg, proxied, index++);
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
callback.call(thisArg, proxied, proxied, receiver);
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
if (key === "has") {
|
|
815
|
+
return function has(value) {
|
|
816
|
+
return target.has(getRaw(value));
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
return Reflect.get(target, key, receiver);
|
|
820
|
+
},
|
|
821
|
+
};
|
|
822
|
+
/** Runtime guard that checks whether a value is a deepSignal proxy. */
|
|
823
|
+
function isDeepSignal(value) {
|
|
824
|
+
return !!getMeta(value);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Create a deep reactive proxy for objects, arrays or Sets.
|
|
828
|
+
* Returns the input itself, if it's a deepSignal already.
|
|
829
|
+
* Throws if provided with unsupported input types.
|
|
830
|
+
*/
|
|
831
|
+
function deepSignal(input, options) {
|
|
832
|
+
if (isDeepSignal(input))
|
|
833
|
+
return input;
|
|
834
|
+
if (!shouldProxy(input))
|
|
835
|
+
throw new Error("deepSignal() expects an object, array, or Set");
|
|
836
|
+
if (rawToProxy.has(input))
|
|
837
|
+
return rawToProxy.get(input);
|
|
838
|
+
const root = Symbol("deepSignalRoot");
|
|
839
|
+
const rootState = {
|
|
840
|
+
options: {
|
|
841
|
+
syntheticIdPropertyName: DEFAULT_SYNTHETIC_ID_PROPERTY_NAME,
|
|
842
|
+
...options,
|
|
843
|
+
},
|
|
844
|
+
version: 0,
|
|
845
|
+
listeners: new Set(),
|
|
846
|
+
justInTimeListeners: new Set(),
|
|
847
|
+
pendingPatches: [],
|
|
848
|
+
};
|
|
849
|
+
rootStates.set(root, rootState);
|
|
850
|
+
const proxy = createProxy(input, root, rootState.options);
|
|
851
|
+
return proxy;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Low-level function, you should probably use `watch` instead.
|
|
855
|
+
*
|
|
856
|
+
* Register a deep mutation subscriber for the provided root or proxy.
|
|
857
|
+
*/
|
|
858
|
+
function subscribeDeepMutations(root, cb, triggerInstantly = false) {
|
|
859
|
+
const rootId = typeof root === "symbol" ? root : getDeepSignalRootId(root);
|
|
860
|
+
if (!rootId)
|
|
861
|
+
throw new Error("subscribeDeepMutations() expects a deepSignal root");
|
|
862
|
+
const state = rootStates.get(rootId);
|
|
863
|
+
if (!state)
|
|
864
|
+
throw new Error("Unknown deepSignal root");
|
|
865
|
+
// Add to listeners / justInTimeListeners.
|
|
866
|
+
if (triggerInstantly) {
|
|
867
|
+
state.justInTimeListeners.add(cb);
|
|
868
|
+
return () => {
|
|
869
|
+
state.justInTimeListeners.delete(cb);
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
state.listeners.add(cb);
|
|
874
|
+
return () => {
|
|
875
|
+
state.listeners.delete(cb);
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
/** Return the root identifier symbol for a deepSignal proxy (if any). */
|
|
880
|
+
function getDeepSignalRootId(value) {
|
|
881
|
+
return getMeta(value)?.root;
|
|
882
|
+
}
|
|
883
|
+
/** Retrieve the current patch version for a deepSignal root (if tracked). */
|
|
884
|
+
function getDeepSignalVersion(root) {
|
|
885
|
+
const rootId = typeof root === "symbol" ? root : getDeepSignalRootId(root);
|
|
886
|
+
if (!rootId)
|
|
887
|
+
return undefined;
|
|
888
|
+
return rootStates.get(rootId)?.version;
|
|
889
|
+
}
|
|
890
|
+
/** Mark an object so deepSignal skips proxying it (shallow boundary). */
|
|
891
|
+
function shallow(obj) {
|
|
892
|
+
ignored.add(obj);
|
|
893
|
+
return obj;
|
|
894
|
+
}
|
|
895
|
+
/** Force a specific synthetic ID to be used for a Set entry prior to insertion. */
|
|
896
|
+
function setSetEntrySyntheticId(obj, id) {
|
|
897
|
+
if (!obj || typeof obj !== "object")
|
|
898
|
+
return;
|
|
899
|
+
forcedSyntheticIds.set(getRaw(obj), String(id));
|
|
900
|
+
}
|
|
901
|
+
/** Convenience helper to add an entry to a proxied Set with a pre-defined synthetic ID. */
|
|
902
|
+
function addWithId(set, entry, id) {
|
|
903
|
+
if (entry && typeof entry === "object") {
|
|
904
|
+
setSetEntrySyntheticId(entry, id);
|
|
905
|
+
}
|
|
906
|
+
set.add(entry);
|
|
907
|
+
if (entry && typeof entry === "object") {
|
|
908
|
+
const getter = set?.getById;
|
|
909
|
+
if (typeof getter === "function") {
|
|
910
|
+
const proxied = getter.call(set, String(id));
|
|
911
|
+
if (proxied)
|
|
912
|
+
return proxied;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return entry;
|
|
916
|
+
}
|