@mmstack/primitives 20.5.10 → 20.5.11

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.
@@ -2446,6 +2446,82 @@ function isOpaque(value) {
2446
2446
  value !== null &&
2447
2447
  value[OPAQUE] === true);
2448
2448
  }
2449
+ /**
2450
+ * @internal Runtime brand carrying a store node's lazily-built leaf probe. Exported (like
2451
+ * {@link OPAQUE}) only so the `{ readonly [LEAF]: () => boolean }` brand on the store types is
2452
+ * nameable in the emitted declarations — not part of the supported surface; use {@link isLeaf}.
2453
+ */
2454
+ const LEAF = Symbol('@mmstack/primitives::store/LEAF');
2455
+ /**
2456
+ * @internal Whether a value is a terminal leaf: a concrete non-record/non-array value always is;
2457
+ * `null`/`undefined` is a leaf only when vivification is disabled (with vivify on it can still
2458
+ * materialize a container, so it stays a descendable substore).
2459
+ */
2460
+ function isLeafValue(value, vivifyEnabled) {
2461
+ if (value == null)
2462
+ return !vivifyEnabled;
2463
+ if (isOpaque(value))
2464
+ return true; // opaque always wins — even arrays
2465
+ return !Array.isArray(value) && !isRecord(value);
2466
+ }
2467
+ /**
2468
+ * @internal Constant leaf probes for nodes whose leaf-ness is statically known, so the reactive
2469
+ * `computed` can be skipped entirely.
2470
+ */
2471
+ function alwaysTrue() {
2472
+ return true;
2473
+ }
2474
+ function alwaysFalse() {
2475
+ return false;
2476
+ }
2477
+ /**
2478
+ * @internal Attaches a lazy, memoized leaf probe to a store node. The probe (`() => boolean`)
2479
+ * closes over the node's value signal and its (stable) vivify setting, building the backing
2480
+ * `computed` on first call so leaf-ness tracks the live value reactively without taxing every
2481
+ * node access. Idempotent.
2482
+ */
2483
+ function markAsLeaf(sig, value, vivifyEnabled, noUnionLeaves) {
2484
+ if (typeof sig[LEAF] !== 'function') {
2485
+ let memo;
2486
+ const probe = () => {
2487
+ if (memo)
2488
+ return memo();
2489
+ const v = untracked(value);
2490
+ memo =
2491
+ isOpaque(v) || (v == null && !vivifyEnabled) || noUnionLeaves
2492
+ ? isLeafValue(v, vivifyEnabled)
2493
+ ? alwaysTrue
2494
+ : alwaysFalse
2495
+ : computed(() => isLeafValue(value(), vivifyEnabled));
2496
+ return memo();
2497
+ };
2498
+ Object.defineProperty(sig, LEAF, {
2499
+ value: probe,
2500
+ enumerable: false,
2501
+ configurable: true,
2502
+ });
2503
+ }
2504
+ return sig;
2505
+ }
2506
+ /**
2507
+ * Reports whether a store node is currently a **leaf** — a terminal value the store does not
2508
+ * descend into (a primitive, `Date`, `RegExp`, {@link opaque} object, class instance, or a
2509
+ * `null`/`undefined` hole when vivification is off) rather than a record/array substore.
2510
+ *
2511
+ * Leaf-ness reflects the node's **live** value: the probe is reactive and memoized, so calling
2512
+ * `isLeaf` inside a `computed`/`effect` re-evaluates when the node's shape changes.
2513
+ *
2514
+ * @internal Exposed for advanced/niche interop only — not part of the supported public surface
2515
+ * and may change without a major version bump.
2516
+ *
2517
+ * @example
2518
+ * const s = store({ name: 'Ada', address: { city: 'London' } });
2519
+ * isLeaf(s.name); // true
2520
+ * isLeaf(s.address); // false — a substore
2521
+ */
2522
+ function isLeaf(value) {
2523
+ return isStore(value) && value[LEAF]?.() === true;
2524
+ }
2449
2525
  const IS_STORE = Symbol('@mmstack/primitives::store/IS_STORE');
