@mmstack/primitives 22.0.2 → 22.0.3

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.
@@ -2459,6 +2459,82 @@ function isOpaque(value) {
2459
2459
  value !== null &&
2460
2460
  value[OPAQUE] === true);
2461
2461
  }
2462
+ /**
2463
+ * @internal Runtime brand carrying a store node's lazily-built leaf probe. Exported (like
2464
+ * {@link OPAQUE}) only so the `{ readonly [LEAF]: () => boolean }` brand on the store types is
2465
+ * nameable in the emitted declarations — not part of the supported surface; use {@link isLeaf}.
2466
+ */
2467
+ const LEAF = Symbol('@mmstack/primitives::store/LEAF');
2468
+ /**
2469
+ * @internal Whether a value is a terminal leaf: a concrete non-record/non-array value always is;
2470
+ * `null`/`undefined` is a leaf only when vivification is disabled (with vivify on it can still
2471
+ * materialize a container, so it stays a descendable substore).
2472
+ */
2473
+ function isLeafValue(value, vivifyEnabled) {
2474
+ if (value == null)
2475
+ return !vivifyEnabled;
2476
+ if (isOpaque(value))
2477
+ return true; // opaque always wins — even arrays
2478
+ return !Array.isArray(value) && !isRecord(value);
2479
+ }
2480
+ /**
2481
+ * @internal Constant leaf probes for nodes whose leaf-ness is statically known, so the reactive
2482
+ * `computed` can be skipped entirely.
2483
+ */
2484
+ function alwaysTrue() {
2485
+ return true;
2486
+ }
2487
+ function alwaysFalse() {
2488
+ return false;
2489
+ }
2490
+ /**
2491
+ * @internal Attaches a lazy, memoized leaf probe to a store node. The probe (`() => boolean`)
2492
+ * closes over the node's value signal and its (stable) vivify setting, building the backing
2493
+ * `computed` on first call so leaf-ness tracks the live value reactively without taxing every
2494
+ * node access. Idempotent.
2495
+ */
2496
+ function markAsLeaf(sig, value, vivifyEnabled, noUnionLeaves) {
2497
+ if (typeof sig[LEAF] !== 'function') {
2498
+ let memo;
2499
+ const probe = () => {
2500
+ if (memo)
2501
+ return memo();
2502
+ const v = untracked(value);
2503
+ memo =
2504
+ isOpaque(v) || (v == null && !vivifyEnabled) || noUnionLeaves
2505
+ ? isLeafValue(v, vivifyEnabled)
2506
+ ? alwaysTrue
2507
+ : alwaysFalse
2508
+ : computed(() => isLeafValue(value(), vivifyEnabled));
2509
+ return memo();
2510
+ };
2511
+ Object.defineProperty(sig, LEAF, {
2512
+ value: probe,
2513
+ enumerable: false,
2514
+ configurable: true,
2515
+ });
2516
+ }
2517
+ return sig;
2518
+ }
2519
+ /**
2520
+ * Reports whether a store node is currently a **leaf** — a terminal value the store does not
2521
+ * descend into (a primitive, `Date`, `RegExp`, {@link opaque} object, class instance, or a
2522
+ * `null`/`undefined` hole when vivification is off) rather than a record/array substore.
2523
+ *
2524
+ * Leaf-ness reflects the node's **live** value: the probe is reactive and memoized, so calling
2525
+ * `isLeaf` inside a `computed`/`effect` re-evaluates when the node's shape changes.
2526
+ *
2527
+ * @internal Exposed for advanced/niche interop only — not part of the supported public surface
2528
+ * and may change without a major version bump.
2529
+ *
2530
+ * @example
2531
+ * const s = store({ name: 'Ada', address: { city: 'London' } });
2532
+ * isLeaf(s.name); // true
2533
+ * isLeaf(s.address); // false — a substore
2534
+ */
2535
+ function isLeaf(value) {
2536
+ return isStore(value) && value[LEAF]?.() === true;
2537
+ }
2462
2538
  const IS_STORE = Symbol('@mmstack/primitives::store/IS_STORE');
