react-fine-grained-signals 0.1.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/LICENSE +21 -0
- package/README.ja.md +53 -0
- package/README.md +54 -0
- package/dist/base-BZTnpU8U.js +286 -0
- package/dist/base-BZTnpU8U.js.map +1 -0
- package/dist/base-Bx5ahfW2.d.ts +24 -0
- package/dist/hooks-BFIWh7AD.js +914 -0
- package/dist/hooks-BFIWh7AD.js.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +4 -0
- package/dist/jsx-D9U6dSwL.js +730 -0
- package/dist/jsx-D9U6dSwL.js.map +1 -0
- package/dist/jsx-Xp5q2y97.d.ts +33 -0
- package/dist/jsx-dev-runtime.d.ts +7 -0
- package/dist/jsx-dev-runtime.js +11 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.d.ts +9 -0
- package/dist/jsx-runtime.js +11 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/render-tracking-DCzw57BW.js +96 -0
- package/dist/render-tracking-DCzw57BW.js.map +1 -0
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +2 -0
- package/dist/use-signals-8i2B-94o.js +173 -0
- package/dist/use-signals-8i2B-94o.js.map +1 -0
- package/dist/use-signals-CBDbbAbV.d.ts +24 -0
- package/dist/utils.d.ts +78 -0
- package/dist/utils.js +91 -0
- package/dist/utils.js.map +1 -0
- package/package.json +114 -0
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
import { a as untrackedRender, n as hasActiveRenderCollector } from "./render-tracking-DCzw57BW.js";
|
|
2
|
+
import { a as effect$1, c as signal$1, i as computed$1, l as untracked, n as SignalImpl, o as isSignal, r as batch, s as registerSignal, t as SIGNAL_BRAND } from "./base-BZTnpU8U.js";
|
|
3
|
+
import { getActiveSub, getBatchDepth } from "alien-signals";
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
5
|
+
//#region src/core/deep-signal.ts
|
|
6
|
+
const ARRAY_MUTATORS = /* @__PURE__ */ new Set([
|
|
7
|
+
"copyWithin",
|
|
8
|
+
"fill",
|
|
9
|
+
"pop",
|
|
10
|
+
"push",
|
|
11
|
+
"reverse",
|
|
12
|
+
"shift",
|
|
13
|
+
"sort",
|
|
14
|
+
"splice",
|
|
15
|
+
"unshift"
|
|
16
|
+
]);
|
|
17
|
+
const proxyToRaw = /* @__PURE__ */ new WeakMap();
|
|
18
|
+
const rawToMetadata = /* @__PURE__ */ new WeakMap();
|
|
19
|
+
const readonlyMapViews = /* @__PURE__ */ new WeakMap();
|
|
20
|
+
const readonlySetViews = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
const readonlyCollectionViewToRaw = /* @__PURE__ */ new WeakMap();
|
|
22
|
+
const normalizedProxyCarriers = /* @__PURE__ */ new WeakMap();
|
|
23
|
+
/**
|
|
24
|
+
* The realm's built-in prototype objects, rejected deliberately rather than by
|
|
25
|
+
* accident. Several of them look exactly like plain data to the rest of this
|
|
26
|
+
* module: `Array.prototype` is array-exotic (so `Array.isArray` short-circuits
|
|
27
|
+
* the prototype check below), while `Error.prototype`, `Date.prototype`,
|
|
28
|
+
* `Promise.prototype`, `String.prototype`, ... all inherit straight from
|
|
29
|
+
* `Object.prototype` and expose only extensible, writable data properties, so
|
|
30
|
+
* neither `assertExtensible` nor `assertDataProperties` stops them. Adopting
|
|
31
|
+
* one as deep state would let the `set` trap `Reflect.set` onto the real,
|
|
32
|
+
* globally shared intrinsic. Some of them (`Object.prototype.__proto__`,
|
|
33
|
+
* `Map.prototype.size`, ...) happen to be caught today by the accessor check,
|
|
34
|
+
* but that protection is incidental and asymmetric; this set makes it uniform.
|
|
35
|
+
*
|
|
36
|
+
* Built from `globalThis` so an engine missing a builtin simply contributes
|
|
37
|
+
* nothing. Only this realm's intrinsics are listed: an object from another
|
|
38
|
+
* realm already fails the `Object.prototype` identity check below (the one
|
|
39
|
+
* exception being a cross-realm array, which stays accepted as it is today).
|
|
40
|
+
*/
|
|
41
|
+
const INTRINSIC_PROTOTYPES = (() => {
|
|
42
|
+
const prototypes = /* @__PURE__ */ new Set();
|
|
43
|
+
for (const name of [
|
|
44
|
+
"Object",
|
|
45
|
+
"Array",
|
|
46
|
+
"Function",
|
|
47
|
+
"Boolean",
|
|
48
|
+
"Number",
|
|
49
|
+
"String",
|
|
50
|
+
"Symbol",
|
|
51
|
+
"BigInt",
|
|
52
|
+
"Date",
|
|
53
|
+
"RegExp",
|
|
54
|
+
"Error",
|
|
55
|
+
"AggregateError",
|
|
56
|
+
"EvalError",
|
|
57
|
+
"RangeError",
|
|
58
|
+
"ReferenceError",
|
|
59
|
+
"SyntaxError",
|
|
60
|
+
"TypeError",
|
|
61
|
+
"URIError",
|
|
62
|
+
"Map",
|
|
63
|
+
"Set",
|
|
64
|
+
"WeakMap",
|
|
65
|
+
"WeakSet",
|
|
66
|
+
"WeakRef",
|
|
67
|
+
"FinalizationRegistry",
|
|
68
|
+
"Promise",
|
|
69
|
+
"ArrayBuffer",
|
|
70
|
+
"SharedArrayBuffer",
|
|
71
|
+
"DataView"
|
|
72
|
+
]) {
|
|
73
|
+
const intrinsic = globalThis[name];
|
|
74
|
+
if (typeof intrinsic !== "function") continue;
|
|
75
|
+
const prototype = intrinsic.prototype;
|
|
76
|
+
if (typeof prototype === "object" && prototype !== null || typeof prototype === "function") prototypes.add(prototype);
|
|
77
|
+
}
|
|
78
|
+
return prototypes;
|
|
79
|
+
})();
|
|
80
|
+
const isIntrinsicPrototype = (value) => isObjectLike(value) && INTRINSIC_PROTOTYPES.has(value);
|
|
81
|
+
function isPlainObjectOrArray(value) {
|
|
82
|
+
if (typeof value !== "object" || value === null || isSignal(value)) return false;
|
|
83
|
+
if (INTRINSIC_PROTOTYPES.has(value)) return false;
|
|
84
|
+
if (Array.isArray(value)) return true;
|
|
85
|
+
const prototype = Object.getPrototypeOf(value);
|
|
86
|
+
return prototype === Object.prototype || prototype === null;
|
|
87
|
+
}
|
|
88
|
+
function isObjectLike(value) {
|
|
89
|
+
return typeof value === "object" && value !== null || typeof value === "function";
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Resolves a value that may be one of our proxies or one of our readonly
|
|
93
|
+
* collection views back to the raw object it wraps. Returns `undefined` when
|
|
94
|
+
* `value` is not object-like or is not a value this module has wrapped, so
|
|
95
|
+
* callers can fall back to using it as-is.
|
|
96
|
+
*/
|
|
97
|
+
function toRaw(value) {
|
|
98
|
+
if (!isObjectLike(value)) return void 0;
|
|
99
|
+
const directRaw = proxyToRaw.get(value);
|
|
100
|
+
if (directRaw !== void 0) return directRaw;
|
|
101
|
+
return readonlyCollectionViewToRaw.get(value);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Shared scaffolding for `readonlyMapView`/`readonlySetView`: cache lookup,
|
|
105
|
+
* the "reject this mutation" factory, defining the descriptors, the
|
|
106
|
+
* feature-detected extras loop, and the dual-WeakMap registration. Only the
|
|
107
|
+
* per-type descriptor bodies (which truly differ between Map and Set) and
|
|
108
|
+
* the extras list are supplied by the caller.
|
|
109
|
+
*/
|
|
110
|
+
function createReadonlyCollectionView(raw, cache, prototype, kind, buildCore, buildExtras) {
|
|
111
|
+
const cached = cache.get(raw);
|
|
112
|
+
if (cached !== void 0) return cached;
|
|
113
|
+
const view = Object.create(prototype);
|
|
114
|
+
const rejectMutation = (operation) => () => {
|
|
115
|
+
throw new TypeError(`deepSignal() ${kind}#${operation}() is not allowed through .value; replace the ${kind} immutably`);
|
|
116
|
+
};
|
|
117
|
+
Object.defineProperties(view, buildCore(raw, view, rejectMutation));
|
|
118
|
+
for (const [name, impl] of buildExtras(raw, rejectMutation)) Object.defineProperty(view, name, {
|
|
119
|
+
enumerable: false,
|
|
120
|
+
configurable: false,
|
|
121
|
+
value: impl
|
|
122
|
+
});
|
|
123
|
+
cache.set(raw, view);
|
|
124
|
+
readonlyCollectionViewToRaw.set(view, raw);
|
|
125
|
+
return view;
|
|
126
|
+
}
|
|
127
|
+
function readonlyMapView(raw) {
|
|
128
|
+
return createReadonlyCollectionView(raw, readonlyMapViews, Map.prototype, "Map", (target, view, rejectMutation) => ({
|
|
129
|
+
size: {
|
|
130
|
+
enumerable: false,
|
|
131
|
+
configurable: false,
|
|
132
|
+
get: () => target.size
|
|
133
|
+
},
|
|
134
|
+
get: {
|
|
135
|
+
enumerable: false,
|
|
136
|
+
configurable: false,
|
|
137
|
+
value: (key) => target.get(key)
|
|
138
|
+
},
|
|
139
|
+
has: {
|
|
140
|
+
enumerable: false,
|
|
141
|
+
configurable: false,
|
|
142
|
+
value: (key) => target.has(key)
|
|
143
|
+
},
|
|
144
|
+
entries: {
|
|
145
|
+
enumerable: false,
|
|
146
|
+
configurable: false,
|
|
147
|
+
value: () => Map.prototype.entries.call(target)
|
|
148
|
+
},
|
|
149
|
+
keys: {
|
|
150
|
+
enumerable: false,
|
|
151
|
+
configurable: false,
|
|
152
|
+
value: () => Map.prototype.keys.call(target)
|
|
153
|
+
},
|
|
154
|
+
values: {
|
|
155
|
+
enumerable: false,
|
|
156
|
+
configurable: false,
|
|
157
|
+
value: () => Map.prototype.values.call(target)
|
|
158
|
+
},
|
|
159
|
+
forEach: {
|
|
160
|
+
enumerable: false,
|
|
161
|
+
configurable: false,
|
|
162
|
+
value: (callback, thisArg) => {
|
|
163
|
+
Map.prototype.forEach.call(target, (value, key) => callback.call(thisArg, value, key, view));
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
[Symbol.iterator]: {
|
|
167
|
+
enumerable: false,
|
|
168
|
+
configurable: false,
|
|
169
|
+
value: () => Map.prototype.entries.call(target)
|
|
170
|
+
},
|
|
171
|
+
set: {
|
|
172
|
+
enumerable: false,
|
|
173
|
+
configurable: false,
|
|
174
|
+
value: rejectMutation("set")
|
|
175
|
+
},
|
|
176
|
+
delete: {
|
|
177
|
+
enumerable: false,
|
|
178
|
+
configurable: false,
|
|
179
|
+
value: rejectMutation("delete")
|
|
180
|
+
},
|
|
181
|
+
clear: {
|
|
182
|
+
enumerable: false,
|
|
183
|
+
configurable: false,
|
|
184
|
+
value: rejectMutation("clear")
|
|
185
|
+
}
|
|
186
|
+
}), (_target, rejectMutation) => {
|
|
187
|
+
const extras = [];
|
|
188
|
+
for (const operation of [
|
|
189
|
+
"getOrInsert",
|
|
190
|
+
"getOrInsertComputed",
|
|
191
|
+
"emplace"
|
|
192
|
+
]) if (typeof Map.prototype[operation] === "function") extras.push([operation, rejectMutation(operation)]);
|
|
193
|
+
return extras;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function readonlySetView(raw) {
|
|
197
|
+
return createReadonlyCollectionView(raw, readonlySetViews, Set.prototype, "Set", (target, view, rejectMutation) => ({
|
|
198
|
+
size: {
|
|
199
|
+
enumerable: false,
|
|
200
|
+
configurable: false,
|
|
201
|
+
get: () => target.size
|
|
202
|
+
},
|
|
203
|
+
has: {
|
|
204
|
+
enumerable: false,
|
|
205
|
+
configurable: false,
|
|
206
|
+
value: (value) => target.has(value)
|
|
207
|
+
},
|
|
208
|
+
entries: {
|
|
209
|
+
enumerable: false,
|
|
210
|
+
configurable: false,
|
|
211
|
+
value: () => Set.prototype.entries.call(target)
|
|
212
|
+
},
|
|
213
|
+
keys: {
|
|
214
|
+
enumerable: false,
|
|
215
|
+
configurable: false,
|
|
216
|
+
value: () => Set.prototype.keys.call(target)
|
|
217
|
+
},
|
|
218
|
+
values: {
|
|
219
|
+
enumerable: false,
|
|
220
|
+
configurable: false,
|
|
221
|
+
value: () => Set.prototype.values.call(target)
|
|
222
|
+
},
|
|
223
|
+
forEach: {
|
|
224
|
+
enumerable: false,
|
|
225
|
+
configurable: false,
|
|
226
|
+
value: (callback, thisArg) => {
|
|
227
|
+
Set.prototype.forEach.call(target, (value) => callback.call(thisArg, value, value, view));
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
[Symbol.iterator]: {
|
|
231
|
+
enumerable: false,
|
|
232
|
+
configurable: false,
|
|
233
|
+
value: () => Set.prototype.values.call(target)
|
|
234
|
+
},
|
|
235
|
+
add: {
|
|
236
|
+
enumerable: false,
|
|
237
|
+
configurable: false,
|
|
238
|
+
value: rejectMutation("add")
|
|
239
|
+
},
|
|
240
|
+
delete: {
|
|
241
|
+
enumerable: false,
|
|
242
|
+
configurable: false,
|
|
243
|
+
value: rejectMutation("delete")
|
|
244
|
+
},
|
|
245
|
+
clear: {
|
|
246
|
+
enumerable: false,
|
|
247
|
+
configurable: false,
|
|
248
|
+
value: rejectMutation("clear")
|
|
249
|
+
}
|
|
250
|
+
}), (target) => {
|
|
251
|
+
const extras = [];
|
|
252
|
+
const forwardSetOperation = (operation) => (other) => {
|
|
253
|
+
const method = Set.prototype[operation];
|
|
254
|
+
if (typeof method !== "function") throw new TypeError(`Set#${operation}() is unavailable in this JavaScript engine`);
|
|
255
|
+
const collection = isObjectLike(other) ? readonlyCollectionViewToRaw.get(other) ?? other : other;
|
|
256
|
+
return Reflect.apply(method, target, [collection]);
|
|
257
|
+
};
|
|
258
|
+
for (const operation of [
|
|
259
|
+
"union",
|
|
260
|
+
"intersection",
|
|
261
|
+
"difference",
|
|
262
|
+
"symmetricDifference",
|
|
263
|
+
"isSubsetOf",
|
|
264
|
+
"isSupersetOf",
|
|
265
|
+
"isDisjointFrom"
|
|
266
|
+
]) if (typeof Set.prototype[operation] === "function") extras.push([operation, forwardSetOperation(operation)]);
|
|
267
|
+
return extras;
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function assertRootValue(value) {
|
|
271
|
+
if (!isPlainObjectOrArray(value)) throw new TypeError("deepSignal() only accepts a plain object or array root");
|
|
272
|
+
}
|
|
273
|
+
function assertExtensible(value) {
|
|
274
|
+
if (!Object.isExtensible(value)) throw new TypeError("deepSignal() cannot proxy a non-extensible object or array");
|
|
275
|
+
}
|
|
276
|
+
function assertDataProperties(value) {
|
|
277
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
278
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
|
|
279
|
+
if (descriptor !== void 0 && !("value" in descriptor)) throw new TypeError("deepSignal() does not support accessor properties");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function assertDeepDataGraph(value) {
|
|
283
|
+
if (!isObjectLike(value)) return false;
|
|
284
|
+
const seen = /* @__PURE__ */ new WeakMap();
|
|
285
|
+
const pending = [{
|
|
286
|
+
value,
|
|
287
|
+
insideOpaque: false
|
|
288
|
+
}];
|
|
289
|
+
let needsNormalization = false;
|
|
290
|
+
while (pending.length > 0) {
|
|
291
|
+
const { value: current, insideOpaque } = pending.pop();
|
|
292
|
+
if (toRaw(current) !== void 0) {
|
|
293
|
+
if (insideOpaque) throw new TypeError(proxyToRaw.has(current) ? "deepSignal() cannot store a deep proxy inside an opaque value" : "deepSignal() cannot store a deep collection view inside an opaque value");
|
|
294
|
+
needsNormalization = true;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
const raw = current;
|
|
298
|
+
const visitedAs = seen.get(raw) ?? 0;
|
|
299
|
+
const visitFlag = insideOpaque ? 2 : 1;
|
|
300
|
+
if ((visitedAs & visitFlag) !== 0) continue;
|
|
301
|
+
seen.set(raw, visitedAs | visitFlag);
|
|
302
|
+
if (!insideOpaque && isIntrinsicPrototype(raw)) throw new TypeError("deepSignal() cannot store a built-in prototype object in deep state");
|
|
303
|
+
if (raw instanceof Map) {
|
|
304
|
+
for (const [key, entry] of raw) {
|
|
305
|
+
if (isObjectLike(key)) pending.push({
|
|
306
|
+
value: key,
|
|
307
|
+
insideOpaque: true
|
|
308
|
+
});
|
|
309
|
+
if (isObjectLike(entry)) pending.push({
|
|
310
|
+
value: entry,
|
|
311
|
+
insideOpaque: true
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (raw instanceof Set) {
|
|
317
|
+
for (const entry of raw) if (isObjectLike(entry)) pending.push({
|
|
318
|
+
value: entry,
|
|
319
|
+
insideOpaque: true
|
|
320
|
+
});
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (!isPlainObjectOrArray(raw)) {
|
|
324
|
+
for (const key of Reflect.ownKeys(raw)) {
|
|
325
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(raw, key);
|
|
326
|
+
if (descriptor !== void 0 && "value" in descriptor && isObjectLike(descriptor.value)) pending.push({
|
|
327
|
+
value: descriptor.value,
|
|
328
|
+
insideOpaque: true
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (!insideOpaque) {
|
|
334
|
+
assertExtensible(raw);
|
|
335
|
+
assertDataProperties(raw);
|
|
336
|
+
}
|
|
337
|
+
for (const key of Reflect.ownKeys(raw)) {
|
|
338
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(raw, key);
|
|
339
|
+
if (descriptor !== void 0 && "value" in descriptor) {
|
|
340
|
+
const child = descriptor.value;
|
|
341
|
+
if (isObjectLike(child)) pending.push({
|
|
342
|
+
value: child,
|
|
343
|
+
insideOpaque
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return needsNormalization;
|
|
349
|
+
}
|
|
350
|
+
function matchesNormalizedGraph(source, normalized) {
|
|
351
|
+
const sources = /* @__PURE__ */ new WeakMap();
|
|
352
|
+
const targets = /* @__PURE__ */ new WeakMap();
|
|
353
|
+
const pending = [[source, normalized]];
|
|
354
|
+
while (pending.length > 0) {
|
|
355
|
+
const [currentSource, currentTarget] = pending.pop();
|
|
356
|
+
if (typeof currentSource !== "object" || currentSource === null) {
|
|
357
|
+
if (!Object.is(currentSource, currentTarget)) return false;
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const wrapperRaw = toRaw(currentSource);
|
|
361
|
+
if (wrapperRaw !== void 0) {
|
|
362
|
+
if (wrapperRaw !== currentTarget) return false;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (!isPlainObjectOrArray(currentSource)) {
|
|
366
|
+
if (currentSource !== currentTarget) return false;
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (typeof currentTarget !== "object" || currentTarget === null || !isPlainObjectOrArray(currentTarget)) return false;
|
|
370
|
+
const knownTarget = sources.get(currentSource);
|
|
371
|
+
if (knownTarget !== void 0) {
|
|
372
|
+
if (knownTarget !== currentTarget) return false;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const knownSource = targets.get(currentTarget);
|
|
376
|
+
if (knownSource !== void 0 && knownSource !== currentSource) return false;
|
|
377
|
+
sources.set(currentSource, currentTarget);
|
|
378
|
+
targets.set(currentTarget, currentSource);
|
|
379
|
+
if (Array.isArray(currentSource) !== Array.isArray(currentTarget) || Object.getPrototypeOf(currentSource) !== Object.getPrototypeOf(currentTarget)) return false;
|
|
380
|
+
const sourceKeys = Reflect.ownKeys(currentSource);
|
|
381
|
+
const targetKeys = Reflect.ownKeys(currentTarget);
|
|
382
|
+
if (sourceKeys.length !== targetKeys.length) return false;
|
|
383
|
+
for (const [index, key] of sourceKeys.entries()) {
|
|
384
|
+
if (key !== targetKeys[index]) return false;
|
|
385
|
+
const sourceDescriptor = Reflect.getOwnPropertyDescriptor(currentSource, key);
|
|
386
|
+
const targetDescriptor = Reflect.getOwnPropertyDescriptor(currentTarget, key);
|
|
387
|
+
if (sourceDescriptor === void 0 || targetDescriptor === void 0 || !("value" in sourceDescriptor) || !("value" in targetDescriptor) || sourceDescriptor.configurable !== targetDescriptor.configurable || sourceDescriptor.enumerable !== targetDescriptor.enumerable || sourceDescriptor.writable !== targetDescriptor.writable) return false;
|
|
388
|
+
pending.push([sourceDescriptor.value, targetDescriptor.value]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* An array of `length` with no own index properties — holes, not `undefined`s.
|
|
395
|
+
* `Array.from({ length })` would materialize every index as an own property,
|
|
396
|
+
* so cloning a sparse carrier produced an array with strictly more own keys
|
|
397
|
+
* than its source. `matchesNormalizedGraph` compares own keys one-for-one, so
|
|
398
|
+
* that clone could never match its source again: the carrier cache missed on
|
|
399
|
+
* every re-assignment, breaking the documented guarantee that assigning the
|
|
400
|
+
* same carrier twice preserves identity and aliasing.
|
|
401
|
+
*/
|
|
402
|
+
function emptyArrayOfLength(length) {
|
|
403
|
+
const result = [];
|
|
404
|
+
result.length = length;
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
function cloneWithoutProxies(value) {
|
|
408
|
+
const wrapperRaw = toRaw(value);
|
|
409
|
+
if (wrapperRaw !== void 0) return wrapperRaw;
|
|
410
|
+
if (!isObjectLike(value)) return value;
|
|
411
|
+
const cachedRoot = normalizedProxyCarriers.get(value);
|
|
412
|
+
if (cachedRoot !== void 0 && matchesNormalizedGraph(value, cachedRoot)) return cachedRoot;
|
|
413
|
+
const clones = /* @__PURE__ */ new WeakMap();
|
|
414
|
+
const created = [];
|
|
415
|
+
const pending = [];
|
|
416
|
+
const resolve = (current) => {
|
|
417
|
+
const resolvedRaw = toRaw(current);
|
|
418
|
+
if (resolvedRaw !== void 0) return resolvedRaw;
|
|
419
|
+
if (!isPlainObjectOrArray(current)) return current;
|
|
420
|
+
const local = clones.get(current);
|
|
421
|
+
if (local !== void 0) return local;
|
|
422
|
+
const result = Array.isArray(current) ? emptyArrayOfLength(current.length) : Object.create(Object.getPrototypeOf(current));
|
|
423
|
+
clones.set(current, result);
|
|
424
|
+
created.push([current, result]);
|
|
425
|
+
pending.push(current);
|
|
426
|
+
return result;
|
|
427
|
+
};
|
|
428
|
+
const root = resolve(value);
|
|
429
|
+
while (pending.length > 0) {
|
|
430
|
+
const source = pending.pop();
|
|
431
|
+
const target = clones.get(source);
|
|
432
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
433
|
+
if (Array.isArray(source) && key === "length") continue;
|
|
434
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
|
|
435
|
+
if (descriptor === void 0) continue;
|
|
436
|
+
if (!("value" in descriptor)) throw new TypeError("deepSignal() does not support accessor properties");
|
|
437
|
+
descriptor.value = resolve(descriptor.value);
|
|
438
|
+
Reflect.defineProperty(target, key, descriptor);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
for (const [source, target] of created) normalizedProxyCarriers.set(source, target);
|
|
442
|
+
return root;
|
|
443
|
+
}
|
|
444
|
+
function prepareDeepValue(value) {
|
|
445
|
+
const wrapperRaw = toRaw(value);
|
|
446
|
+
if (wrapperRaw !== void 0) return wrapperRaw;
|
|
447
|
+
return assertDeepDataGraph(value) ? cloneWithoutProxies(value) : value;
|
|
448
|
+
}
|
|
449
|
+
function isArrayIndex(key) {
|
|
450
|
+
if (typeof key !== "string" || key === "") return false;
|
|
451
|
+
const index = Number(key);
|
|
452
|
+
return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key;
|
|
453
|
+
}
|
|
454
|
+
const unwrap = (value) => {
|
|
455
|
+
return prepareDeepValue(value);
|
|
456
|
+
};
|
|
457
|
+
const OBJECT_PROTOTYPE_KEYS = new Set(Reflect.ownKeys(Object.prototype));
|
|
458
|
+
const ARRAY_PROTOTYPE_KEYS = /* @__PURE__ */ new Set([...Reflect.ownKeys(Object.prototype), ...Reflect.ownKeys(Array.prototype)]);
|
|
459
|
+
const isInheritedPrototypeMember = (target, key) => {
|
|
460
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) return false;
|
|
461
|
+
const prototype = Object.getPrototypeOf(target);
|
|
462
|
+
if (prototype === Object.prototype) return OBJECT_PROTOTYPE_KEYS.has(key);
|
|
463
|
+
if (prototype === Array.prototype) return ARRAY_PROTOTYPE_KEYS.has(key);
|
|
464
|
+
return false;
|
|
465
|
+
};
|
|
466
|
+
const getVersion = (versions, key) => {
|
|
467
|
+
let version = versions.get(key);
|
|
468
|
+
if (version === void 0) {
|
|
469
|
+
version = registerSignal(new SignalImpl(0));
|
|
470
|
+
versions.set(key, version);
|
|
471
|
+
}
|
|
472
|
+
return version;
|
|
473
|
+
};
|
|
474
|
+
const track = (target, versions, indices, key) => {
|
|
475
|
+
if (getActiveSub() === void 0 && !hasActiveRenderCollector()) return;
|
|
476
|
+
if (isInheritedPrototypeMember(target, key)) return;
|
|
477
|
+
if (isArrayIndex(key)) indices.add(Number(key));
|
|
478
|
+
const version = getVersion(versions, key);
|
|
479
|
+
version.markWatched();
|
|
480
|
+
version.value;
|
|
481
|
+
};
|
|
482
|
+
const notify = (versions, key) => {
|
|
483
|
+
const version = versions.get(key);
|
|
484
|
+
if (version !== void 0) version.value += 1;
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Queues `key`'s version signals for removal once nothing depends on them.
|
|
488
|
+
* Only called for a key that has just been removed *and* notified, which is
|
|
489
|
+
* what makes the eventual removal safe: the notification forces every
|
|
490
|
+
* subscriber to re-run, so one that still cares re-reads the key (re-arming
|
|
491
|
+
* `markWatched`) and one that does not has already been unlinked.
|
|
492
|
+
*/
|
|
493
|
+
const markPrunable = (metadata, key) => {
|
|
494
|
+
if (!metadata.properties.has(key) && !metadata.existence.has(key)) return;
|
|
495
|
+
(metadata.prunable ??= /* @__PURE__ */ new Set()).add(key);
|
|
496
|
+
};
|
|
497
|
+
/**
|
|
498
|
+
* Drops the version signals of removed keys once they have no subscribers
|
|
499
|
+
* left, so a virtualized or repeatedly-spliced list stops accumulating one
|
|
500
|
+
* entry per key that ever existed.
|
|
501
|
+
*
|
|
502
|
+
* Runs only at batch depth 0, where the write that queued these keys has
|
|
503
|
+
* already flushed its effects; before that, `hasSubscribers()` would still
|
|
504
|
+
* report the pre-flush picture. A key that is still subscribed (typically a
|
|
505
|
+
* React store whose re-render has not committed yet) stays queued and is
|
|
506
|
+
* reconsidered on the next sweep rather than being dropped early.
|
|
507
|
+
*/
|
|
508
|
+
const sweepPrunedKeys = (metadata, target) => {
|
|
509
|
+
const prunable = metadata.prunable;
|
|
510
|
+
if (prunable === void 0 || prunable.size === 0) return;
|
|
511
|
+
if (getBatchDepth() !== 0) return;
|
|
512
|
+
for (const key of prunable) {
|
|
513
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
514
|
+
prunable.delete(key);
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
const property = metadata.properties.get(key);
|
|
518
|
+
const existence = metadata.existence.get(key);
|
|
519
|
+
if (property?.hasSubscribers() === true || existence?.hasSubscribers() === true) continue;
|
|
520
|
+
metadata.properties.delete(key);
|
|
521
|
+
metadata.existence.delete(key);
|
|
522
|
+
if (isArrayIndex(key)) {
|
|
523
|
+
const index = Number(key);
|
|
524
|
+
metadata.propertyIndices.delete(index);
|
|
525
|
+
metadata.existenceIndices.delete(index);
|
|
526
|
+
}
|
|
527
|
+
prunable.delete(key);
|
|
528
|
+
}
|
|
529
|
+
if (prunable.size === 0) metadata.prunable = void 0;
|
|
530
|
+
};
|
|
531
|
+
const trackIteration = (metadata) => {
|
|
532
|
+
if (getActiveSub() === void 0 && !hasActiveRenderCollector()) return;
|
|
533
|
+
metadata.iteration ??= signal$1(0);
|
|
534
|
+
metadata.iteration.value;
|
|
535
|
+
};
|
|
536
|
+
const notifyIteration = (metadata) => {
|
|
537
|
+
if (metadata.iteration !== void 0) metadata.iteration.value += 1;
|
|
538
|
+
};
|
|
539
|
+
const notifyTruncatedIndices = (metadata, currentLength, oldLength) => {
|
|
540
|
+
const truncatedCount = oldLength - currentLength;
|
|
541
|
+
const trackedCount = metadata.propertyIndices.size + metadata.existenceIndices.size;
|
|
542
|
+
if (trackedCount === 0) return;
|
|
543
|
+
if (truncatedCount <= trackedCount) {
|
|
544
|
+
for (let index = currentLength; index < oldLength; index++) {
|
|
545
|
+
const key = String(index);
|
|
546
|
+
notify(metadata.properties, key);
|
|
547
|
+
notify(metadata.existence, key);
|
|
548
|
+
markPrunable(metadata, key);
|
|
549
|
+
}
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
for (const index of metadata.propertyIndices) if (index >= currentLength && index < oldLength) {
|
|
553
|
+
const key = String(index);
|
|
554
|
+
notify(metadata.properties, key);
|
|
555
|
+
markPrunable(metadata, key);
|
|
556
|
+
}
|
|
557
|
+
for (const index of metadata.existenceIndices) if (index >= currentLength && index < oldLength) {
|
|
558
|
+
const key = String(index);
|
|
559
|
+
notify(metadata.existence, key);
|
|
560
|
+
markPrunable(metadata, key);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* Whether `wrap()` would hand back something other than `value` itself — a
|
|
565
|
+
* deep proxy for a plain object/array, or a readonly view for a Map/Set. The
|
|
566
|
+
* `get` trap uses this to reject a non-configurable, non-writable property
|
|
567
|
+
* whose value cannot be substituted, before `wrap()` performs the swap.
|
|
568
|
+
*/
|
|
569
|
+
const isWrappedByValue = (value) => isPlainObjectOrArray(value) || value instanceof Map || value instanceof Set;
|
|
570
|
+
const wrap = (value) => {
|
|
571
|
+
const rawValue = toRaw(value) ?? value;
|
|
572
|
+
if (rawValue instanceof Map) return readonlyMapView(rawValue);
|
|
573
|
+
if (rawValue instanceof Set) return readonlySetView(rawValue);
|
|
574
|
+
if (!isPlainObjectOrArray(rawValue)) return rawValue;
|
|
575
|
+
assertExtensible(rawValue);
|
|
576
|
+
const cached = rawToMetadata.get(rawValue);
|
|
577
|
+
if (cached !== void 0) return cached.proxy;
|
|
578
|
+
assertDataProperties(rawValue);
|
|
579
|
+
const metadata = {
|
|
580
|
+
properties: /* @__PURE__ */ new Map(),
|
|
581
|
+
existence: /* @__PURE__ */ new Map(),
|
|
582
|
+
propertyIndices: /* @__PURE__ */ new Set(),
|
|
583
|
+
existenceIndices: /* @__PURE__ */ new Set(),
|
|
584
|
+
arrayMethods: /* @__PURE__ */ new Map(),
|
|
585
|
+
proxy: void 0
|
|
586
|
+
};
|
|
587
|
+
const proxy = new Proxy(rawValue, {
|
|
588
|
+
get(target, key, receiver) {
|
|
589
|
+
if (key === SIGNAL_BRAND && !Object.prototype.hasOwnProperty.call(target, key)) return;
|
|
590
|
+
track(target, metadata.properties, metadata.propertyIndices, key);
|
|
591
|
+
const result = Reflect.get(target, key, receiver);
|
|
592
|
+
if (Array.isArray(target) && ARRAY_MUTATORS.has(key) && typeof result === "function") {
|
|
593
|
+
const cachedMethod = metadata.arrayMethods.get(key);
|
|
594
|
+
if (cachedMethod?.method === result) return cachedMethod.wrapper;
|
|
595
|
+
const method = result;
|
|
596
|
+
const wrapper = function(...args) {
|
|
597
|
+
try {
|
|
598
|
+
return batch(() => Reflect.apply(method, this, args));
|
|
599
|
+
} finally {
|
|
600
|
+
sweepPrunedKeys(metadata, target);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
metadata.arrayMethods.set(key, {
|
|
604
|
+
method,
|
|
605
|
+
wrapper
|
|
606
|
+
});
|
|
607
|
+
return wrapper;
|
|
608
|
+
}
|
|
609
|
+
if (isWrappedByValue(result)) {
|
|
610
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
611
|
+
if (descriptor !== void 0 && "value" in descriptor && descriptor.configurable === false && descriptor.writable === false) throw new TypeError("deepSignal() cannot wrap a non-configurable, non-writable object property");
|
|
612
|
+
}
|
|
613
|
+
return wrap(result);
|
|
614
|
+
},
|
|
615
|
+
set(target, key, nextValue) {
|
|
616
|
+
if (key === "__proto__") throw new TypeError("deepSignal() does not support prototype mutation");
|
|
617
|
+
if (key === SIGNAL_BRAND) throw new TypeError("deepSignal() does not support branding state as a signal");
|
|
618
|
+
const oldValue = Reflect.get(target, key, target);
|
|
619
|
+
const existed = Reflect.has(target, key);
|
|
620
|
+
const owned = Object.prototype.hasOwnProperty.call(target, key);
|
|
621
|
+
const oldLength = Array.isArray(target) ? target.length : void 0;
|
|
622
|
+
const rawNextValue = unwrap(nextValue);
|
|
623
|
+
if (!Reflect.set(target, key, rawNextValue, target)) return false;
|
|
624
|
+
const currentValue = Reflect.get(target, key, target);
|
|
625
|
+
const existsNow = Reflect.has(target, key);
|
|
626
|
+
const ownedNow = Object.prototype.hasOwnProperty.call(target, key);
|
|
627
|
+
batch(() => {
|
|
628
|
+
if (!Object.is(oldValue, currentValue) || owned !== ownedNow) notify(metadata.properties, key);
|
|
629
|
+
if (existed !== existsNow) notify(metadata.existence, key);
|
|
630
|
+
if (owned !== ownedNow) notifyIteration(metadata);
|
|
631
|
+
if (Array.isArray(target) && oldLength !== void 0) {
|
|
632
|
+
const currentLength = target.length;
|
|
633
|
+
if (key !== "length" && oldLength !== currentLength) notify(metadata.properties, "length");
|
|
634
|
+
if (key === "length" && currentLength < oldLength) {
|
|
635
|
+
notifyTruncatedIndices(metadata, currentLength, oldLength);
|
|
636
|
+
notifyIteration(metadata);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
sweepPrunedKeys(metadata, target);
|
|
641
|
+
return true;
|
|
642
|
+
},
|
|
643
|
+
deleteProperty(target, key) {
|
|
644
|
+
const existed = Reflect.has(target, key);
|
|
645
|
+
const owned = Object.prototype.hasOwnProperty.call(target, key);
|
|
646
|
+
const succeeded = Reflect.deleteProperty(target, key);
|
|
647
|
+
if (!succeeded || !owned) return succeeded;
|
|
648
|
+
batch(() => {
|
|
649
|
+
notify(metadata.properties, key);
|
|
650
|
+
if (existed !== Reflect.has(target, key)) notify(metadata.existence, key);
|
|
651
|
+
notifyIteration(metadata);
|
|
652
|
+
markPrunable(metadata, key);
|
|
653
|
+
});
|
|
654
|
+
sweepPrunedKeys(metadata, target);
|
|
655
|
+
return true;
|
|
656
|
+
},
|
|
657
|
+
getOwnPropertyDescriptor(target, key) {
|
|
658
|
+
track(target, metadata.properties, metadata.propertyIndices, key);
|
|
659
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
|
|
660
|
+
if (descriptor === void 0 || !("value" in descriptor)) return descriptor;
|
|
661
|
+
if (descriptor.configurable === false && descriptor.writable === false) return descriptor;
|
|
662
|
+
descriptor.value = wrap(descriptor.value);
|
|
663
|
+
return descriptor;
|
|
664
|
+
},
|
|
665
|
+
has(target, key) {
|
|
666
|
+
track(target, metadata.existence, metadata.existenceIndices, key);
|
|
667
|
+
return Reflect.has(target, key);
|
|
668
|
+
},
|
|
669
|
+
ownKeys(target) {
|
|
670
|
+
trackIteration(metadata);
|
|
671
|
+
return Reflect.ownKeys(target);
|
|
672
|
+
},
|
|
673
|
+
defineProperty() {
|
|
674
|
+
throw new TypeError("deepSignal() does not support property descriptors");
|
|
675
|
+
},
|
|
676
|
+
preventExtensions() {
|
|
677
|
+
throw new TypeError("deepSignal() state must remain extensible");
|
|
678
|
+
},
|
|
679
|
+
setPrototypeOf() {
|
|
680
|
+
throw new TypeError("deepSignal() does not support prototype mutation");
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
metadata.proxy = proxy;
|
|
684
|
+
rawToMetadata.set(rawValue, metadata);
|
|
685
|
+
proxyToRaw.set(proxy, rawValue);
|
|
686
|
+
return proxy;
|
|
687
|
+
};
|
|
688
|
+
var DeepSignalImpl = class {
|
|
689
|
+
#source;
|
|
690
|
+
constructor(initialValue) {
|
|
691
|
+
this.#source = new SignalImpl(initialValue);
|
|
692
|
+
}
|
|
693
|
+
get value() {
|
|
694
|
+
return wrap(this.#source.value);
|
|
695
|
+
}
|
|
696
|
+
set value(nextValue) {
|
|
697
|
+
const rawValue = unwrap(nextValue);
|
|
698
|
+
assertRootValue(rawValue);
|
|
699
|
+
this.#source.value = rawValue;
|
|
700
|
+
}
|
|
701
|
+
peek() {
|
|
702
|
+
return this.#source.peek();
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
/**
|
|
706
|
+
* Creates a signal that lazily tracks nested plain-object and array properties.
|
|
707
|
+
* Mutations must go through `.value`; changes made through the original raw
|
|
708
|
+
* object are intentionally not observable.
|
|
709
|
+
*/
|
|
710
|
+
function deepSignal(initialValue) {
|
|
711
|
+
const rawInitialValue = prepareDeepValue(initialValue);
|
|
712
|
+
assertRootValue(rawInitialValue);
|
|
713
|
+
wrap(rawInitialValue);
|
|
714
|
+
return registerSignal(new DeepSignalImpl(rawInitialValue));
|
|
715
|
+
}
|
|
716
|
+
//#endregion
|
|
717
|
+
//#region src/react/hooks.ts
|
|
718
|
+
const EMPTY_DEPENDENCIES = [];
|
|
719
|
+
function assertSignalSnapshot(value) {
|
|
720
|
+
if (typeof value === "object" && value !== null || typeof value === "function") throw new TypeError("useDeepSignalValue selector must return a primitive snapshot; objects, Proxies, and functions are not supported");
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Shared `useSyncExternalStore` subscription wiring for a signal-backed
|
|
724
|
+
* store. On `subscribe`, starts an `effect()` that reruns `onEvaluate` on
|
|
725
|
+
* every relevant signal write and notifies React exactly when it reports a
|
|
726
|
+
* change; returns that effect's own dispose as the unsubscribe. The very
|
|
727
|
+
* first synchronous run — made while `subscribe` itself is still
|
|
728
|
+
* establishing the effect — never notifies, matching how each caller here
|
|
729
|
+
* already behaved: `useSyncExternalStore` performs its own "did the
|
|
730
|
+
* snapshot change since render" check independently of anything a `notify`
|
|
731
|
+
* call does, so a store's own subscribe-time run does not need to report on
|
|
732
|
+
* that window too.
|
|
733
|
+
*
|
|
734
|
+
* `getSnapshot` is intentionally not this helper's concern — the caller
|
|
735
|
+
* wires its own, whether that means recomputing fresh each call
|
|
736
|
+
* (`useSignalValue`) or returning a cached result (the deep-selector store).
|
|
737
|
+
*
|
|
738
|
+
* An exception thrown by `onEvaluate` is swallowed here and treated as a
|
|
739
|
+
* change worth notifying about. This mirrors `useSignalValue`'s prior
|
|
740
|
+
* inline behavior: a computed that starts failing must still trigger a
|
|
741
|
+
* re-render so `getSnapshot`'s own unguarded read can rethrow into an Error
|
|
742
|
+
* Boundary, rather than leaving this background effect's throw to escape
|
|
743
|
+
* into whatever write triggered the re-run. Callers whose `onEvaluate`
|
|
744
|
+
* already captures errors into its own return value (the deep-selector
|
|
745
|
+
* store) never hit this path.
|
|
746
|
+
*/
|
|
747
|
+
function createSignalStore(onEvaluate) {
|
|
748
|
+
return (notify) => {
|
|
749
|
+
let isInitialRun = true;
|
|
750
|
+
return effect$1(() => {
|
|
751
|
+
let changed;
|
|
752
|
+
try {
|
|
753
|
+
changed = onEvaluate();
|
|
754
|
+
} catch {
|
|
755
|
+
changed = true;
|
|
756
|
+
}
|
|
757
|
+
if (isInitialRun) {
|
|
758
|
+
isInitialRun = false;
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (changed) notify();
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Holds a selector result outside the reactive graph. In particular, a
|
|
767
|
+
* selector error must not escape from the signal write that caused a reactive
|
|
768
|
+
* re-evaluation: React needs to observe it during its next render so an Error
|
|
769
|
+
* Boundary can handle it. Unlike `useSignalValue`, errors are modeled as an
|
|
770
|
+
* explicit `SelectorResult` union rather than swallowed and re-thrown from a
|
|
771
|
+
* fresh read: `getSnapshot` below must return the same cached result the
|
|
772
|
+
* subscribed effect last settled on, not re-run `selector` on every render.
|
|
773
|
+
*/
|
|
774
|
+
function createDeepSelectorStore(source, selector) {
|
|
775
|
+
const evaluate = () => {
|
|
776
|
+
try {
|
|
777
|
+
const value = untrackedRender(() => selector(source.value));
|
|
778
|
+
assertSignalSnapshot(value);
|
|
779
|
+
return {
|
|
780
|
+
kind: "value",
|
|
781
|
+
value
|
|
782
|
+
};
|
|
783
|
+
} catch (error) {
|
|
784
|
+
return {
|
|
785
|
+
kind: "error",
|
|
786
|
+
error
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
};
|
|
790
|
+
let result = evaluate();
|
|
791
|
+
const hasChanged = (next) => {
|
|
792
|
+
if (result.kind !== next.kind) return true;
|
|
793
|
+
return result.kind === "value" && next.kind === "value" ? !Object.is(result.value, next.value) : result.kind === "error" && next.kind === "error" ? !Object.is(result.error, next.error) : false;
|
|
794
|
+
};
|
|
795
|
+
return {
|
|
796
|
+
subscribe: createSignalStore(() => {
|
|
797
|
+
const next = evaluate();
|
|
798
|
+
if (!hasChanged(next)) return false;
|
|
799
|
+
result = next;
|
|
800
|
+
return true;
|
|
801
|
+
}),
|
|
802
|
+
getSnapshot() {
|
|
803
|
+
if (result.kind === "error") throw result.error;
|
|
804
|
+
return result.value;
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
/** Creates a signal whose identity is stable for the lifetime of this component. */
|
|
809
|
+
function useSignal(initialValue) {
|
|
810
|
+
const signalRef = useRef(void 0);
|
|
811
|
+
if (signalRef.current === void 0) signalRef.current = signal$1(initialValue);
|
|
812
|
+
return signalRef.current;
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Creates a deep signal whose identity is stable for the component lifetime.
|
|
816
|
+
* A factory is evaluated only while initializing a mounted component instance;
|
|
817
|
+
* it must remain pure because React Strict Mode may replay initial rendering.
|
|
818
|
+
*/
|
|
819
|
+
function useDeepSignal(initialValue) {
|
|
820
|
+
const signalRef = useRef(void 0);
|
|
821
|
+
if (signalRef.current === void 0) signalRef.current = deepSignal(typeof initialValue === "function" ? initialValue() : initialValue);
|
|
822
|
+
return signalRef.current;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Selects a property-level primitive snapshot from a deep signal.
|
|
826
|
+
*
|
|
827
|
+
* Every non-signal value captured by `selector` must be listed in
|
|
828
|
+
* `dependencies`. Object and proxy results are intentionally rejected because
|
|
829
|
+
* mutable snapshots cannot satisfy `useSyncExternalStore` identity semantics.
|
|
830
|
+
*
|
|
831
|
+
* `dependencies` must keep a fixed length across this component's lifetime,
|
|
832
|
+
* matching `useMemo`'s own rule (its length feeds a `useMemo` deps array
|
|
833
|
+
* below via `[source, ...dependencies]`). A length change is caught and
|
|
834
|
+
* thrown as a clear error rather than left to degrade into React's silent
|
|
835
|
+
* "changed size between renders" dev warning, mirroring `useComputed`'s
|
|
836
|
+
* dependency-mode-switch guard.
|
|
837
|
+
*/
|
|
838
|
+
function useDeepSignalValue(source, selector, dependencies) {
|
|
839
|
+
const initialDependencyLengthRef = useRef(void 0);
|
|
840
|
+
initialDependencyLengthRef.current ??= dependencies.length;
|
|
841
|
+
if (dependencies.length !== initialDependencyLengthRef.current) {
|
|
842
|
+
const error = /* @__PURE__ */ new Error(`useDeepSignalValue: the \`dependencies\` array length changed between renders (from ${initialDependencyLengthRef.current} to ${dependencies.length}) for this call site. Keep \`dependencies\` a fixed length across the component's lifetime, matching useMemo's rules.`);
|
|
843
|
+
error.name = "UseDeepSignalValueDependencyLengthChangeError";
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
const store = useMemo(() => createDeepSelectorStore(source, selector), [source, ...dependencies]);
|
|
847
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Creates a computed signal with a stable identity.
|
|
851
|
+
*
|
|
852
|
+
* When `dependencies` is omitted, the getter must only read signals. Its initial
|
|
853
|
+
* closure is retained for the component lifetime, so props, state, and other
|
|
854
|
+
* non-signal values must not be captured in that mode.
|
|
855
|
+
*
|
|
856
|
+
* When the getter captures props, state, or any other non-signal value, list all
|
|
857
|
+
* of those values in `dependencies`. React memoization then creates a separate
|
|
858
|
+
* computed when they change, rather than replacing the getter of an existing
|
|
859
|
+
* computed during render. An abandoned render therefore cannot change the
|
|
860
|
+
* closure used by the previously committed computed. Choose one mode for a
|
|
861
|
+
* component's lifetime.
|
|
862
|
+
*/
|
|
863
|
+
function useComputed(getValue, dependencies) {
|
|
864
|
+
const dependencyComputed = useMemo(() => dependencies === void 0 ? void 0 : computed$1(getValue), dependencies ?? EMPTY_DEPENDENCIES);
|
|
865
|
+
const initialModeRef = useRef(void 0);
|
|
866
|
+
const signalOnlyComputedRef = useRef(void 0);
|
|
867
|
+
const mode = dependencies === void 0 ? "without a dependency array" : "with a dependency array";
|
|
868
|
+
initialModeRef.current ??= mode;
|
|
869
|
+
if (initialModeRef.current !== mode) {
|
|
870
|
+
const error = /* @__PURE__ */ new Error(`useComputed: the dependency-array mode changed between renders (from ${initialModeRef.current} to ${mode}) for this call site. Keep passing deps consistently, matching useMemo's rules.`);
|
|
871
|
+
error.name = "UseComputedModeChangeError";
|
|
872
|
+
throw error;
|
|
873
|
+
}
|
|
874
|
+
if (dependencyComputed !== void 0) return dependencyComputed;
|
|
875
|
+
if (signalOnlyComputedRef.current === void 0) signalOnlyComputedRef.current = computed$1(getValue);
|
|
876
|
+
return signalOnlyComputedRef.current;
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Runs a reactive effect after this component has committed, disposing it when
|
|
880
|
+
* the component unmounts (and during React Strict Mode's development replay).
|
|
881
|
+
*
|
|
882
|
+
* When `dependencies` is omitted, the callback must only capture signals. Its
|
|
883
|
+
* initial closure is retained for the component lifetime, so unrelated React
|
|
884
|
+
* renders do not restart the effect.
|
|
885
|
+
*
|
|
886
|
+
* When the callback captures props, state, or any other non-signal value, list
|
|
887
|
+
* all of those values in `dependencies`. The effect is then reconnected after
|
|
888
|
+
* those dependencies change. Choose one mode for a component's lifetime.
|
|
889
|
+
*/
|
|
890
|
+
function useSignalEffect(callback, dependencies) {
|
|
891
|
+
useEffect(() => effect$1(callback), dependencies ?? EMPTY_DEPENDENCIES);
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Reads a signal and subscribes the component to subsequent changes.
|
|
895
|
+
*
|
|
896
|
+
* The immediate run made by the public `effect` API establishes dependency
|
|
897
|
+
* tracking only. It intentionally does not notify React: `useSyncExternalStore`
|
|
898
|
+
* owns the initial consistency check after subscribing. See `createSignalStore`
|
|
899
|
+
* for the shared subscribe/notify wiring, including why a read that throws
|
|
900
|
+
* (a computed whose cached error `.value` rethrows) is swallowed in the
|
|
901
|
+
* background effect and left for `getSnapshot` to rethrow instead.
|
|
902
|
+
*/
|
|
903
|
+
function useSignalValue(source) {
|
|
904
|
+
const subscribe = useMemo(() => createSignalStore(() => {
|
|
905
|
+
source.value;
|
|
906
|
+
return true;
|
|
907
|
+
}), [source]);
|
|
908
|
+
const getSnapshot = useCallback(() => untracked(() => source.value), [source]);
|
|
909
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
910
|
+
}
|
|
911
|
+
//#endregion
|
|
912
|
+
export { useSignalEffect as a, useSignal as i, useDeepSignal as n, useSignalValue as o, useDeepSignalValue as r, deepSignal as s, useComputed as t };
|
|
913
|
+
|
|
914
|
+
//# sourceMappingURL=hooks-BFIWh7AD.js.map
|