@mmstack/primitives 21.0.28 → 21.0.29

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.
@@ -2440,6 +2440,82 @@ function isOpaque(value) {
2440
2440
  value !== null &&
2441
2441
  value[OPAQUE] === true);
2442
2442
  }
2443
+ /**
2444
+ * @internal Runtime brand carrying a store node's lazily-built leaf probe. Exported (like
2445
+ * {@link OPAQUE}) only so the `{ readonly [LEAF]: () => boolean }` brand on the store types is
2446
+ * nameable in the emitted declarations — not part of the supported surface; use {@link isLeaf}.
2447
+ */
2448
+ const LEAF = Symbol('@mmstack/primitives::store/LEAF');
2449
+ /**
2450
+ * @internal Whether a value is a terminal leaf: a concrete non-record/non-array value always is;
2451
+ * `null`/`undefined` is a leaf only when vivification is disabled (with vivify on it can still
2452
+ * materialize a container, so it stays a descendable substore).
2453
+ */
2454
+ function isLeafValue(value, vivifyEnabled) {
2455
+ if (value == null)
2456
+ return !vivifyEnabled;
2457
+ if (isOpaque(value))
2458
+ return true; // opaque always wins — even arrays
2459
+ return !Array.isArray(value) && !isRecord(value);
2460
+ }
2461
+ /**
2462
+ * @internal Constant leaf probes for nodes whose leaf-ness is statically known, so the reactive
2463
+ * `computed` can be skipped entirely.
2464
+ */
2465
+ function alwaysTrue() {
2466
+ return true;
2467
+ }
2468
+ function alwaysFalse() {
2469
+ return false;
2470
+ }
2471
+ /**
2472
+ * @internal Attaches a lazy, memoized leaf probe to a store node. The probe (`() => boolean`)
2473
+ * closes over the node's value signal and its (stable) vivify setting, building the backing
2474
+ * `computed` on first call so leaf-ness tracks the live value reactively without taxing every
2475
+ * node access. Idempotent.
2476
+ */
2477
+ function markAsLeaf(sig, value, vivifyEnabled, noUnionLeaves) {
2478
+ if (typeof sig[LEAF] !== 'function') {
2479
+ let memo;
2480
+ const probe = () => {
2481
+ if (memo)
2482
+ return memo();
2483
+ const v = untracked(value);
2484
+ memo =
2485
+ isOpaque(v) || (v == null && !vivifyEnabled) || noUnionLeaves
2486
+ ? isLeafValue(v, vivifyEnabled)
2487
+ ? alwaysTrue
2488
+ : alwaysFalse
2489
+ : computed(() => isLeafValue(value(), vivifyEnabled));
2490
+ return memo();
2491
+ };
2492
+ Object.defineProperty(sig, LEAF, {
2493
+ value: probe,
2494
+ enumerable: false,
2495
+ configurable: true,
2496
+ });
2497
+ }
2498
+ return sig;
2499
+ }
2500
+ /**
2501
+ * Reports whether a store node is currently a **leaf** — a terminal value the store does not
2502
+ * descend into (a primitive, `Date`, `RegExp`, {@link opaque} object, class instance, or a
2503
+ * `null`/`undefined` hole when vivification is off) rather than a record/array substore.
2504
+ *
2505
+ * Leaf-ness reflects the node's **live** value: the probe is reactive and memoized, so calling
2506
+ * `isLeaf` inside a `computed`/`effect` re-evaluates when the node's shape changes.
2507
+ *
2508
+ * @internal Exposed for advanced/niche interop only — not part of the supported public surface
2509
+ * and may change without a major version bump.
2510
+ *
2511
+ * @example
2512
+ * const s = store({ name: 'Ada', address: { city: 'London' } });
2513
+ * isLeaf(s.name); // true
2514
+ * isLeaf(s.address); // false — a substore
2515
+ */
2516
+ function isLeaf(value) {
2517
+ return isStore(value) && value[LEAF]?.() === true;
2518
+ }
2443
2519
  const IS_STORE = Symbol('@mmstack/primitives::store/IS_STORE');
2444
2520
  const SCOPE_PARENT = Symbol('@mmstack/primitives::store/SCOPE_PARENT');
2445
2521
  /**
@@ -2502,7 +2578,7 @@ function hasOwnKey(value, key) {
2502
2578
  * @internal
2503
2579
  * Makes an array store
2504
2580
  */