2463
2539
  const SCOPE_PARENT = Symbol('@mmstack/primitives::store/SCOPE_PARENT');
2464
2540
  /**
@@ -2521,7 +2597,7 @@ function hasOwnKey(value, key) {
2521
2597
  * @internal
2522
2598
  * Makes an array store
2523
2599
  */
2524
- function toArrayStore(source, injector, vivify) {
2600
+ function toArrayStore(source, injector, vivify, noUnionLeaves = false) {
2525
2601
  if (isStore(source))
2526
2602
  return source;
2527
2603
  const isMutableSource = isMutable(source);
@@ -2632,9 +2708,10 @@ function toArrayStore(source, injector, vivify) {
2632
2708
  });
2633
2709
  const childSample = untracked(computation);
2634
2710
  const childVivify = resolveVivify(childSample, vivify);
2635
- const proxy = Array.isArray(childSample)
2636
- ? toArrayStore(computation, injector, childVivify)
2637
- : toStore(computation, injector, childVivify);
2711
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2712
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2713
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2714
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2638
2715
  const ref = new WeakRef(proxy);
2639
2716
  storeCache.set(idx, ref);
2640
2717
  PROXY_CLEANUP.register(proxy, { target, prop: idx }, ref);
@@ -2651,7 +2728,7 @@ function toArrayStore(source, injector, vivify) {
2651
2728
  * const state = store({ user: { name: 'John' } });
2652
2729
  * const nameSignal = state.user.name; // WritableSignal<string>
2653
2730
  */
2654
- function toStore(source, injector, vivify = false) {
2731
+ function toStore(source, injector, vivify = false, noUnionLeaves = false) {
2655
2732
  if (isStore(source))
2656
2733
  return source;
2657
2734
  if (!injector)
@@ -2692,7 +2769,7 @@ function toStore(source, injector, vivify = false) {
2692
2769
  return () => {
2693
2770
  if (!isWritableSource)
2694
2771
  return s;
2695
- return untracked(() => toStore(source.asReadonly(), injector, vivify));
2772
+ return untracked(() => toStore(source.asReadonly(), injector, vivify, noUnionLeaves));
2696
2773
  };
2697
2774
  if (prop === 'extend')
2698
2775
  return (seed) => scopedStore(s, seed, isMutableSource
@@ -2745,9 +2822,10 @@ function toStore(source, injector, vivify = false) {
2745
2822
  });
2746
2823
  const childSample = untracked(computation);
2747
2824
  const childVivify = resolveVivify(childSample, vivify);
2748
- const proxy = Array.isArray(childSample)
2749
- ? toArrayStore(computation, injector, childVivify)
2750
- : toStore(computation, injector, childVivify);
2825
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2826
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2827
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2828
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2751
2829
  const ref = new WeakRef(proxy);
2752
2830
  storeCache.set(prop, ref);
2753
2831
  PROXY_CLEANUP.register(proxy, { target, prop }, ref);
@@ -2841,14 +2919,14 @@ function scopedStore(parent, seed, kind, injector) {
2841
2919
  * @see {@link toStore}
2842
2920
  */
2843
2921
  function store(value, opt) {
2844
- return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false);
2922
+ return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2845
2923
  }
2846
2924
  /**
2847
2925
  * Creates a MutableSignalStore from a value.
2848
2926
  * @see {@link toStore}
2849
2927
  */
2850
2928
  function mutableStore(value, opt) {
2851
- return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false);
2929
+ return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2852
2930
  }
2853
2931
 
2854
2932
  // Internal dummy store for server-side rendering
@@ -3329,5 +3407,5 @@ function withHistory(sourceOrValue, opt) {
3329
3407
  * Generated bundle index. Do not edit.
3330
3408
  */
3331
3409
 
3332
- 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 };
3410
+ 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 };
3333
3411
  //# sourceMappingURL=mmstack-primitives.mjs.map