react-fine-grained-signals 0.1.1 → 0.2.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.
@@ -0,0 +1,1833 @@
1
+ import { a as untrackedRender, c as getSharedInteropContext, d as UNTRACKED_OWNER, f as executionContext, i as notifyListener, l as isInteropSpeculative, n as SIGNAL_BRAND, o as attachReadableInterop, r as hasActiveRenderCollector, s as getReadableInterop, t as coreRuntime, u as markInteropSpeculativeDeepRead } from "./core-runtime-2jSE9Tz5.js";
2
+ import { a as isSignal, c as untracked, n as computed, o as registerSignal, r as detachedEffect, s as signal, t as batch } from "./base-WD_74mfC.js";
3
+ import { Fragment, createElement, useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
4
+ //#region src/core/deep-signal-engine.ts
5
+ function isObjectLike(value) {
6
+ return typeof value === "object" && value !== null || typeof value === "function";
7
+ }
8
+ function assertExtensible(value) {
9
+ if (!Object.isExtensible(value)) throw new TypeError("deepSignal() cannot proxy a non-extensible object or array");
10
+ }
11
+ function assertDataProperties(value) {
12
+ for (const key of Reflect.ownKeys(value)) {
13
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
14
+ if (descriptor !== void 0 && !("value" in descriptor)) throw new TypeError("deepSignal() does not support accessor properties");
15
+ }
16
+ }
17
+ function emptyArrayOfLength(length) {
18
+ const result = [];
19
+ result.length = length;
20
+ return result;
21
+ }
22
+ function isArrayIndex(key) {
23
+ if (typeof key !== "string" || key === "") return false;
24
+ const index = Number(key);
25
+ return Number.isInteger(index) && index >= 0 && index < 4294967295 && String(index) === key;
26
+ }
27
+ const notify = (versions, key) => {
28
+ const version = versions.get(key);
29
+ if (version !== void 0) version.value += 1;
30
+ };
31
+ const markPrunable = (metadata, key) => {
32
+ if (!metadata.properties.has(key) && !metadata.existence.has(key)) return;
33
+ (metadata.prunable ??= /* @__PURE__ */ new Set()).add(key);
34
+ };
35
+ const notifyIteration = (metadata) => {
36
+ if (metadata.iteration !== void 0) metadata.iteration.value += 1;
37
+ };
38
+ function createDeepSignalFactory(adapter) {
39
+ const ARRAY_MUTATORS = /* @__PURE__ */ new Set([
40
+ "copyWithin",
41
+ "fill",
42
+ "pop",
43
+ "push",
44
+ "reverse",
45
+ "shift",
46
+ "sort",
47
+ "splice",
48
+ "unshift"
49
+ ]);
50
+ const ARRAY_SEARCHES = /* @__PURE__ */ new Set([
51
+ "includes",
52
+ "indexOf",
53
+ "lastIndexOf"
54
+ ]);
55
+ const proxyToRaw = /* @__PURE__ */ new WeakMap();
56
+ const rawToMetadata = /* @__PURE__ */ new WeakMap();
57
+ const readonlyMapViews = /* @__PURE__ */ new WeakMap();
58
+ const readonlySetViews = /* @__PURE__ */ new WeakMap();
59
+ const readonlyCollectionViewToRaw = /* @__PURE__ */ new WeakMap();
60
+ const normalizedProxyCarriers = /* @__PURE__ */ new WeakMap();
61
+ const isSpeculative = adapter.isSpeculative ?? isInteropSpeculative;
62
+ const markSpeculativeDeepRead = adapter.markSpeculativeDeepRead ?? markInteropSpeculativeDeepRead;
63
+ function shouldTrackDeepRead() {
64
+ if (isSpeculative()) {
65
+ markSpeculativeDeepRead();
66
+ return false;
67
+ }
68
+ const shared = getSharedInteropContext();
69
+ return adapter.hasActiveSubscriber() || hasActiveRenderCollector() || shared.graphCollector !== void 0 || shared.renderCollector !== void 0;
70
+ }
71
+ /**
72
+ * The realm's built-in prototype objects, rejected deliberately rather than by
73
+ * accident. Several of them look exactly like plain data to the rest of this
74
+ * module: `Array.prototype` is array-exotic (so `Array.isArray` short-circuits
75
+ * the prototype check below), while `Error.prototype`, `Date.prototype`,
76
+ * `Promise.prototype`, `String.prototype`, ... all inherit straight from
77
+ * `Object.prototype` and expose only extensible, writable data properties, so
78
+ * neither `assertExtensible` nor `assertDataProperties` stops them. Adopting
79
+ * one as deep state would let the `set` trap `Reflect.set` onto the real,
80
+ * globally shared intrinsic. Some of them (`Object.prototype.__proto__`,
81
+ * `Map.prototype.size`, ...) happen to be caught today by the accessor check,
82
+ * but that protection is incidental and asymmetric; this set makes it uniform.
83
+ *
84
+ * Built from `globalThis` so an engine missing a builtin simply contributes
85
+ * nothing. Only this realm's intrinsics are listed: an object from another
86
+ * realm already fails the `Object.prototype` identity check below (the one
87
+ * exception being a cross-realm array, which stays accepted as it is today).
88
+ */
89
+ const INTRINSIC_PROTOTYPES = (() => {
90
+ const prototypes = /* @__PURE__ */ new Set();
91
+ for (const name of [
92
+ "Object",
93
+ "Array",
94
+ "Function",
95
+ "Boolean",
96
+ "Number",
97
+ "String",
98
+ "Symbol",
99
+ "BigInt",
100
+ "Date",
101
+ "RegExp",
102
+ "Error",
103
+ "AggregateError",
104
+ "EvalError",
105
+ "RangeError",
106
+ "ReferenceError",
107
+ "SyntaxError",
108
+ "TypeError",
109
+ "URIError",
110
+ "Map",
111
+ "Set",
112
+ "WeakMap",
113
+ "WeakSet",
114
+ "WeakRef",
115
+ "FinalizationRegistry",
116
+ "Promise",
117
+ "ArrayBuffer",
118
+ "SharedArrayBuffer",
119
+ "DataView"
120
+ ]) {
121
+ const intrinsic = globalThis[name];
122
+ if (typeof intrinsic !== "function") continue;
123
+ const prototype = intrinsic.prototype;
124
+ if (typeof prototype === "object" && prototype !== null || typeof prototype === "function") prototypes.add(prototype);
125
+ }
126
+ return prototypes;
127
+ })();
128
+ const isIntrinsicPrototype = (value) => isObjectLike(value) && INTRINSIC_PROTOTYPES.has(value);
129
+ function isPlainObjectOrArray(value) {
130
+ if (typeof value !== "object" || value === null || adapter.isSignal(value)) return false;
131
+ if (INTRINSIC_PROTOTYPES.has(value)) return false;
132
+ if (Array.isArray(value)) return true;
133
+ const prototype = Object.getPrototypeOf(value);
134
+ return prototype === Object.prototype || prototype === null;
135
+ }
136
+ /**
137
+ * Resolves a value that may be one of our proxies or one of our readonly
138
+ * collection views back to the raw object it wraps. Returns `undefined` when
139
+ * `value` is not object-like or is not a value this module has wrapped, so
140
+ * callers can fall back to using it as-is.
141
+ */
142
+ function toRaw(value) {
143
+ if (!isObjectLike(value)) return void 0;
144
+ const directRaw = proxyToRaw.get(value);
145
+ if (directRaw !== void 0) return directRaw;
146
+ return readonlyCollectionViewToRaw.get(value);
147
+ }
148
+ /**
149
+ * Shared scaffolding for `readonlyMapView`/`readonlySetView`: cache lookup,
150
+ * the "reject this mutation" factory, defining the descriptors, the
151
+ * feature-detected extras loop, and the dual-WeakMap registration. Only the
152
+ * per-type descriptor bodies (which truly differ between Map and Set) and
153
+ * the extras list are supplied by the caller.
154
+ */
155
+ function createReadonlyCollectionView(raw, cache, prototype, kind, buildCore, buildExtras) {
156
+ const cached = cache.get(raw);
157
+ if (cached !== void 0) return cached;
158
+ const view = Object.create(prototype);
159
+ const rejectMutation = (operation) => () => {
160
+ throw new TypeError(`deepSignal() ${kind}#${operation}() is not allowed through .value; replace the ${kind} immutably`);
161
+ };
162
+ Object.defineProperties(view, buildCore(raw, view, rejectMutation));
163
+ for (const [name, impl] of buildExtras(raw, rejectMutation)) Object.defineProperty(view, name, {
164
+ enumerable: false,
165
+ configurable: false,
166
+ value: impl
167
+ });
168
+ cache.set(raw, view);
169
+ readonlyCollectionViewToRaw.set(view, raw);
170
+ return view;
171
+ }
172
+ function readonlyMapView(raw) {
173
+ return createReadonlyCollectionView(raw, readonlyMapViews, Map.prototype, "Map", (target, view, rejectMutation) => ({
174
+ size: {
175
+ enumerable: false,
176
+ configurable: false,
177
+ get: () => target.size
178
+ },
179
+ get: {
180
+ enumerable: false,
181
+ configurable: false,
182
+ value: (key) => target.get(key)
183
+ },
184
+ has: {
185
+ enumerable: false,
186
+ configurable: false,
187
+ value: (key) => target.has(key)
188
+ },
189
+ entries: {
190
+ enumerable: false,
191
+ configurable: false,
192
+ value: () => Map.prototype.entries.call(target)
193
+ },
194
+ keys: {
195
+ enumerable: false,
196
+ configurable: false,
197
+ value: () => Map.prototype.keys.call(target)
198
+ },
199
+ values: {
200
+ enumerable: false,
201
+ configurable: false,
202
+ value: () => Map.prototype.values.call(target)
203
+ },
204
+ forEach: {
205
+ enumerable: false,
206
+ configurable: false,
207
+ value: (callback, thisArg) => {
208
+ Map.prototype.forEach.call(target, (value, key) => callback.call(thisArg, value, key, view));
209
+ }
210
+ },
211
+ [Symbol.iterator]: {
212
+ enumerable: false,
213
+ configurable: false,
214
+ value: () => Map.prototype.entries.call(target)
215
+ },
216
+ set: {
217
+ enumerable: false,
218
+ configurable: false,
219
+ value: rejectMutation("set")
220
+ },
221
+ delete: {
222
+ enumerable: false,
223
+ configurable: false,
224
+ value: rejectMutation("delete")
225
+ },
226
+ clear: {
227
+ enumerable: false,
228
+ configurable: false,
229
+ value: rejectMutation("clear")
230
+ }
231
+ }), (_target, rejectMutation) => {
232
+ const extras = [];
233
+ for (const operation of [
234
+ "getOrInsert",
235
+ "getOrInsertComputed",
236
+ "emplace"
237
+ ]) if (typeof Map.prototype[operation] === "function") extras.push([operation, rejectMutation(operation)]);
238
+ return extras;
239
+ });
240
+ }
241
+ function readonlySetView(raw) {
242
+ return createReadonlyCollectionView(raw, readonlySetViews, Set.prototype, "Set", (target, view, rejectMutation) => ({
243
+ size: {
244
+ enumerable: false,
245
+ configurable: false,
246
+ get: () => target.size
247
+ },
248
+ has: {
249
+ enumerable: false,
250
+ configurable: false,
251
+ value: (value) => target.has(value)
252
+ },
253
+ entries: {
254
+ enumerable: false,
255
+ configurable: false,
256
+ value: () => Set.prototype.entries.call(target)
257
+ },
258
+ keys: {
259
+ enumerable: false,
260
+ configurable: false,
261
+ value: () => Set.prototype.keys.call(target)
262
+ },
263
+ values: {
264
+ enumerable: false,
265
+ configurable: false,
266
+ value: () => Set.prototype.values.call(target)
267
+ },
268
+ forEach: {
269
+ enumerable: false,
270
+ configurable: false,
271
+ value: (callback, thisArg) => {
272
+ Set.prototype.forEach.call(target, (value) => callback.call(thisArg, value, value, view));
273
+ }
274
+ },
275
+ [Symbol.iterator]: {
276
+ enumerable: false,
277
+ configurable: false,
278
+ value: () => Set.prototype.values.call(target)
279
+ },
280
+ add: {
281
+ enumerable: false,
282
+ configurable: false,
283
+ value: rejectMutation("add")
284
+ },
285
+ delete: {
286
+ enumerable: false,
287
+ configurable: false,
288
+ value: rejectMutation("delete")
289
+ },
290
+ clear: {
291
+ enumerable: false,
292
+ configurable: false,
293
+ value: rejectMutation("clear")
294
+ }
295
+ }), (target) => {
296
+ const extras = [];
297
+ const forwardSetOperation = (operation) => (other) => {
298
+ const method = Set.prototype[operation];
299
+ if (typeof method !== "function") throw new TypeError(`Set#${operation}() is unavailable in this JavaScript engine`);
300
+ const collection = isObjectLike(other) ? readonlyCollectionViewToRaw.get(other) ?? other : other;
301
+ return Reflect.apply(method, target, [collection]);
302
+ };
303
+ for (const operation of [
304
+ "union",
305
+ "intersection",
306
+ "difference",
307
+ "symmetricDifference",
308
+ "isSubsetOf",
309
+ "isSupersetOf",
310
+ "isDisjointFrom"
311
+ ]) if (typeof Set.prototype[operation] === "function") extras.push([operation, forwardSetOperation(operation)]);
312
+ return extras;
313
+ });
314
+ }
315
+ function assertRootValue(value) {
316
+ if (!isPlainObjectOrArray(value)) throw new TypeError("deepSignal() only accepts a plain object or array root");
317
+ }
318
+ function assertDeepDataGraph(value) {
319
+ if (!isObjectLike(value)) return false;
320
+ const seen = /* @__PURE__ */ new WeakMap();
321
+ const pending = [{
322
+ value,
323
+ insideOpaque: false
324
+ }];
325
+ let needsNormalization = false;
326
+ while (pending.length > 0) {
327
+ const { value: current, insideOpaque } = pending.pop();
328
+ if (toRaw(current) !== void 0) {
329
+ 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");
330
+ needsNormalization = true;
331
+ continue;
332
+ }
333
+ const raw = current;
334
+ const visitedAs = seen.get(raw) ?? 0;
335
+ const visitFlag = insideOpaque ? 2 : 1;
336
+ if ((visitedAs & visitFlag) !== 0) continue;
337
+ seen.set(raw, visitedAs | visitFlag);
338
+ if (!insideOpaque && isIntrinsicPrototype(raw)) throw new TypeError("deepSignal() cannot store a built-in prototype object in deep state");
339
+ if (raw instanceof Map) {
340
+ for (const [key, entry] of raw) {
341
+ if (isObjectLike(key)) pending.push({
342
+ value: key,
343
+ insideOpaque: true
344
+ });
345
+ if (isObjectLike(entry)) pending.push({
346
+ value: entry,
347
+ insideOpaque: true
348
+ });
349
+ }
350
+ continue;
351
+ }
352
+ if (raw instanceof Set) {
353
+ for (const entry of raw) if (isObjectLike(entry)) pending.push({
354
+ value: entry,
355
+ insideOpaque: true
356
+ });
357
+ continue;
358
+ }
359
+ if (!isPlainObjectOrArray(raw)) {
360
+ for (const key of Reflect.ownKeys(raw)) {
361
+ const descriptor = Reflect.getOwnPropertyDescriptor(raw, key);
362
+ if (descriptor !== void 0 && "value" in descriptor && isObjectLike(descriptor.value)) pending.push({
363
+ value: descriptor.value,
364
+ insideOpaque: true
365
+ });
366
+ }
367
+ continue;
368
+ }
369
+ if (!insideOpaque) {
370
+ assertExtensible(raw);
371
+ assertDataProperties(raw);
372
+ }
373
+ for (const key of Reflect.ownKeys(raw)) {
374
+ const descriptor = Reflect.getOwnPropertyDescriptor(raw, key);
375
+ if (descriptor !== void 0 && "value" in descriptor) {
376
+ const child = descriptor.value;
377
+ if (isObjectLike(child)) pending.push({
378
+ value: child,
379
+ insideOpaque
380
+ });
381
+ }
382
+ }
383
+ }
384
+ return needsNormalization;
385
+ }
386
+ function matchesNormalizedGraph(source, normalized) {
387
+ const sources = /* @__PURE__ */ new WeakMap();
388
+ const targets = /* @__PURE__ */ new WeakMap();
389
+ const pending = [[source, normalized]];
390
+ while (pending.length > 0) {
391
+ const [currentSource, currentTarget] = pending.pop();
392
+ if (typeof currentSource !== "object" || currentSource === null) {
393
+ if (!Object.is(currentSource, currentTarget)) return false;
394
+ continue;
395
+ }
396
+ const wrapperRaw = toRaw(currentSource);
397
+ if (wrapperRaw !== void 0) {
398
+ if (wrapperRaw !== currentTarget) return false;
399
+ continue;
400
+ }
401
+ if (!isPlainObjectOrArray(currentSource)) {
402
+ if (currentSource !== currentTarget) return false;
403
+ continue;
404
+ }
405
+ if (typeof currentTarget !== "object" || currentTarget === null || !isPlainObjectOrArray(currentTarget)) return false;
406
+ const knownTarget = sources.get(currentSource);
407
+ if (knownTarget !== void 0) {
408
+ if (knownTarget !== currentTarget) return false;
409
+ continue;
410
+ }
411
+ const knownSource = targets.get(currentTarget);
412
+ if (knownSource !== void 0 && knownSource !== currentSource) return false;
413
+ sources.set(currentSource, currentTarget);
414
+ targets.set(currentTarget, currentSource);
415
+ if (Array.isArray(currentSource) !== Array.isArray(currentTarget) || Object.getPrototypeOf(currentSource) !== Object.getPrototypeOf(currentTarget)) return false;
416
+ const sourceKeys = Reflect.ownKeys(currentSource);
417
+ const targetKeys = Reflect.ownKeys(currentTarget);
418
+ if (sourceKeys.length !== targetKeys.length) return false;
419
+ for (const [index, key] of sourceKeys.entries()) {
420
+ if (key !== targetKeys[index]) return false;
421
+ const sourceDescriptor = Reflect.getOwnPropertyDescriptor(currentSource, key);
422
+ const targetDescriptor = Reflect.getOwnPropertyDescriptor(currentTarget, key);
423
+ 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;
424
+ pending.push([sourceDescriptor.value, targetDescriptor.value]);
425
+ }
426
+ }
427
+ return true;
428
+ }
429
+ /**
430
+ * An array of `length` with no own index properties — holes, not `undefined`s.
431
+ * `Array.from({ length })` would materialize every index as an own property,
432
+ * so cloning a sparse carrier produced an array with strictly more own keys
433
+ * than its source. `matchesNormalizedGraph` compares own keys one-for-one, so
434
+ * that clone could never match its source again: the carrier cache missed on
435
+ * every re-assignment, breaking the documented guarantee that assigning the
436
+ * same carrier twice preserves identity and aliasing.
437
+ */
438
+ function cloneWithoutProxies(value) {
439
+ const wrapperRaw = toRaw(value);
440
+ if (wrapperRaw !== void 0) return wrapperRaw;
441
+ if (!isObjectLike(value)) return value;
442
+ const cachedRoot = normalizedProxyCarriers.get(value);
443
+ if (cachedRoot !== void 0 && matchesNormalizedGraph(value, cachedRoot)) return cachedRoot;
444
+ const clones = /* @__PURE__ */ new WeakMap();
445
+ const created = [];
446
+ const pending = [];
447
+ const resolve = (current) => {
448
+ const resolvedRaw = toRaw(current);
449
+ if (resolvedRaw !== void 0) return resolvedRaw;
450
+ if (!isPlainObjectOrArray(current)) return current;
451
+ const local = clones.get(current);
452
+ if (local !== void 0) return local;
453
+ const result = Array.isArray(current) ? emptyArrayOfLength(current.length) : Object.create(Object.getPrototypeOf(current));
454
+ clones.set(current, result);
455
+ created.push([current, result]);
456
+ pending.push(current);
457
+ return result;
458
+ };
459
+ const root = resolve(value);
460
+ while (pending.length > 0) {
461
+ const source = pending.pop();
462
+ const target = clones.get(source);
463
+ for (const key of Reflect.ownKeys(source)) {
464
+ if (Array.isArray(source) && key === "length") continue;
465
+ const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
466
+ if (descriptor === void 0) continue;
467
+ if (!("value" in descriptor)) throw new TypeError("deepSignal() does not support accessor properties");
468
+ descriptor.value = resolve(descriptor.value);
469
+ Reflect.defineProperty(target, key, descriptor);
470
+ }
471
+ }
472
+ for (const [source, target] of created) normalizedProxyCarriers.set(source, target);
473
+ return root;
474
+ }
475
+ function prepareDeepValue(value) {
476
+ const wrapperRaw = toRaw(value);
477
+ if (wrapperRaw !== void 0) return wrapperRaw;
478
+ return assertDeepDataGraph(value) ? cloneWithoutProxies(value) : value;
479
+ }
480
+ const unwrap = (value) => {
481
+ return prepareDeepValue(value);
482
+ };
483
+ const OBJECT_PROTOTYPE_KEYS = new Set(Reflect.ownKeys(Object.prototype));
484
+ const ARRAY_PROTOTYPE_KEYS = /* @__PURE__ */ new Set([...Reflect.ownKeys(Object.prototype), ...Reflect.ownKeys(Array.prototype)]);
485
+ const isInheritedPrototypeMember = (target, key) => {
486
+ if (Object.prototype.hasOwnProperty.call(target, key)) return false;
487
+ const prototype = Object.getPrototypeOf(target);
488
+ if (prototype === Object.prototype) return OBJECT_PROTOTYPE_KEYS.has(key);
489
+ if (prototype === Array.prototype) return ARRAY_PROTOTYPE_KEYS.has(key);
490
+ return false;
491
+ };
492
+ const getVersion = (versions, key) => {
493
+ let version = versions.get(key);
494
+ if (version === void 0) {
495
+ version = adapter.createVersionSignal(0);
496
+ versions.set(key, version);
497
+ }
498
+ return version;
499
+ };
500
+ const track = (metadata, target, versions, indices, key) => {
501
+ if (!shouldTrackDeepRead()) return;
502
+ if (isInheritedPrototypeMember(target, key)) {
503
+ if (!Array.isArray(target)) trackIterationOf(metadata);
504
+ return;
505
+ }
506
+ if (isArrayIndex(key)) indices.add(Number(key));
507
+ const version = getVersion(versions, key);
508
+ adapter.markWatched(version);
509
+ version.value;
510
+ };
511
+ /**
512
+ * Queues `key`'s version signals for removal once nothing depends on them.
513
+ * Only called for a key that has just been removed *and* notified, which is
514
+ * what makes the eventual removal safe: the notification forces every
515
+ * subscriber to re-run, so one that still cares re-reads the key (re-arming
516
+ * `markWatched`) and one that does not has already been unlinked.
517
+ */
518
+ /**
519
+ * Drops the version signals of removed keys once they have no subscribers
520
+ * left, so a virtualized or repeatedly-spliced list stops accumulating one
521
+ * entry per key that ever existed.
522
+ *
523
+ * Runs only at batch depth 0, where the write that queued these keys has
524
+ * already flushed its effects; before that, `hasSubscribers()` would still
525
+ * report the pre-flush picture. A key that is still subscribed (typically a
526
+ * React store whose re-render has not committed yet) stays queued and is
527
+ * reconsidered on the next sweep rather than being dropped early.
528
+ */
529
+ const sweepPrunedKeys = (metadata, target) => {
530
+ const prunable = metadata.prunable;
531
+ if (prunable === void 0 || prunable.size === 0) return;
532
+ if (adapter.getBatchDepth() !== 0) return;
533
+ for (const key of prunable) {
534
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
535
+ prunable.delete(key);
536
+ continue;
537
+ }
538
+ const property = metadata.properties.get(key);
539
+ const existence = metadata.existence.get(key);
540
+ if (property !== void 0 && adapter.hasSubscribers(property) || existence !== void 0 && adapter.hasSubscribers(existence)) continue;
541
+ metadata.properties.delete(key);
542
+ metadata.existence.delete(key);
543
+ if (isArrayIndex(key)) {
544
+ const index = Number(key);
545
+ metadata.propertyIndices.delete(index);
546
+ metadata.existenceIndices.delete(index);
547
+ }
548
+ prunable.delete(key);
549
+ }
550
+ if (prunable.size === 0) metadata.prunable = void 0;
551
+ };
552
+ const trackIterationOf = (metadata) => {
553
+ metadata.iteration ??= adapter.createVersionSignal(0);
554
+ metadata.iteration.value;
555
+ };
556
+ const trackIteration = (metadata) => {
557
+ if (!shouldTrackDeepRead()) return;
558
+ trackIterationOf(metadata);
559
+ };
560
+ const notifyTruncatedIndices = (metadata, currentLength, oldLength) => {
561
+ const truncatedCount = oldLength - currentLength;
562
+ const trackedCount = metadata.propertyIndices.size + metadata.existenceIndices.size;
563
+ if (trackedCount === 0) return;
564
+ if (truncatedCount <= trackedCount) {
565
+ for (let index = currentLength; index < oldLength; index++) {
566
+ const key = String(index);
567
+ notify(metadata.properties, key);
568
+ notify(metadata.existence, key);
569
+ markPrunable(metadata, key);
570
+ }
571
+ return;
572
+ }
573
+ for (const index of metadata.propertyIndices) if (index >= currentLength && index < oldLength) {
574
+ const key = String(index);
575
+ notify(metadata.properties, key);
576
+ markPrunable(metadata, key);
577
+ }
578
+ for (const index of metadata.existenceIndices) if (index >= currentLength && index < oldLength) {
579
+ const key = String(index);
580
+ notify(metadata.existence, key);
581
+ markPrunable(metadata, key);
582
+ }
583
+ };
584
+ /**
585
+ * Whether `wrap()` would hand back something other than `value` itself — a
586
+ * deep proxy for a plain object/array, or a readonly view for a Map/Set. The
587
+ * `get` trap uses this to reject a non-configurable, non-writable property
588
+ * whose value cannot be substituted, before `wrap()` performs the swap.
589
+ */
590
+ const isWrappedByValue = (value) => isPlainObjectOrArray(value) || value instanceof Map || value instanceof Set;
591
+ const wrap = (value) => {
592
+ const rawValue = toRaw(value) ?? value;
593
+ if (rawValue instanceof Map) return readonlyMapView(rawValue);
594
+ if (rawValue instanceof Set) return readonlySetView(rawValue);
595
+ if (!isPlainObjectOrArray(rawValue)) return rawValue;
596
+ assertExtensible(rawValue);
597
+ const cached = rawToMetadata.get(rawValue);
598
+ if (cached !== void 0) return cached.proxy;
599
+ assertDataProperties(rawValue);
600
+ const metadata = {
601
+ properties: /* @__PURE__ */ new Map(),
602
+ existence: /* @__PURE__ */ new Map(),
603
+ propertyIndices: /* @__PURE__ */ new Set(),
604
+ existenceIndices: /* @__PURE__ */ new Set(),
605
+ arrayMethods: /* @__PURE__ */ new Map(),
606
+ proxy: void 0
607
+ };
608
+ const proxy = new Proxy(rawValue, {
609
+ get(target, key, receiver) {
610
+ if (key === SIGNAL_BRAND && !Object.prototype.hasOwnProperty.call(target, key)) return;
611
+ track(metadata, target, metadata.properties, metadata.propertyIndices, key);
612
+ const result = Reflect.get(target, key, receiver);
613
+ if (Array.isArray(target) && ARRAY_MUTATORS.has(key) && typeof result === "function") {
614
+ const cachedMethod = metadata.arrayMethods.get(key);
615
+ if (cachedMethod?.method === result) return cachedMethod.wrapper;
616
+ const method = result;
617
+ const wrapper = function(...args) {
618
+ try {
619
+ return adapter.batch(() => adapter.untracked(() => Reflect.apply(method, this, args)));
620
+ } finally {
621
+ sweepPrunedKeys(metadata, target);
622
+ }
623
+ };
624
+ metadata.arrayMethods.set(key, {
625
+ method,
626
+ wrapper
627
+ });
628
+ return wrapper;
629
+ }
630
+ if (Array.isArray(target) && ARRAY_SEARCHES.has(key) && typeof result === "function") {
631
+ const cachedMethod = metadata.arrayMethods.get(key);
632
+ if (cachedMethod?.method === result) return cachedMethod.wrapper;
633
+ const method = result;
634
+ const wrapper = function(...args) {
635
+ const found = Reflect.apply(method, this, args);
636
+ if (found !== -1 && found !== false || !isObjectLike(args[0])) return found;
637
+ args[0] = toRaw(args[0]) ?? args[0];
638
+ return Reflect.apply(method, toRaw(this) ?? this, args);
639
+ };
640
+ metadata.arrayMethods.set(key, {
641
+ method,
642
+ wrapper
643
+ });
644
+ return wrapper;
645
+ }
646
+ if (isWrappedByValue(result)) {
647
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
648
+ 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");
649
+ }
650
+ return wrap(result);
651
+ },
652
+ set(target, key, nextValue, receiver) {
653
+ if (receiver !== metadata.proxy) return Reflect.set(target, key, nextValue, receiver);
654
+ if (key === "__proto__") throw new TypeError("deepSignal() does not support prototype mutation");
655
+ if (key === SIGNAL_BRAND) throw new TypeError("deepSignal() does not support branding state as a signal");
656
+ const oldValue = Reflect.get(target, key, target);
657
+ const existed = Reflect.has(target, key);
658
+ const owned = Object.prototype.hasOwnProperty.call(target, key);
659
+ const oldLength = Array.isArray(target) ? target.length : void 0;
660
+ const rawNextValue = unwrap(nextValue);
661
+ if (!Reflect.set(target, key, rawNextValue, target)) return false;
662
+ const currentValue = Reflect.get(target, key, target);
663
+ const existsNow = Reflect.has(target, key);
664
+ const ownedNow = Object.prototype.hasOwnProperty.call(target, key);
665
+ adapter.batch(() => {
666
+ if (!Object.is(oldValue, currentValue) || owned !== ownedNow) notify(metadata.properties, key);
667
+ if (existed !== existsNow || owned !== ownedNow) notify(metadata.existence, key);
668
+ if (owned !== ownedNow) notifyIteration(metadata);
669
+ if (Array.isArray(target) && oldLength !== void 0) {
670
+ const currentLength = target.length;
671
+ if (key !== "length" && oldLength !== currentLength) notify(metadata.properties, "length");
672
+ if (key === "length" && currentLength < oldLength) {
673
+ notifyTruncatedIndices(metadata, currentLength, oldLength);
674
+ notifyIteration(metadata);
675
+ }
676
+ }
677
+ });
678
+ sweepPrunedKeys(metadata, target);
679
+ return true;
680
+ },
681
+ deleteProperty(target, key) {
682
+ const owned = Object.prototype.hasOwnProperty.call(target, key);
683
+ const succeeded = Reflect.deleteProperty(target, key);
684
+ if (!succeeded || !owned) return succeeded;
685
+ adapter.batch(() => {
686
+ notify(metadata.properties, key);
687
+ notify(metadata.existence, key);
688
+ notifyIteration(metadata);
689
+ markPrunable(metadata, key);
690
+ });
691
+ sweepPrunedKeys(metadata, target);
692
+ return true;
693
+ },
694
+ getOwnPropertyDescriptor(target, key) {
695
+ track(metadata, target, metadata.existence, metadata.existenceIndices, key);
696
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
697
+ if (descriptor === void 0 || !("value" in descriptor)) return descriptor;
698
+ if (descriptor.configurable === false && descriptor.writable === false) return descriptor;
699
+ descriptor.value = wrap(descriptor.value);
700
+ return descriptor;
701
+ },
702
+ has(target, key) {
703
+ track(metadata, target, metadata.existence, metadata.existenceIndices, key);
704
+ return Reflect.has(target, key);
705
+ },
706
+ ownKeys(target) {
707
+ trackIteration(metadata);
708
+ return Reflect.ownKeys(target);
709
+ },
710
+ defineProperty() {
711
+ throw new TypeError("deepSignal() does not support property descriptors");
712
+ },
713
+ preventExtensions() {
714
+ throw new TypeError("deepSignal() state must remain extensible");
715
+ },
716
+ setPrototypeOf() {
717
+ throw new TypeError("deepSignal() does not support prototype mutation");
718
+ }
719
+ });
720
+ metadata.proxy = proxy;
721
+ rawToMetadata.set(rawValue, metadata);
722
+ proxyToRaw.set(proxy, rawValue);
723
+ return proxy;
724
+ };
725
+ class DeepSignalImpl {
726
+ #source;
727
+ constructor(source) {
728
+ this.#source = source;
729
+ const protocol = getReadableInterop(this.#source);
730
+ if (protocol !== void 0) attachReadableInterop(this, protocol);
731
+ }
732
+ get value() {
733
+ return wrap(this.#source.value);
734
+ }
735
+ set value(nextValue) {
736
+ const rawValue = unwrap(nextValue);
737
+ assertRootValue(rawValue);
738
+ this.#source.value = rawValue;
739
+ }
740
+ peek() {
741
+ return this.#source.peek();
742
+ }
743
+ }
744
+ /**
745
+ * Reports the per-key reactive metadata currently retained for a deep proxy
746
+ * (or for the raw object behind it). Exposed for this package's own tests
747
+ * only — it is deliberately absent from every published entry point, and the
748
+ * shape it returns is internal detail, not API.
749
+ */
750
+ function inspectDeepSignalMetadata(value) {
751
+ const metadata = rawToMetadata.get(toRaw(value) ?? value);
752
+ if (metadata === void 0) return void 0;
753
+ return {
754
+ properties: [...metadata.properties.keys()],
755
+ existence: [...metadata.existence.keys()],
756
+ propertyIndices: [...metadata.propertyIndices],
757
+ existenceIndices: [...metadata.existenceIndices]
758
+ };
759
+ }
760
+ /**
761
+ * Creates a signal that lazily tracks nested plain-object and array properties.
762
+ * Mutations must go through `.value`; changes made through the original raw
763
+ * object are intentionally not observable.
764
+ */
765
+ function deepSignal(initialValue) {
766
+ const rawInitialValue = prepareDeepValue(initialValue);
767
+ assertRootValue(rawInitialValue);
768
+ wrap(rawInitialValue);
769
+ const root = adapter.createSignal(rawInitialValue);
770
+ const result = new DeepSignalImpl(root);
771
+ adapter.registerDeepSignal?.(result);
772
+ return result;
773
+ }
774
+ return {
775
+ deepSignal,
776
+ inspectDeepSignalMetadata
777
+ };
778
+ }
779
+ //#endregion
780
+ //#region src/core/deep-signal.ts
781
+ let productionDeepSignals;
782
+ function getProductionDeepSignals() {
783
+ return productionDeepSignals ??= createDeepSignalFactory({
784
+ createSignal(initial) {
785
+ return coreRuntime.createDeepSignal(initial);
786
+ },
787
+ createVersionSignal(initial) {
788
+ return coreRuntime.createDeepSignalVersion(initial);
789
+ },
790
+ markWatched(source) {
791
+ coreRuntime.markDeepSignalWatched(source);
792
+ },
793
+ hasSubscribers(source) {
794
+ return coreRuntime.hasDeepSignalSubscribers(source);
795
+ },
796
+ batch,
797
+ untracked,
798
+ isSignal,
799
+ hasActiveSubscriber: () => {
800
+ const owner = executionContext.owner;
801
+ if (owner === UNTRACKED_OWNER) return false;
802
+ return coreRuntime.hasActiveSubscriber() || owner !== void 0 && typeof owner !== "symbol" && (owner.kind === "graph" || owner.kind === "render");
803
+ },
804
+ getBatchDepth: () => coreRuntime.getBatchDepth(),
805
+ isSpeculative: () => coreRuntime.isSpeculative(),
806
+ markSpeculativeDeepRead: () => coreRuntime.markSpeculativeDeepRead(),
807
+ registerDeepSignal(value) {
808
+ registerSignal(value);
809
+ }
810
+ });
811
+ }
812
+ /**
813
+ * Creates a signal that lazily tracks nested plain-object and array properties.
814
+ * Mutations must go through `.value`; changes made through the original raw
815
+ * object are intentionally not observable.
816
+ */
817
+ function deepSignal(initialValue) {
818
+ return getProductionDeepSignals().deepSignal(initialValue);
819
+ }
820
+ //#endregion
821
+ //#region src/core/readable-subscription.ts
822
+ /** Subscribe through the private V1 boundary without creating an EffectNode. */
823
+ function subscribeReadableV1(readable, listener) {
824
+ const protocol = getReadableInterop(readable);
825
+ if (protocol === void 0) return void 0;
826
+ const subscription = protocol.subscribe(() => notifyListener(listener));
827
+ return () => subscription.unsubscribe();
828
+ }
829
+ //#endregion
830
+ //#region src/react/hooks.ts
831
+ const EMPTY_DEPENDENCIES = [];
832
+ function assertSignalSnapshot(value) {
833
+ 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");
834
+ }
835
+ /**
836
+ * Shared `useSyncExternalStore` wiring for effect-backed subscriptions only:
837
+ * the dynamic deep-selector store and structural-readable compatibility
838
+ * fallback. Package readables with ReadableInterop V1 use a direct watcher.
839
+ * On `subscribe`, starts an `effect()` that reruns `onEvaluate` on relevant
840
+ * writes and notifies React when it reports a change.
841
+ *
842
+ * `getSnapshot` is intentionally not this helper's concern — the caller
843
+ * wires its own, whether that means recomputing fresh each call
844
+ * (`useSignalValue`) or returning a cached result (the deep-selector store).
845
+ *
846
+ * An exception thrown by `onEvaluate` is swallowed here and treated as a
847
+ * change worth notifying about. That leaves the next render-time snapshot
848
+ * read to surface the error to an Error Boundary rather than letting the
849
+ * background effect's exception escape through the signal writer.
850
+ */
851
+ function createEffectBackedStoreSubscription(onEvaluate) {
852
+ return (notify) => {
853
+ let isInitialRun = true;
854
+ return detachedEffect(() => {
855
+ let changed;
856
+ try {
857
+ changed = onEvaluate();
858
+ } catch {
859
+ changed = true;
860
+ }
861
+ if (isInitialRun) {
862
+ isInitialRun = false;
863
+ return;
864
+ }
865
+ if (changed) notify();
866
+ });
867
+ };
868
+ }
869
+ /**
870
+ * Holds a selector result outside the reactive graph. A selector error must
871
+ * not escape from the signal write that caused a reactive re-evaluation:
872
+ * React needs to observe it during its next render so an Error Boundary can
873
+ * handle it. The cached result also avoids rerunning the selector on every
874
+ * render.
875
+ */
876
+ function createDeepSelectorStore(source, selector) {
877
+ const evaluate = () => {
878
+ try {
879
+ const value = untrackedRender(() => selector(source.value));
880
+ assertSignalSnapshot(value);
881
+ return {
882
+ kind: "value",
883
+ value
884
+ };
885
+ } catch (error) {
886
+ return {
887
+ kind: "error",
888
+ error
889
+ };
890
+ }
891
+ };
892
+ let result = evaluate();
893
+ const hasChanged = (next) => {
894
+ if (result.kind !== next.kind) return true;
895
+ 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;
896
+ };
897
+ return {
898
+ subscribe: createEffectBackedStoreSubscription(() => {
899
+ const next = evaluate();
900
+ if (!hasChanged(next)) return false;
901
+ result = next;
902
+ return true;
903
+ }),
904
+ getSnapshot() {
905
+ if (result.kind === "error") throw result.error;
906
+ return result.value;
907
+ }
908
+ };
909
+ }
910
+ /** Creates a signal whose identity is stable for the lifetime of this component. */
911
+ function useSignal(initialValue) {
912
+ const signalRef = useRef(void 0);
913
+ if (signalRef.current === void 0) signalRef.current = signal(initialValue);
914
+ return signalRef.current;
915
+ }
916
+ /**
917
+ * Creates a deep signal whose identity is stable for the component lifetime.
918
+ * A factory is evaluated only while initializing a mounted component instance;
919
+ * it must remain pure because React Strict Mode may replay initial rendering.
920
+ */
921
+ function useDeepSignal(initialValue) {
922
+ const signalRef = useRef(void 0);
923
+ if (signalRef.current === void 0) signalRef.current = deepSignal(typeof initialValue === "function" ? initialValue() : initialValue);
924
+ return signalRef.current;
925
+ }
926
+ /**
927
+ * Selects a property-level primitive snapshot from a deep signal.
928
+ *
929
+ * Every non-signal value captured by `selector` must be listed in
930
+ * `dependencies`. Object and proxy results are intentionally rejected because
931
+ * mutable snapshots cannot satisfy `useSyncExternalStore` identity semantics.
932
+ *
933
+ * `dependencies` must keep a fixed length across this component's lifetime,
934
+ * matching `useMemo`'s own rule (its length feeds a `useMemo` deps array
935
+ * below via `[source, ...dependencies]`). A length change is caught and
936
+ * thrown as a clear error rather than left to degrade into React's silent
937
+ * "changed size between renders" dev warning, mirroring `useComputed`'s
938
+ * dependency-mode-switch guard.
939
+ */
940
+ function useDeepSignalValue(source, selector, dependencies) {
941
+ const initialDependencyLengthRef = useRef(void 0);
942
+ initialDependencyLengthRef.current ??= dependencies.length;
943
+ if (dependencies.length !== initialDependencyLengthRef.current) {
944
+ 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.`);
945
+ error.name = "UseDeepSignalValueDependencyLengthChangeError";
946
+ throw error;
947
+ }
948
+ const store = useMemo(() => createDeepSelectorStore(source, selector), [source, ...dependencies]);
949
+ return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
950
+ }
951
+ /**
952
+ * Creates a computed signal with a stable identity.
953
+ *
954
+ * When `dependencies` is omitted, the getter must only read signals. Its initial
955
+ * closure is retained for the component lifetime, so props, state, and other
956
+ * non-signal values must not be captured in that mode.
957
+ *
958
+ * When the getter captures props, state, or any other non-signal value, list all
959
+ * of those values in `dependencies`. React memoization then creates a separate
960
+ * computed when they change, rather than replacing the getter of an existing
961
+ * computed during render. An abandoned render therefore cannot change the
962
+ * closure used by the previously committed computed. Choose one mode for a
963
+ * component's lifetime.
964
+ */
965
+ function useComputed(getValue, dependencies) {
966
+ const dependencyComputed = useMemo(() => dependencies === void 0 ? void 0 : computed(getValue), dependencies ?? EMPTY_DEPENDENCIES);
967
+ const initialModeRef = useRef(void 0);
968
+ const signalOnlyComputedRef = useRef(void 0);
969
+ const mode = dependencies === void 0 ? "without a dependency array" : "with a dependency array";
970
+ initialModeRef.current ??= mode;
971
+ if (initialModeRef.current !== mode) {
972
+ 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.`);
973
+ error.name = "UseComputedModeChangeError";
974
+ throw error;
975
+ }
976
+ if (dependencyComputed !== void 0) return dependencyComputed;
977
+ if (signalOnlyComputedRef.current === void 0) signalOnlyComputedRef.current = computed(getValue);
978
+ return signalOnlyComputedRef.current;
979
+ }
980
+ /**
981
+ * Runs a reactive effect after this component has committed, disposing it when
982
+ * the component unmounts (and during React Strict Mode's development replay).
983
+ *
984
+ * When `dependencies` is omitted, the callback must only capture signals. Its
985
+ * initial closure is retained for the component lifetime, so unrelated React
986
+ * renders do not restart the effect.
987
+ *
988
+ * When the callback captures props, state, or any other non-signal value, list
989
+ * all of those values in `dependencies`. The effect is then reconnected after
990
+ * those dependencies change. Choose one mode for a component's lifetime.
991
+ */
992
+ function useSignalEffect(callback, dependencies) {
993
+ useEffect(() => detachedEffect(callback), dependencies ?? EMPTY_DEPENDENCIES);
994
+ }
995
+ /**
996
+ * Reads a signal and subscribes the component to subsequent changes through
997
+ * ReadableInterop V1 when available.
998
+ *
999
+ * `useSyncExternalStore` owns the initial consistency check after subscribing.
1000
+ * For V1 readables, its watcher tracks revisions without an EffectNode. A
1001
+ * read that throws (a computed whose cached error `.value` rethrows) remains
1002
+ * for the render-time `getSnapshot` to surface to an Error Boundary; only the
1003
+ * structural-readable fallback needs an effect to contain background errors.
1004
+ *
1005
+ * A `deepSignal` is rejected at compile time: this subscribes to root
1006
+ * replacement only, so nested mutations would never re-render. Read deep state
1007
+ * with `useDeepSignalValue` or inside a tracked component instead.
1008
+ */
1009
+ function useSignalValue(source) {
1010
+ const subscribe = useCallback((notify) => {
1011
+ const unsubscribe = subscribeReadableV1(source, notify);
1012
+ if (unsubscribe !== void 0) return unsubscribe;
1013
+ return createEffectBackedStoreSubscription(() => {
1014
+ source.value;
1015
+ return true;
1016
+ })(notify);
1017
+ }, [source]);
1018
+ const getSnapshot = useCallback(() => untracked(() => source.value), [source]);
1019
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1020
+ }
1021
+ //#endregion
1022
+ //#region src/runtime/jsx.ts
1023
+ /** The small set of DOM properties that support direct signal bindings. */
1024
+ const REACTIVE_PROP_NAMES = /* @__PURE__ */ new Set([
1025
+ "title",
1026
+ "id",
1027
+ "className",
1028
+ "hidden",
1029
+ "disabled",
1030
+ "style",
1031
+ "value",
1032
+ "checked"
1033
+ ]);
1034
+ function isControlledTwoWayProp(tagName, name) {
1035
+ if (name === "value") return tagName === "input" || tagName === "textarea" || tagName === "select";
1036
+ if (name === "checked") return tagName === "input";
1037
+ return false;
1038
+ }
1039
+ function resolveBindingKind(tagName, name) {
1040
+ if (name === "style") return "style";
1041
+ if (isControlledTwoWayProp(tagName, name)) {
1042
+ if (name === "value") return tagName === "select" ? "select-value" : "text-value";
1043
+ return "checked";
1044
+ }
1045
+ return "prop";
1046
+ }
1047
+ function isTwoWayBindingKind(kind) {
1048
+ return kind === "select-value" || kind === "text-value" || kind === "checked";
1049
+ }
1050
+ const UNCONTROLLED_PROP_NAMES = {
1051
+ value: "defaultValue",
1052
+ checked: "defaultChecked"
1053
+ };
1054
+ const UNITLESS_CSS_PROPERTIES = /* @__PURE__ */ new Set([
1055
+ "animationIterationCount",
1056
+ "aspectRatio",
1057
+ "borderImageOutset",
1058
+ "borderImageSlice",
1059
+ "borderImageWidth",
1060
+ "boxFlex",
1061
+ "boxFlexGroup",
1062
+ "boxOrdinalGroup",
1063
+ "columnCount",
1064
+ "columns",
1065
+ "flex",
1066
+ "flexGrow",
1067
+ "flexPositive",
1068
+ "flexShrink",
1069
+ "flexNegative",
1070
+ "flexOrder",
1071
+ "gridArea",
1072
+ "gridColumn",
1073
+ "gridColumnEnd",
1074
+ "gridColumnSpan",
1075
+ "gridColumnStart",
1076
+ "gridRow",
1077
+ "gridRowEnd",
1078
+ "gridRowSpan",
1079
+ "gridRowStart",
1080
+ "fontWeight",
1081
+ "lineClamp",
1082
+ "lineHeight",
1083
+ "opacity",
1084
+ "order",
1085
+ "orphans",
1086
+ "scale",
1087
+ "tabSize",
1088
+ "WebkitLineClamp",
1089
+ "widows",
1090
+ "zIndex",
1091
+ "zoom"
1092
+ ]);
1093
+ const SVG_ELEMENTS = [
1094
+ "svg",
1095
+ "animate",
1096
+ "animateMotion",
1097
+ "animateTransform",
1098
+ "circle",
1099
+ "clipPath",
1100
+ "defs",
1101
+ "desc",
1102
+ "ellipse",
1103
+ "feBlend",
1104
+ "feColorMatrix",
1105
+ "feComponentTransfer",
1106
+ "feComposite",
1107
+ "feConvolveMatrix",
1108
+ "feDiffuseLighting",
1109
+ "feDisplacementMap",
1110
+ "feDistantLight",
1111
+ "feDropShadow",
1112
+ "feFlood",
1113
+ "feFuncA",
1114
+ "feFuncB",
1115
+ "feFuncG",
1116
+ "feFuncR",
1117
+ "feGaussianBlur",
1118
+ "feImage",
1119
+ "feMerge",
1120
+ "feMergeNode",
1121
+ "feMorphology",
1122
+ "feOffset",
1123
+ "fePointLight",
1124
+ "feSpecularLighting",
1125
+ "feSpotLight",
1126
+ "feTile",
1127
+ "feTurbulence",
1128
+ "filter",
1129
+ "foreignObject",
1130
+ "g",
1131
+ "image",
1132
+ "line",
1133
+ "linearGradient",
1134
+ "marker",
1135
+ "mask",
1136
+ "metadata",
1137
+ "mpath",
1138
+ "path",
1139
+ "pattern",
1140
+ "polygon",
1141
+ "polyline",
1142
+ "radialGradient",
1143
+ "rect",
1144
+ "set",
1145
+ "stop",
1146
+ "switch",
1147
+ "symbol",
1148
+ "text",
1149
+ "textPath",
1150
+ "tspan",
1151
+ "use",
1152
+ "view"
1153
+ ];
1154
+ const MATHML_ELEMENTS = [
1155
+ "annotation",
1156
+ "annotation-xml",
1157
+ "maction",
1158
+ "math",
1159
+ "merror",
1160
+ "mfrac",
1161
+ "mi",
1162
+ "mmultiscripts",
1163
+ "mn",
1164
+ "mo",
1165
+ "mover",
1166
+ "mpadded",
1167
+ "mphantom",
1168
+ "mprescripts",
1169
+ "mroot",
1170
+ "mrow",
1171
+ "ms",
1172
+ "mspace",
1173
+ "msqrt",
1174
+ "mstyle",
1175
+ "msub",
1176
+ "msubsup",
1177
+ "msup",
1178
+ "mtable",
1179
+ "mtd",
1180
+ "mtext",
1181
+ "mtr",
1182
+ "munder",
1183
+ "munderover",
1184
+ "semantics"
1185
+ ];
1186
+ const NON_HTML_HOST_ELEMENTS = /* @__PURE__ */ (() => {
1187
+ const tags = new Set(SVG_ELEMENTS);
1188
+ for (const tag of MATHML_ELEMENTS) tags.add(tag);
1189
+ return tags;
1190
+ })();
1191
+ const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
1192
+ /**
1193
+ * A leaf that lets React own reconciliation while its signal dependency stays
1194
+ * local to the leaf. A signal resolving to another signal/array is normalized
1195
+ * again so nested reactive children also work.
1196
+ */
1197
+ function SignalValue({ source }) {
1198
+ return normalizeChild(useSignalValue(source));
1199
+ }
1200
+ function normalizeChild(value) {
1201
+ if (isSignal(value)) return createElement(SignalValue, { source: value });
1202
+ if (Array.isArray(value)) return value.map(normalizeArrayItem);
1203
+ return value;
1204
+ }
1205
+ /**
1206
+ * A signal inside an array child has no way to carry a key of its own, so the
1207
+ * leaf it becomes is keyed by position. That is the identity such an array
1208
+ * already has (a literal `[a, b]` never reorders), and it keeps React's
1209
+ * missing-key warning for genuinely dynamic element lists intact. The prefix
1210
+ * keeps these keys out of the way of keys on the array's other elements.
1211
+ */
1212
+ function normalizeArrayItem(value, index) {
1213
+ if (isSignal(value)) return createElement(SignalValue, {
1214
+ source: value,
1215
+ key: `rfgs-signal:${index}`
1216
+ });
1217
+ return normalizeChild(value);
1218
+ }
1219
+ function isReactiveHostProp(name, value) {
1220
+ if (!isSignal(value)) return false;
1221
+ return REACTIVE_PROP_NAMES.has(name) || name.startsWith("data-") || name.startsWith("aria-");
1222
+ }
1223
+ function readInitialValue(source, kind) {
1224
+ const value = source.peek();
1225
+ if (kind === "style" && typeof value === "object" && value !== null && !Array.isArray(value)) return { ...value };
1226
+ return value;
1227
+ }
1228
+ /**
1229
+ * Reads a signal on behalf of one of this module's direct DOM bindings (see
1230
+ * the module doc: these write straight to the DOM from a subscription mounted
1231
+ * alongside the element's ref, bypassing React's render, so the owning
1232
+ * component never re-renders). A `computed()` whose getter throws caches and
1233
+ * rethrows that error on every read (see `updateComputed` in
1234
+ * src/core/alien-derived-runtime-core.mts) —
1235
+ * if `source` is such a computed and it starts failing after the binding is
1236
+ * already mounted, an unguarded read here could throw from the subscription's
1237
+ * update callback while processing the write. The direct V1 watcher contains
1238
+ * computed dirty-check errors, but this binding read is a separate operation;
1239
+ * catching it here also keeps the structural-readable effect fallback local.
1240
+ *
1241
+ * Catching here keeps a failure local to this one binding: the DOM write for
1242
+ * this cycle is skipped (the DOM is left at its last successful value) and
1243
+ * the failure is reported with `console.error(message, { cause: error })` —
1244
+ * assert against `mock.calls[i][1].cause` in tests, the same shape used by
1245
+ * `effect()`'s failure report (`reportFailure` in
1246
+ * src/core/alien-derived-runtime-core.mts) and by `render-tracking.ts`. Reporting is intentional here too: a direct binding
1247
+ * has no Error Boundary or other surface to fall back on, and silence would
1248
+ * mean a binding that mysteriously stops updating with zero trace.
1249
+ *
1250
+ * Reported at most once per contiguous run of failures ("episode"): `episode`
1251
+ * latches after the first report and is cleared the moment a read next
1252
+ * succeeds, so a later, distinct failure episode logs again. This keeps a
1253
+ * `computed()` that fails on every keystroke from spamming one `console.error`
1254
+ * per keystroke.
1255
+ */
1256
+ function readBoundSignal(read, episode) {
1257
+ try {
1258
+ const value = read();
1259
+ episode.hasReported = false;
1260
+ return {
1261
+ ok: true,
1262
+ value
1263
+ };
1264
+ } catch (error) {
1265
+ if (!episode.hasReported) {
1266
+ episode.hasReported = true;
1267
+ console.error("react-fine-grained-signals: a direct signal binding's read threw; skipping this update and leaving the DOM at its last value.", { cause: error });
1268
+ }
1269
+ return { ok: false };
1270
+ }
1271
+ }
1272
+ /**
1273
+ * Reads via `readBoundSignal` and, on success, hands the value to `apply`; a
1274
+ * failed read is already reported by `readBoundSignal`, so this just skips
1275
+ * the write. Shared by every read-and-apply call site below, including the
1276
+ * one that isn't itself a subscription callback — the MutationObserver-triggered
1277
+ * re-apply in `bindSelectValue`, which reads outside the reactive graph via
1278
+ * `.peek()`.
1279
+ */
1280
+ function applyBoundSignal(read, apply, episode) {
1281
+ const result = readBoundSignal(read, episode);
1282
+ if (result.ok) apply(result.value);
1283
+ }
1284
+ function createBindingSubscription(source, apply, episode = { hasReported: false }) {
1285
+ const update = () => applyBoundSignal(() => source.value, apply, episode);
1286
+ const refresh = () => untracked(update);
1287
+ const unsubscribe = subscribeReadableV1(source, update);
1288
+ if (unsubscribe !== void 0) {
1289
+ untracked(() => notifyListener(update));
1290
+ return {
1291
+ dispose: unsubscribe,
1292
+ refresh
1293
+ };
1294
+ }
1295
+ return {
1296
+ dispose: detachedEffect(update),
1297
+ refresh
1298
+ };
1299
+ }
1300
+ function setAttribute(node, name, value) {
1301
+ if (value == null) node.removeAttribute(name);
1302
+ else node.setAttribute(name, String(value));
1303
+ }
1304
+ function setDomProp(node, name, value) {
1305
+ if (node.namespaceURI !== HTML_NAMESPACE) {
1306
+ setAttribute(node, name === "className" ? "class" : name, value);
1307
+ return;
1308
+ }
1309
+ switch (name) {
1310
+ case "title":
1311
+ node.title = value == null ? "" : String(value);
1312
+ return;
1313
+ case "id":
1314
+ node.id = value == null ? "" : String(value);
1315
+ return;
1316
+ case "className":
1317
+ node.className = value == null ? "" : String(value);
1318
+ return;
1319
+ case "hidden":
1320
+ if (value === "until-found") setAttribute(node, "hidden", value);
1321
+ else node.hidden = Boolean(value);
1322
+ return;
1323
+ case "disabled":
1324
+ if ("disabled" in node) node.disabled = Boolean(value);
1325
+ else if (value == null || value === false) node.removeAttribute(name);
1326
+ else setAttribute(node, name, value);
1327
+ return;
1328
+ default: setAttribute(node, name, value);
1329
+ }
1330
+ }
1331
+ /**
1332
+ * Writes `value`/`checked` on a controlled-two-way element, skipping the DOM
1333
+ * write when it already holds the value being written. A binding update
1334
+ * follows signal writes, including keystrokes written by `onChange`, so without
1335
+ * this guard every keystroke would re-set a property the DOM already has,
1336
+ * moving the caret and disrupting IME composition for no reason.
1337
+ */
1338
+ function setControlledProp(node, name, value) {
1339
+ if (name === "value") {
1340
+ const select = node;
1341
+ if (select.tagName === "SELECT") {
1342
+ setSelectValue(select, value);
1343
+ return;
1344
+ }
1345
+ const next = value == null ? "" : String(value);
1346
+ const input = node;
1347
+ if (input.value !== next) input.value = next;
1348
+ if (input.defaultValue !== next) input.defaultValue = next;
1349
+ return;
1350
+ }
1351
+ const next = Boolean(value);
1352
+ const input = node;
1353
+ if (input.checked !== next) input.checked = next;
1354
+ if (input.defaultChecked !== next) input.defaultChecked = next;
1355
+ }
1356
+ /**
1357
+ * `<select multiple>` does not support assigning `.value` directly — the DOM
1358
+ * setter only ever selects a single option, silently corrupting the
1359
+ * selection instead of erroring. Its documented API for a multi-value select
1360
+ * is per-`<option>` `.selected`, so an array-valued signal (React's own
1361
+ * typing for a multi-select) is applied that way instead.
1362
+ */
1363
+ function setSelectValue(select, value) {
1364
+ const multiple = select.multiple;
1365
+ const values = new Set(Array.isArray(value) && multiple ? value.map(String) : value == null ? multiple ? [] : [""] : [String(value)]);
1366
+ if (!multiple) {
1367
+ const next = [...values][0];
1368
+ if (select.value !== next) select.value = next;
1369
+ }
1370
+ for (const option of select.options) {
1371
+ const selected = values.has(option.value);
1372
+ if (multiple && option.selected !== selected) option.selected = selected;
1373
+ if (option.defaultSelected !== selected) option.defaultSelected = selected;
1374
+ }
1375
+ }
1376
+ /**
1377
+ * A `<select>` whose matching `<option>` does not exist yet (for example when
1378
+ * the options themselves are rendered from a signal or other state, mounted
1379
+ * after this one) silently ends up with nothing selected: the DOM neither
1380
+ * errors nor retroactively applies `.value`/`.selected` once a matching
1381
+ * `<option>` is later added. The value subscription below only updates when
1382
+ * the bound signal itself changes, so it cannot see that the option list changed
1383
+ * out from under it. A MutationObserver on the select's subtree re-applies
1384
+ * the signal's current value whenever its `<option>` list changes, closing
1385
+ * that gap without requiring the signal to change too.
1386
+ */
1387
+ function bindSelectValue(select, source, apply) {
1388
+ const episode = { hasReported: false };
1389
+ const subscription = createBindingSubscription(source, apply, episode);
1390
+ const observer = new MutationObserver(() => {
1391
+ applyBoundSignal(() => source.peek(), apply, episode);
1392
+ });
1393
+ observer.observe(select, {
1394
+ childList: true,
1395
+ subtree: true
1396
+ });
1397
+ return {
1398
+ dispose() {
1399
+ subscription.dispose();
1400
+ observer.disconnect();
1401
+ },
1402
+ refresh: subscription.refresh
1403
+ };
1404
+ }
1405
+ /**
1406
+ * Forcing a `value` write while an IME composition is in progress can abort
1407
+ * the composition outright, independent of whether the string being written
1408
+ * happens to match what was typed — the write-skip guard in
1409
+ * `setControlledProp` only protects the same-value echo case, not this one.
1410
+ * `compositionstart`/`compositionend` listeners on the node itself track
1411
+ * composition state directly, regardless of whether the component declares
1412
+ * its own composition handlers, and a write requested while composing is
1413
+ * deferred until composition ends instead of applied immediately.
1414
+ */
1415
+ function bindTextValue(node, source, write) {
1416
+ let composing = false;
1417
+ let hasPending = false;
1418
+ let pending;
1419
+ const episode = { hasReported: false };
1420
+ const onCompositionStart = () => {
1421
+ composing = true;
1422
+ };
1423
+ const onCompositionEnd = () => {
1424
+ composing = false;
1425
+ if (hasPending) {
1426
+ hasPending = false;
1427
+ write(pending);
1428
+ }
1429
+ };
1430
+ node.addEventListener("compositionstart", onCompositionStart);
1431
+ node.addEventListener("compositionend", onCompositionEnd);
1432
+ const subscription = createBindingSubscription(source, (next) => {
1433
+ if (composing) {
1434
+ hasPending = true;
1435
+ pending = next;
1436
+ return;
1437
+ }
1438
+ write(next);
1439
+ }, episode);
1440
+ return {
1441
+ dispose() {
1442
+ subscription.dispose();
1443
+ node.removeEventListener("compositionstart", onCompositionStart);
1444
+ node.removeEventListener("compositionend", onCompositionEnd);
1445
+ },
1446
+ refresh: subscription.refresh
1447
+ };
1448
+ }
1449
+ function clearStyleProperty(style, key) {
1450
+ if (key.startsWith("--")) style.removeProperty(key);
1451
+ else style[key] = "";
1452
+ }
1453
+ function setStyleProperty(style, key, value) {
1454
+ if (value == null) {
1455
+ clearStyleProperty(style, key);
1456
+ return;
1457
+ }
1458
+ if (key.startsWith("--")) {
1459
+ style.setProperty(key, String(value));
1460
+ return;
1461
+ }
1462
+ style[key] = typeof value === "number" && !UNITLESS_CSS_PROPERTIES.has(key) ? `${value}px` : String(value);
1463
+ }
1464
+ /**
1465
+ * Applies a whole style object to an element, clearing keys that were present
1466
+ * in a previous call but are absent from this one. Only the coarse
1467
+ * `style={signal}` form is bound this way — an object whose individual entries
1468
+ * are themselves signals is out of scope (see development/design/direct-binding-value-checked-style.md).
1469
+ */
1470
+ function applyStyle(node, value, previousKeys) {
1471
+ const nextStyle = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1472
+ const nextKeys = Object.keys(nextStyle);
1473
+ const nextKeySet = new Set(nextKeys);
1474
+ for (const key of previousKeys) if (!nextKeySet.has(key)) clearStyleProperty(node.style, key);
1475
+ for (const key of nextKeys) setStyleProperty(node.style, key, nextStyle[key]);
1476
+ return nextKeys;
1477
+ }
1478
+ function applyRef(ref, node) {
1479
+ if (typeof ref === "function") return ref(node);
1480
+ if (ref != null) ref.current = node;
1481
+ }
1482
+ /** A binding's identity for the re-render diff: same name, source, and kind. */
1483
+ function isSameBinding(a, b) {
1484
+ return b !== void 0 && a[0] === b[0] && a[1] === b[1] && a[2] === b[2];
1485
+ }
1486
+ /** Whether two style values set the same properties to the same values. */
1487
+ function isSameStyle(a, b) {
1488
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return Object.is(a, b);
1489
+ const keys = Object.keys(a);
1490
+ return keys.length === Object.keys(b).length && keys.every((key) => Object.is(a[key], b[key]));
1491
+ }
1492
+ /**
1493
+ * Whether React was handed the same value for this binding's prop. A two-way
1494
+ * binding hands React an uncontrolled default that it reads only at mount, so
1495
+ * only `"prop"` and `"style"` snapshots count; a style snapshot is a fresh copy
1496
+ * every render (see `readInitialValue`), so it is compared by content.
1497
+ */
1498
+ function hasSameSnapshot(a, b) {
1499
+ return isTwoWayBindingKind(a[2]) || (a[2] === "style" ? isSameStyle(a[3], b[3]) : Object.is(a[3], b[3]));
1500
+ }
1501
+ /**
1502
+ * Subscribes a single binding to `node` and returns its teardown. Split out of
1503
+ * the ref callback so `NodeBinder` can rebuild one binding on
1504
+ * its own, without disturbing the siblings that did not change.
1505
+ *
1506
+ * `initialStyleKeys` seeds a `"style"` binding's own `previousKeys` (see
1507
+ * `getInitialStyleKeys`), so the fresh binding starts already knowing which
1508
+ * CSS properties may be on the node, instead of starting from an empty set
1509
+ * and never clearing them. Ignored for every other kind.
1510
+ */
1511
+ function subscribeBinding(node, name, source, kind, initialStyleKeys, binder) {
1512
+ const whileAttached = (write) => (value) => {
1513
+ if (binder.attached) write(value);
1514
+ };
1515
+ switch (kind) {
1516
+ case "style": {
1517
+ let previousKeys = initialStyleKeys ?? [];
1518
+ let applied;
1519
+ const write = (value) => {
1520
+ applied = value;
1521
+ previousKeys = applyStyle(node, value, previousKeys);
1522
+ };
1523
+ const subscription = createBindingSubscription(source, whileAttached(write));
1524
+ return {
1525
+ ...subscription,
1526
+ refresh: (snapshot) => {
1527
+ previousKeys = getInitialStyleKeys(previousKeys, snapshot);
1528
+ subscription.refresh();
1529
+ },
1530
+ getStyleKeys: () => previousKeys,
1531
+ restore: (snapshot) => {
1532
+ if (!isSameStyle(applied, snapshot)) write(snapshot);
1533
+ }
1534
+ };
1535
+ }
1536
+ case "select-value": return {
1537
+ ...bindSelectValue(node, source, whileAttached((value) => setControlledProp(node, "value", value))),
1538
+ getStyleKeys: void 0,
1539
+ restore: void 0
1540
+ };
1541
+ case "text-value": return {
1542
+ ...bindTextValue(node, source, whileAttached((value) => setControlledProp(node, "value", value))),
1543
+ getStyleKeys: void 0,
1544
+ restore: void 0
1545
+ };
1546
+ case "checked": return {
1547
+ ...createBindingSubscription(source, whileAttached((value) => setControlledProp(node, name, value))),
1548
+ getStyleKeys: void 0,
1549
+ restore: void 0
1550
+ };
1551
+ case "prop": {
1552
+ let applied;
1553
+ const write = (value) => {
1554
+ applied = value;
1555
+ setDomProp(node, name, value);
1556
+ };
1557
+ return {
1558
+ ...createBindingSubscription(source, whileAttached(write)),
1559
+ getStyleKeys: void 0,
1560
+ restore: (snapshot) => {
1561
+ if (!Object.is(applied, snapshot)) write(snapshot);
1562
+ }
1563
+ };
1564
+ }
1565
+ }
1566
+ }
1567
+ /**
1568
+ * The CSS properties a new `"style"` binding may find on the node: React wrote
1569
+ * this binding's render-time snapshot, and a replaced binding (`staleKeys`) may
1570
+ * have written keys React never saw. The signal can drop any of them before the
1571
+ * binding attaches (a write between render and commit, an Activity or Suspense
1572
+ * reveal), so the first write must be able to clear all of them. A kept
1573
+ * binding re-attached for a newer render widens its own keys the same way.
1574
+ */
1575
+ function getInitialStyleKeys(staleKeys, snapshot) {
1576
+ const keys = typeof snapshot === "object" && snapshot !== null && !Array.isArray(snapshot) ? Object.keys(snapshot) : [];
1577
+ if (staleKeys === void 0) return keys;
1578
+ for (const key of staleKeys) if (!keys.includes(key)) keys.push(key);
1579
+ return keys;
1580
+ }
1581
+ function mountBinding(node, binding, binder, staleStyleKeys) {
1582
+ const initialStyleKeys = binding[2] === "style" ? getInitialStyleKeys(staleStyleKeys, binding[3]) : void 0;
1583
+ const { dispose, refresh, getStyleKeys, restore } = subscribeBinding(node, binding[0], binding[1], binding[2], initialStyleKeys, binder);
1584
+ return {
1585
+ binding,
1586
+ dispose,
1587
+ refresh,
1588
+ getStyleKeys,
1589
+ restore
1590
+ };
1591
+ }
1592
+ /**
1593
+ * Owns the direct bindings mounted on one DOM node.
1594
+ *
1595
+ * Bindings are attached through a callback ref on the host element itself
1596
+ * rather than through a wrapper component. A wrapper would change the element
1597
+ * type whenever a prop switched between a signal and a plain value, and React
1598
+ * remounts a subtree whose element type changes, losing descendant state and
1599
+ * focus. With the ref, the element stays the same host type in both cases.
1600
+ *
1601
+ * The binder is keyed by the node, not by the ref callback, because React
1602
+ * replaces a callback ref whose identity changed by detaching the old one and
1603
+ * attaching the new one to the same node within a single commit. Tearing the
1604
+ * bindings down on that detach would recreate every subscription — and reset
1605
+ * `bindTextValue`'s in-flight IME composition state — on unrelated re-renders.
1606
+ * So a detach only schedules the teardown, and a re-attach of the same node in
1607
+ * the same commit reconciles the bindings in place instead (`syncBindings`
1608
+ * diffs by `(name, source, kind)`). A detach that is not followed by an attach
1609
+ * (unmount, a hidden Activity or Suspense boundary) tears down in a microtask.
1610
+ *
1611
+ * The user's own ref is forwarded synchronously on every attach and detach,
1612
+ * which is exactly what React would do with it directly; `getBindingRef` keeps
1613
+ * the callback's identity stable whenever the inputs are, so a stable user ref
1614
+ * is not churned.
1615
+ */
1616
+ var NodeBinder = class {
1617
+ #node;
1618
+ #mounted = [];
1619
+ #token = 0;
1620
+ #detached = false;
1621
+ constructor(node) {
1622
+ this.#node = node;
1623
+ }
1624
+ /** Whether React currently has this node attached through a binding ref. */
1625
+ get attached() {
1626
+ return !this.#detached;
1627
+ }
1628
+ attach(bindings, userRef) {
1629
+ const token = ++this.#token;
1630
+ const wasDetached = this.#detached;
1631
+ this.#detached = false;
1632
+ if (wasDetached) for (const entry of this.#mounted) {
1633
+ const next = bindings.find((binding) => binding[0] === entry.binding[0]);
1634
+ if (isSameBinding(entry.binding, next)) entry.refresh(next[3]);
1635
+ }
1636
+ this.#sync(bindings);
1637
+ let userCleanup;
1638
+ try {
1639
+ userCleanup = applyRef(userRef, this.#node);
1640
+ } catch (error) {
1641
+ this.#detach(token, bindings);
1642
+ throw error;
1643
+ }
1644
+ return () => {
1645
+ try {
1646
+ if (typeof userCleanup === "function") userCleanup();
1647
+ else applyRef(userRef, null);
1648
+ } finally {
1649
+ this.#detach(token, bindings);
1650
+ }
1651
+ };
1652
+ }
1653
+ /**
1654
+ * Hands the node back to React for the attach identified by `token`: writes
1655
+ * are held back from now on, and the bindings are disposed in a microtask
1656
+ * unless the same node is attached again first (see the class doc).
1657
+ */
1658
+ #detach(token, bindings) {
1659
+ if (token !== this.#token) return;
1660
+ this.#detached = true;
1661
+ this.#mounted.forEach((entry, index) => entry.restore?.(bindings[index][3]));
1662
+ queueMicrotask(() => {
1663
+ if (this.#detached && token === this.#token) this.#dispose();
1664
+ });
1665
+ }
1666
+ #dispose() {
1667
+ const mounted = this.#mounted;
1668
+ this.#mounted = [];
1669
+ for (const binding of mounted) binding.dispose();
1670
+ }
1671
+ #sync(bindings) {
1672
+ const mounted = this.#mounted;
1673
+ if (mounted.length === bindings.length && mounted.every((entry, index) => isSameBinding(entry.binding, bindings[index]))) return;
1674
+ const reusable = /* @__PURE__ */ new Map();
1675
+ for (const entry of mounted) reusable.set(entry.binding[0], entry);
1676
+ const reused = bindings.map((binding) => {
1677
+ const candidate = reusable.get(binding[0]);
1678
+ if (candidate === void 0 || !isSameBinding(candidate.binding, binding)) return void 0;
1679
+ reusable.delete(binding[0]);
1680
+ return candidate;
1681
+ });
1682
+ const staleStyleKeys = /* @__PURE__ */ new Map();
1683
+ for (const stale of reusable.values()) if (stale.getStyleKeys !== void 0) staleStyleKeys.set(stale.binding[0], stale.getStyleKeys());
1684
+ for (const stale of reusable.values()) stale.dispose();
1685
+ const node = this.#node;
1686
+ this.#mounted = bindings.map((binding, index) => reused[index] ?? mountBinding(node, binding, this, staleStyleKeys.get(binding[0])));
1687
+ }
1688
+ };
1689
+ const nodeBinders = /* @__PURE__ */ new WeakMap();
1690
+ function createBindingRef(bindings, userRef) {
1691
+ return (node) => {
1692
+ if (node == null) return;
1693
+ let binder = nodeBinders.get(node);
1694
+ if (binder === void 0) {
1695
+ binder = new NodeBinder(node);
1696
+ nodeBinders.set(node, binder);
1697
+ }
1698
+ return binder.attach(bindings, userRef);
1699
+ };
1700
+ }
1701
+ const bindingRefs = /* @__PURE__ */ new WeakMap();
1702
+ const BINDING_REF_CACHE_LIMIT = 8;
1703
+ function haveSameBindings(a, b) {
1704
+ return a.length === b.length && a.every((binding, index) => isSameBinding(binding, b[index]) && hasSameSnapshot(binding, b[index]));
1705
+ }
1706
+ function getBindingRef(bindings, userRef) {
1707
+ const cacheKey = userRef ?? bindings[0][1];
1708
+ const cache = bindingRefs;
1709
+ let entries = cache.get(cacheKey);
1710
+ if (entries !== void 0) {
1711
+ for (const entry of entries) if (entry.userRef === userRef && haveSameBindings(entry.bindings, bindings)) return entry.ref;
1712
+ } else {
1713
+ entries = [];
1714
+ cache.set(cacheKey, entries);
1715
+ }
1716
+ const ref = createBindingRef(bindings, userRef);
1717
+ if (entries.length >= BINDING_REF_CACHE_LIMIT) entries.shift();
1718
+ entries.push({
1719
+ bindings,
1720
+ userRef,
1721
+ ref
1722
+ });
1723
+ return ref;
1724
+ }
1725
+ /**
1726
+ * Scans a native host element's props for anything reactive (see
1727
+ * `isReactiveHostProp`). Kept separate from building the transformed props
1728
+ * copy in `transformHostProps` so that function can decide whether a copy is
1729
+ * needed at all before ever allocating one.
1730
+ */
1731
+ function findHostBindings(props, tagName) {
1732
+ const bindings = [];
1733
+ for (const [name, value] of Object.entries(props)) if (isReactiveHostProp(name, value)) {
1734
+ const kind = resolveBindingKind(tagName, name);
1735
+ bindings.push([
1736
+ name,
1737
+ value,
1738
+ kind,
1739
+ readInitialValue(value, kind)
1740
+ ]);
1741
+ }
1742
+ return bindings;
1743
+ }
1744
+ /**
1745
+ * Transforms a native host element's (`<div>`, `<svg>`, ...) props: replaces
1746
+ * any directly-bound prop with its initial, non-reactive value (`bindings`
1747
+ * carries the reactive ones for the binding ref to mount later — see
1748
+ * `createJsxWrapper`) and normalizes a signal/array `children` the same way
1749
+ * `Fragment` does.
1750
+ *
1751
+ * The overwhelmingly common host element has neither. Scanning first and only
1752
+ * allocating a copy when something is actually found means a plain
1753
+ * `<div className="card">…</div>` costs one prop walk and zero extra
1754
+ * allocations — the original props object is returned as-is (see the
1755
+ * `bindings.length === 0 && !childrenNeedNormalization` branch below), and
1756
+ * `createJsxWrapper` passes it straight to `factory` unchanged.
1757
+ */
1758
+ function transformHostProps(type, input) {
1759
+ const inputProps = input ?? {};
1760
+ const bindings = !NON_HTML_HOST_ELEMENTS.has(type) ? findHostBindings(inputProps, type) : [];
1761
+ const rawChildren = inputProps.children;
1762
+ const childrenNeedNormalization = "children" in inputProps && (isSignal(rawChildren) || Array.isArray(rawChildren));
1763
+ if (bindings.length === 0 && !childrenNeedNormalization) return {
1764
+ props: inputProps,
1765
+ bindings
1766
+ };
1767
+ const props = { ...inputProps };
1768
+ if (childrenNeedNormalization) props.children = normalizeChild(rawChildren);
1769
+ for (const [name, , kind, snapshot] of bindings) {
1770
+ const uncontrolledName = isTwoWayBindingKind(kind) ? UNCONTROLLED_PROP_NAMES[name] : void 0;
1771
+ if (uncontrolledName !== void 0) {
1772
+ delete props[name];
1773
+ props[uncontrolledName] = snapshot;
1774
+ } else props[name] = snapshot;
1775
+ }
1776
+ return {
1777
+ props,
1778
+ bindings
1779
+ };
1780
+ }
1781
+ /**
1782
+ * `Fragment`'s only prop worth transforming is `children`, and only when it
1783
+ * is itself a signal or array — `normalizeChild` is the identity for
1784
+ * anything else (see its definition) — so a plain `<>...</>` with ordinary
1785
+ * children is passed straight through, uncopied, the same way a custom
1786
+ * component is below.
1787
+ */
1788
+ function transformFragmentProps(input) {
1789
+ const props = input;
1790
+ if (props == null || !("children" in props) || !isSignal(props.children) && !Array.isArray(props.children)) return input;
1791
+ return {
1792
+ ...props,
1793
+ children: normalizeChild(props.children)
1794
+ };
1795
+ }
1796
+ /**
1797
+ * A host element's final props: the transformed copy with the binding ref in
1798
+ * place of the user's own ref, which the binding ref forwards to. `props` is
1799
+ * always a fresh copy whenever there are bindings (see `transformHostProps`).
1800
+ */
1801
+ function withBindingRef(props, bindings) {
1802
+ if (bindings.length > 0) props.ref = getBindingRef(bindings, props.ref);
1803
+ return props;
1804
+ }
1805
+ /** Creates a JSX wrapper while letting each module supply React's JSX factory. */
1806
+ function createJsxWrapper(factory) {
1807
+ return (type, input, key) => {
1808
+ if (typeof type === "string") {
1809
+ const { props, bindings } = transformHostProps(type, input);
1810
+ return factory(type, withBindingRef(props, bindings), key);
1811
+ }
1812
+ if (type === Fragment) return factory(type, transformFragmentProps(input), key);
1813
+ return factory(type, input, key);
1814
+ };
1815
+ }
1816
+ /**
1817
+ * The classic-runtime `createElement` that the automatic runtime falls back to
1818
+ * for `<div {...props} key="k" />` — TypeScript, Babel and Oxc all import it
1819
+ * from the package root of `jsxImportSource`. It applies the same host-prop
1820
+ * bindings and child normalization as `jsx`, and leaves `key` (and `ref`) in
1821
+ * the config for React's own `createElement` to extract.
1822
+ */
1823
+ function createSignalAwareElement(type, config, ...children) {
1824
+ if (typeof type !== "string" && type !== Fragment) return createElement(type, config, ...children);
1825
+ const normalizedChildren = children.map((child) => normalizeChild(child));
1826
+ if (typeof type !== "string") return createElement(type, config, ...normalizedChildren);
1827
+ const { props, bindings } = transformHostProps(type, config ?? {});
1828
+ return createElement(type, withBindingRef(props, bindings), ...normalizedChildren);
1829
+ }
1830
+ //#endregion
1831
+ export { useDeepSignalValue as a, useSignalValue as c, useDeepSignal as i, deepSignal as l, createSignalAwareElement as n, useSignal as o, useComputed as r, useSignalEffect as s, createJsxWrapper as t };
1832
+
1833
+ //# sourceMappingURL=jsx-P6volZdr.js.map