2505
- function toArrayStore(source, injector, vivify) {
2581
+ function toArrayStore(source, injector, vivify, noUnionLeaves = false) {
2506
2582
  if (isStore(source))
2507
2583
  return source;
2508
2584
  const isMutableSource = isMutable(source);
@@ -2612,9 +2688,10 @@ function toArrayStore(source, injector, vivify) {
2612
2688
  });
2613
2689
  const childSample = untracked(computation);
2614
2690
  const childVivify = resolveVivify(childSample, vivify);
2615
- const proxy = Array.isArray(childSample)
2616
- ? toArrayStore(computation, injector, childVivify)
2617
- : toStore(computation, injector, childVivify);
2691
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2692
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2693
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2694
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2618
2695
  const ref = new WeakRef(proxy);
2619
2696
  storeCache.set(idx, ref);
2620
2697
  PROXY_CLEANUP.register(proxy, { target, prop: idx }, ref);
@@ -2631,7 +2708,7 @@ function toArrayStore(source, injector, vivify) {
2631
2708
  * const state = store({ user: { name: 'John' } });
2632
2709
  * const nameSignal = state.user.name; // WritableSignal<string>
2633
2710
  */
2634
- function toStore(source, injector, vivify = false) {
2711
+ function toStore(source, injector, vivify = false, noUnionLeaves = false) {
2635
2712
  if (isStore(source))
2636
2713
  return source;
2637
2714
  if (!injector)
@@ -2672,7 +2749,7 @@ function toStore(source, injector, vivify = false) {
2672
2749
  return () => {
2673
2750
  if (!isWritableSource)
2674
2751
  return s;
2675
- return untracked(() => toStore(source.asReadonly(), injector, vivify));
2752
+ return untracked(() => toStore(source.asReadonly(), injector, vivify, noUnionLeaves));
2676
2753
  };
2677
2754
  if (prop === 'extend')
2678
2755
  return (seed) => scopedStore(s, seed, isMutableSource
@@ -2725,9 +2802,10 @@ function toStore(source, injector, vivify = false) {
2725
2802
  });
2726
2803
  const childSample = untracked(computation);
2727
2804
  const childVivify = resolveVivify(childSample, vivify);
2728
- const proxy = Array.isArray(childSample)
2729
- ? toArrayStore(computation, injector, childVivify)
2730
- : toStore(computation, injector, childVivify);
2805
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2806
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2807
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2808
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2731
2809
  const ref = new WeakRef(proxy);
2732
2810
  storeCache.set(prop, ref);
2733
2811
  PROXY_CLEANUP.register(proxy, { target, prop }, ref);
@@ -2820,14 +2898,14 @@ function scopedStore(parent, seed, kind, injector) {
2820
2898
  * @see {@link toStore}
2821
2899
  */
2822
2900
  function store(value, opt) {
2823
- return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false);
2901
+ return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2824
2902
  }
2825
2903
  /**
2826
2904
  * Creates a MutableSignalStore from a value.
2827
2905
  * @see {@link toStore}
2828
2906
  */
2829
2907
  function mutableStore(value, opt) {
2830
- return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false);
2908
+ return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2831
2909
  }
2832
2910
 
2833
2911
  // Internal dummy store for server-side rendering
@@ -3304,5 +3382,5 @@ function withHistory(sourceOrValue, opt) {
3304
3382
  * Generated bundle index. Do not edit.
3305
3383
  */
3306
3384
 
3307
- export { batteryStatus, chunked, clipboard, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, filterWith, focusWithin, geolocation, idle, indexArray, isDerivation, isMutable, isOpaque, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, opaque, orientation, pageVisibility, pairwise, pipeable, piped, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
3385
+ export { batteryStatus, chunked, clipboard, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, filterWith, focusWithin, geolocation, idle, indexArray, isDerivation, isLeaf, isMutable, isOpaque, isStore, keyArray, map, mapArray, mapObject, mediaQuery, mousePosition, mutable, mutableStore, nestedEffect, networkStatus, opaque, orientation, pageVisibility, pairwise, pipeable, piped, pooled, pooledArray, pooledMap, pooledSet, prefersDarkMode, prefersReducedMotion, scan, scrollPosition, select, sensor, sensors, signalFromEvent, startWith, store, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toStore, toWritable, until, windowSize, withHistory };
3308
3386
  //# sourceMappingURL=mmstack-primitives.mjs.map