2450
2526
  const SCOPE_PARENT = Symbol('@mmstack/primitives::store/SCOPE_PARENT');
2451
2527
  /**
@@ -2508,7 +2584,7 @@ function hasOwnKey(value, key) {
2508
2584
  * @internal
2509
2585
  * Makes an array store
2510
2586
  */
2511
- function toArrayStore(source, injector, vivify) {
2587
+ function toArrayStore(source, injector, vivify, noUnionLeaves = false) {
2512
2588
  if (isStore(source))
2513
2589
  return source;
2514
2590
  const isMutableSource = isMutable(source);
@@ -2618,9 +2694,10 @@ function toArrayStore(source, injector, vivify) {
2618
2694
  });
2619
2695
  const childSample = untracked(computation);
2620
2696
  const childVivify = resolveVivify(childSample, vivify);
2621
- const proxy = Array.isArray(childSample)
2622
- ? toArrayStore(computation, injector, childVivify)
2623
- : toStore(computation, injector, childVivify);
2697
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2698
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2699
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2700
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2624
2701
  const ref = new WeakRef(proxy);
2625
2702
  storeCache.set(idx, ref);
2626
2703
  PROXY_CLEANUP.register(proxy, { target, prop: idx }, ref);
@@ -2637,7 +2714,7 @@ function toArrayStore(source, injector, vivify) {
2637
2714
  * const state = store({ user: { name: 'John' } });
2638
2715
  * const nameSignal = state.user.name; // WritableSignal<string>
2639
2716
  */
2640
- function toStore(source, injector, vivify = false) {
2717
+ function toStore(source, injector, vivify = false, noUnionLeaves = false) {
2641
2718
  if (isStore(source))
2642
2719
  return source;
2643
2720
  if (!injector)
@@ -2678,7 +2755,7 @@ function toStore(source, injector, vivify = false) {
2678
2755
  return () => {
2679
2756
  if (!isWritableSource)
2680
2757
  return s;
2681
- return untracked(() => toStore(source.asReadonly(), injector, vivify));
2758
+ return untracked(() => toStore(source.asReadonly(), injector, vivify, noUnionLeaves));
2682
2759
  };
2683
2760
  if (prop === 'extend')
2684
2761
  return (seed) => scopedStore(s, seed, isMutableSource
@@ -2731,9 +2808,10 @@ function toStore(source, injector, vivify = false) {
2731
2808
  });
2732
2809
  const childSample = untracked(computation);
2733
2810
  const childVivify = resolveVivify(childSample, vivify);
2734
- const proxy = Array.isArray(childSample)
2735
- ? toArrayStore(computation, injector, childVivify)
2736
- : toStore(computation, injector, childVivify);
2811
+ const proxy = Array.isArray(childSample) && !isOpaque(childSample)
2812
+ ? toArrayStore(computation, injector, childVivify, noUnionLeaves)
2813
+ : toStore(computation, injector, childVivify, noUnionLeaves);
2814
+ markAsLeaf(proxy, computation, childVivify !== false, noUnionLeaves);
2737
2815
  const ref = new WeakRef(proxy);
2738
2816
  storeCache.set(prop, ref);
2739
2817
  PROXY_CLEANUP.register(proxy, { target, prop }, ref);
@@ -2826,14 +2904,14 @@ function scopedStore(parent, seed, kind, injector) {
2826
2904
  * @see {@link toStore}
2827
2905
  */
2828
2906
  function store(value, opt) {
2829
- return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false);
2907
+ return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2830
2908
  }
2831
2909
  /**
2832
2910
  * Creates a MutableSignalStore from a value.
2833
2911
  * @see {@link toStore}
2834
2912
  */
2835
2913
  function mutableStore(value, opt) {
2836
- return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false);
2914
+ return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
2837
2915
  }
2838
2916
 
2839
2917
  // Internal dummy store for server-side rendering
@@ -3319,5 +3397,5 @@ function withHistory(sourceOrValue, opt) {
3319
3397
  * Generated bundle index. Do not edit.
3320
3398
  */
3321
3399
 
3322
- 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 };
3400
+ 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 };
3323
3401
  //# sourceMappingURL=mmstack-primitives.mjs.map