@mmstack/primitives 20.5.9 → 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.
- package/fesm2022/mmstack-primitives.mjs +130 -40
- package/fesm2022/mmstack-primitives.mjs.map +1 -1
- package/index.d.ts +85 -23
- package/package.json +1 -1
|
@@ -2410,9 +2410,120 @@ function signalFromEvent(target, eventName, initial, projectOrOpt, maybeOpt) {
|
|
|
2410
2410
|
* has a `unique symbol` type, so the same symbol serves as both the property key written
|
|
2411
2411
|
* by {@link opaque} and the type-level brand carried by {@link Opaque}.
|
|
2412
2412
|
*/
|
|
2413
|
-
const OPAQUE = Symbol('
|
|
2414
|
-
|
|
2415
|
-
|
|
2413
|
+
const OPAQUE = Symbol('@mmstack/primitives::store/OPAQUE');
|
|
2414
|
+
/**
|
|
2415
|
+
* Marks a plain object as opaque so {@link store} treats it as an indivisible leaf
|
|
2416
|
+
* (returned whole, never deep-proxied) — the same way it treats a `Date` or `RegExp`.
|
|
2417
|
+
* The marker is a non-enumerable symbol, so it never appears in spreads or iteration.
|
|
2418
|
+
* Idempotent. Call before freezing (`defineProperty` fails on a frozen object).
|
|
2419
|
+
*
|
|
2420
|
+
* @example
|
|
2421
|
+
* const s = store({ config: opaque({ theme: 'dark', nested: { a: 1 } }) });
|
|
2422
|
+
* s.config(); // the whole object, not a child store
|
|
2423
|
+
* s.config.set(opaque({ theme: 'light', nested: { a: 2 } }));
|
|
2424
|
+
*/
|
|
2425
|
+
function opaque(value) {
|
|
2426
|
+
if (value[OPAQUE] !== true)
|
|
2427
|
+
Object.defineProperty(value, OPAQUE, { value: true, enumerable: false });
|
|
2428
|
+
return value;
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* Type guard companion to {@link opaque}: returns `true` when `value` carries the
|
|
2432
|
+
* {@link OPAQUE} brand, narrowing it to {@link Opaque}. This is the same check the
|
|
2433
|
+
* store uses to route opaque values to its leaf branch (alongside `Date`/`RegExp`).
|
|
2434
|
+
*
|
|
2435
|
+
* @internal Exposed for advanced/niche interop only — not part of the supported public
|
|
2436
|
+
* surface and may change without a major version bump. Reach for {@link opaque} for
|
|
2437
|
+
* normal usage.
|
|
2438
|
+
*
|
|
2439
|
+
* @example
|
|
2440
|
+
* if (isOpaque(value)) {
|
|
2441
|
+
* // value: Opaque<object> — `store` would treat it as an indivisible leaf
|
|
2442
|
+
* }
|
|
2443
|
+
*/
|
|
2444
|
+
function isOpaque(value) {
|
|
2445
|
+
return (typeof value === 'object' &&
|
|
2446
|
+
value !== null &&
|
|
2447
|
+
value[OPAQUE] === true);
|
|
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
|
+
}
|
|
2525
|
+
const IS_STORE = Symbol('@mmstack/primitives::store/IS_STORE');
|
|
2526
|
+
const SCOPE_PARENT = Symbol('@mmstack/primitives::store/SCOPE_PARENT');
|
|
2416
2527
|
/**
|
|
2417
2528
|
* @internal
|
|
2418
2529
|
* Test-only handle on the proxy cache (deliberately NOT re-exported from the public barrel).
|
|
@@ -2446,10 +2557,8 @@ function isStore(value) {
|
|
|
2446
2557
|
value[IS_STORE] === true);
|
|
2447
2558
|
}
|
|
2448
2559
|
function isRecord(value) {
|
|
2449
|
-
if (value === null || typeof value !== 'object')
|
|
2560
|
+
if (value === null || typeof value !== 'object' || isOpaque(value))
|
|
2450
2561
|
return false;
|
|
2451
|
-
if (value[OPAQUE] === true)
|
|
2452
|
-
return false; // opaque → leaf
|
|
2453
2562
|
const proto = Object.getPrototypeOf(value);
|
|
2454
2563
|
return proto === Object.prototype || proto === null;
|
|
2455
2564
|
}
|
|
@@ -2475,7 +2584,7 @@ function hasOwnKey(value, key) {
|
|
|
2475
2584
|
* @internal
|
|
2476
2585
|
* Makes an array store
|
|
2477
2586
|
*/
|
|
2478
|
-
function toArrayStore(source, injector, vivify) {
|
|
2587
|
+
function toArrayStore(source, injector, vivify, noUnionLeaves = false) {
|
|
2479
2588
|
if (isStore(source))
|
|
2480
2589
|
return source;
|
|
2481
2590
|
const isMutableSource = isMutable(source);
|
|
@@ -2585,9 +2694,10 @@ function toArrayStore(source, injector, vivify) {
|
|
|
2585
2694
|
});
|
|
2586
2695
|
const childSample = untracked(computation);
|
|
2587
2696
|
const childVivify = resolveVivify(childSample, vivify);
|
|
2588
|
-
const proxy = Array.isArray(childSample)
|
|
2589
|
-
? toArrayStore(computation, injector, childVivify)
|
|
2590
|
-
: 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);
|
|
2591
2701
|
const ref = new WeakRef(proxy);
|
|
2592
2702
|
storeCache.set(idx, ref);
|
|
2593
2703
|
PROXY_CLEANUP.register(proxy, { target, prop: idx }, ref);
|
|
@@ -2604,7 +2714,7 @@ function toArrayStore(source, injector, vivify) {
|
|
|
2604
2714
|
* const state = store({ user: { name: 'John' } });
|
|
2605
2715
|
* const nameSignal = state.user.name; // WritableSignal<string>
|
|
2606
2716
|
*/
|
|
2607
|
-
function toStore(source, injector, vivify = false) {
|
|
2717
|
+
function toStore(source, injector, vivify = false, noUnionLeaves = false) {
|
|
2608
2718
|
if (isStore(source))
|
|
2609
2719
|
return source;
|
|
2610
2720
|
if (!injector)
|
|
@@ -2645,7 +2755,7 @@ function toStore(source, injector, vivify = false) {
|
|
|
2645
2755
|
return () => {
|
|
2646
2756
|
if (!isWritableSource)
|
|
2647
2757
|
return s;
|
|
2648
|
-
return untracked(() => toStore(source.asReadonly(), injector, vivify));
|
|
2758
|
+
return untracked(() => toStore(source.asReadonly(), injector, vivify, noUnionLeaves));
|
|
2649
2759
|
};
|
|
2650
2760
|
if (prop === 'extend')
|
|
2651
2761
|
return (seed) => scopedStore(s, seed, isMutableSource
|
|
@@ -2698,9 +2808,10 @@ function toStore(source, injector, vivify = false) {
|
|
|
2698
2808
|
});
|
|
2699
2809
|
const childSample = untracked(computation);
|
|
2700
2810
|
const childVivify = resolveVivify(childSample, vivify);
|
|
2701
|
-
const proxy = Array.isArray(childSample)
|
|
2702
|
-
? toArrayStore(computation, injector, childVivify)
|
|
2703
|
-
: 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);
|
|
2704
2815
|
const ref = new WeakRef(proxy);
|
|
2705
2816
|
storeCache.set(prop, ref);
|
|
2706
2817
|
PROXY_CLEANUP.register(proxy, { target, prop }, ref);
|
|
@@ -2775,12 +2886,7 @@ function scopedStore(parent, seed, kind, injector) {
|
|
|
2775
2886
|
return hasOwnKey(localValue(), prop) || hasOwnKey(parentValue(), prop);
|
|
2776
2887
|
},
|
|
2777
2888
|
ownKeys() {
|
|
2778
|
-
return
|
|
2779
|
-
...new Set([
|
|
2780
|
-
...Reflect.ownKeys(parentValue()),
|
|
2781
|
-
...Reflect.ownKeys(localValue()),
|
|
2782
|
-
]),
|
|
2783
|
-
];
|
|
2889
|
+
return Reflect.ownKeys(untracked(view));
|
|
2784
2890
|
},
|
|
2785
2891
|
getOwnPropertyDescriptor(_, prop) {
|
|
2786
2892
|
if (hasOwnKey(localValue(), prop) || hasOwnKey(parentValue(), prop))
|
|
@@ -2798,30 +2904,14 @@ function scopedStore(parent, seed, kind, injector) {
|
|
|
2798
2904
|
* @see {@link toStore}
|
|
2799
2905
|
*/
|
|
2800
2906
|
function store(value, opt) {
|
|
2801
|
-
return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false);
|
|
2907
|
+
return toStore(signal(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
|
|
2802
2908
|
}
|
|
2803
2909
|
/**
|
|
2804
2910
|
* Creates a MutableSignalStore from a value.
|
|
2805
2911
|
* @see {@link toStore}
|
|
2806
2912
|
*/
|
|
2807
2913
|
function mutableStore(value, opt) {
|
|
2808
|
-
return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false);
|
|
2809
|
-
}
|
|
2810
|
-
/**
|
|
2811
|
-
* Marks a plain object as opaque so {@link store} treats it as an indivisible leaf
|
|
2812
|
-
* (returned whole, never deep-proxied) — the same way it treats a `Date` or `RegExp`.
|
|
2813
|
-
* The marker is a non-enumerable symbol, so it never appears in spreads or iteration.
|
|
2814
|
-
* Idempotent. Call before freezing (`defineProperty` fails on a frozen object).
|
|
2815
|
-
*
|
|
2816
|
-
* @example
|
|
2817
|
-
* const s = store({ config: opaque({ theme: 'dark', nested: { a: 1 } }) });
|
|
2818
|
-
* s.config(); // the whole object, not a child store
|
|
2819
|
-
* s.config.set(opaque({ theme: 'light', nested: { a: 2 } }));
|
|
2820
|
-
*/
|
|
2821
|
-
function opaque(value) {
|
|
2822
|
-
if (value[OPAQUE] !== true)
|
|
2823
|
-
Object.defineProperty(value, OPAQUE, { value: true, enumerable: false });
|
|
2824
|
-
return value;
|
|
2914
|
+
return toStore(mutable(value, opt), opt?.injector, opt?.vivify ?? false, opt?.noUnionLeaves ?? false);
|
|
2825
2915
|
}
|
|
2826
2916
|
|
|
2827
2917
|
// Internal dummy store for server-side rendering
|
|
@@ -3307,5 +3397,5 @@ function withHistory(sourceOrValue, opt) {
|
|
|
3307
3397
|
* Generated bundle index. Do not edit.
|
|
3308
3398
|
*/
|
|
3309
3399
|
|
|
3310
|
-
export { batteryStatus, chunked, clipboard, combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, filterWith, focusWithin, geolocation, idle, indexArray, isDerivation, isMutable, 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 };
|
|
3311
3401
|
//# sourceMappingURL=mmstack-primitives.mjs.map
|