@mmstack/primitives 21.0.0 → 21.0.1

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { untracked, signal, inject, DestroyRef, computed, PLATFORM_ID, isSignal, effect, ElementRef, linkedSignal, isDevMode, Injector, Injectable, runInInjectionContext } from '@angular/core';
2
+ import { computed, untracked, signal, inject, DestroyRef, isWritableSignal as isWritableSignal$1, isSignal, linkedSignal, isDevMode, Injector, effect, ElementRef, PLATFORM_ID, Injectable, runInInjectionContext } from '@angular/core';
3
3
  import { isPlatformServer } from '@angular/common';
4
4
  import { SIGNAL } from '@angular/core/primitives/signals';
5
5
 
@@ -31,9 +31,9 @@ import { SIGNAL } from '@angular/core/primitives/signals';
31
31
  *
32
32
  * writableSignal.set(5); // sets value of originalValue.a to 5 & triggers all signals
33
33
  */
34
- function toWritable(signal, set, update) {
35
- const internal = signal;
36
- internal.asReadonly = () => signal;
34
+ function toWritable(source, set, update, opt) {
35
+ const internal = (opt?.pure !== false ? computed(source) : source);
36
+ internal.asReadonly = () => source;
37
37
  internal.set = set;
38
38
  internal.update = update ?? ((updater) => set(updater(untracked(internal))));
39
39
  return internal;
@@ -289,117 +289,10 @@ function isDerivation(sig) {
289
289
  return 'from' in sig;
290
290
  }
291
291
 
292
- function observerSupported() {
293
- return typeof IntersectionObserver !== 'undefined';
294
- }
295
- /**
296
- * Creates a read-only signal that tracks the intersection status of a target DOM element
297
- * with the viewport or a specified root element, using the `IntersectionObserver` API.
298
- *
299
- * It can observe a static `ElementRef`/`Element` or a `Signal` that resolves to one,
300
- * allowing for dynamic targets.
301
- *
302
- * @param target The DOM element (or `ElementRef`, or a `Signal` resolving to one) to observe.
303
- * If the signal resolves to `null`, observation stops.
304
- * @param options Optional `IntersectionObserverInit` options (e.g., `root`, `rootMargin`, `threshold`)
305
- * and an optional `debugName`.
306
- * @returns A `Signal<IntersectionObserverEntry | undefined>`. It emits `undefined` initially,
307
- * on the server, or if the target is `null`. Otherwise, it emits the latest
308
- * `IntersectionObserverEntry`. Consumers can derive a boolean `isVisible` from
309
- * this entry's `isIntersecting` property.
310
- *
311
- * @example
312
- * ```ts
313
- * import { Component, effect, ElementRef, viewChild } from '@angular/core';
314
- * import { elementVisibility } from '@mmstack/primitives';
315
- * import { computed } from '@angular/core'; // For derived boolean
316
- *
317
- * @Component({
318
- * selector: 'app-lazy-image',
319
- * template: `
320
- * <div #imageContainer style="height: 200px; border: 1px dashed grey;">
321
- * @if (isVisible()) {
322
- * <img src="your-image-url.jpg" alt="Lazy loaded image" />
323
- * <p>Image is VISIBLE!</p>
324
- * } @else {
325
- * <p>Scroll down to see the image...</p>
326
- * }
327
- * </div>
328
- * `
329
- * })
330
- * export class LazyImageComponent {
331
- * readonly imageContainer = viewChild.required<ElementRef<HTMLDivElement>>('imageContainer');
332
- *
333
- * // Observe the element, get the full IntersectionObserverEntry
334
- * readonly intersectionEntry = elementVisibility(this.imageContainer);
335
- *
336
- * // Derive a simple boolean for visibility
337
- * readonly isVisible = computed(() => this.intersectionEntry()?.isIntersecting ?? false);
338
- *
339
- * constructor() {
340
- * effect(() => {
341
- * console.log('Intersection Entry:', this.intersectionEntry());
342
- * console.log('Is Visible:', this.isVisible());
343
- * });
344
- * }
345
- * }
346
- * ```
347
- */
348
- function elementVisibility(target = inject(ElementRef), opt) {
349
- if (isPlatformServer(inject(PLATFORM_ID)) || !observerSupported()) {
350
- const base = computed(() => undefined, {
351
- debugName: opt?.debugName,
352
- });
353
- base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
354
- return base;
355
- }
356
- const state = signal(undefined, {
357
- debugName: opt?.debugName,
358
- equal: (a, b) => {
359
- if (!a && !b)
360
- return true;
361
- if (!a || !b)
362
- return false;
363
- return (a.target === b.target &&
364
- a.isIntersecting === b.isIntersecting &&
365
- a.intersectionRatio === b.intersectionRatio &&
366
- a.boundingClientRect.top === b.boundingClientRect.top &&
367
- a.boundingClientRect.left === b.boundingClientRect.left &&
368
- a.boundingClientRect.width === b.boundingClientRect.width &&
369
- a.boundingClientRect.height === b.boundingClientRect.height);
370
- },
371
- });
372
- const targetSignal = isSignal(target) ? target : computed(() => target);
373
- effect((cleanup) => {
374
- const el = targetSignal();
375
- if (!el)
376
- return state.set(undefined);
377
- let observer = null;
378
- observer = new IntersectionObserver(([entry]) => state.set(entry), opt);
379
- observer.observe(el instanceof ElementRef ? el.nativeElement : el);
380
- cleanup(() => {
381
- observer?.disconnect();
382
- });
383
- });
384
- const base = state.asReadonly();
385
- base.visible = computed(() => {
386
- const s = state();
387
- if (!s)
388
- return false;
389
- return s.isIntersecting;
390
- }, ...(ngDevMode ? [{ debugName: "visible" }] : []));
391
- return base;
292
+ function isWritableSignal(value) {
293
+ return isWritableSignal$1(value);
392
294
  }
393
295
 
394
- /**
395
- * @internal
396
- * Checks if a signal is a WritableSignal.
397
- * @param sig The signal to check.
398
- */
399
- function isWritable(sig) {
400
- // We just need to check for the presence of a 'set' method.
401
- return 'set' in sig;
402
- }
403
296
  /**
404
297
  * @internal
405
298
  * Creates a setter function for a source signal of type `Signal<T[]>` or a function returning `T[]`.
@@ -407,31 +300,45 @@ function isWritable(sig) {
407
300
  * @returns
408
301
  */
409
302
  function createSetter(source) {
410
- if (!isWritable(source))
303
+ if (!isWritableSignal(source))
411
304
  return () => {
412
305
  // noop;
413
306
  };
414
307
  if (isMutable(source))
415
308
  return (value, index) => {
416
- source.inline((arr) => {
309
+ source.mutate((arr) => {
417
310
  arr[index] = value;
311
+ return arr;
418
312
  });
419
313
  };
420
314
  return (value, index) => {
421
315
  source.update((arr) => arr.map((v, i) => (i === index ? value : v)));
422
316
  };
423
317
  }
424
- function mapArray(source, map, options) {
318
+
319
+ /**
320
+ * Helper to create the derived signal for a specific index.
321
+ * Extracts the cast logic to keep the main loop clean.
322
+ */
323
+ function createItemSignal(source, index, setter, opt) {
324
+ return derived(
325
+ // We cast to any/Mutable to satisfy the overload signature,
326
+ // but 'derived' internally checks isMutable() for safety.
327
+ source, {
328
+ from: (src) => src[index],
329
+ onChange: (value) => setter(value, index),
330
+ }, opt);
331
+ }
332
+ function indexArray(source, map, opt = {}) {
425
333
  const data = isSignal(source) ? source : computed(source);
426
334
  const len = computed(() => data().length, ...(ngDevMode ? [{ debugName: "len" }] : []));
427
335
  const setter = createSetter(data);
428
- const opt = { ...options };
429
- const writableData = isWritable(data)
336
+ const writableData = isWritableSignal(data)
430
337
  ? data
431
338
  : toWritable(data, () => {
432
339
  // noop
433
340
  });
434
- if (isWritable(data) && isMutable(data) && !opt.equal) {
341
+ if (isWritableSignal(data) && isMutable(data) && !opt.equal) {
435
342
  opt.equal = (a, b) => {
436
343
  if (a !== b)
437
344
  return false; // actually check primitives and references
@@ -442,41 +349,92 @@ function mapArray(source, map, options) {
442
349
  source: () => len(),
443
350
  computation: (len, prev) => {
444
351
  if (!prev)
445
- return Array.from({ length: len }, (_, i) => {
446
- const derivation = derived(writableData, // typcase to largest type
447
- {
448
- from: (src) => src[i],
449
- onChange: (value) => setter(value, i),
450
- }, opt);
451
- return map(derivation, i);
452
- });
352
+ return Array.from({ length: len }, (_, i) => map(createItemSignal(writableData, i, setter, opt), i));
453
353
  if (len === prev.value.length)
454
354
  return prev.value;
455
355
  if (len < prev.value.length) {
456
- const slice = prev.value.slice(0, len);
457
- if (opt.onDestroy) {
458
- for (let i = len; i < prev.value.length; i++) {
459
- opt.onDestroy?.(prev.value[i]);
460
- }
461
- }
462
- return slice;
463
- }
464
- else {
465
- const next = [...prev.value];
466
- for (let i = prev.value.length; i < len; i++) {
467
- const derivation = derived(writableData, // typcase to largest type
468
- {
469
- from: (src) => src[i],
470
- onChange: (value) => setter(value, i),
471
- }, opt);
472
- next[i] = map(derivation, i);
473
- }
474
- return next;
356
+ if (opt.onDestroy)
357
+ prev.value.forEach((v) => opt.onDestroy?.(v));
358
+ return prev.value.slice(0, len);
475
359
  }
360
+ const next = prev.value.slice();
361
+ for (let i = prev.value.length; i < len; i++)
362
+ next[i] = map(createItemSignal(writableData, i, setter, opt), i);
363
+ return next;
476
364
  },
477
365
  equal: (a, b) => a.length === b.length,
478
366
  });
479
367
  }
368
+ /**
369
+ * @deprecated use indexArray instead
370
+ */
371
+ const mapArray = indexArray;
372
+
373
+ function keyArray(source, keyFn, map, opt = {}) {
374
+ const data = isSignal(source) ? source : computed(source);
375
+ const setter = createSetter(data);
376
+ const writableData = isWritableSignal(data)
377
+ ? data
378
+ : toWritable(data, () => {
379
+ // noop
380
+ });
381
+ if (isWritableSignal(data) && isMutable(data) && !opt.equal) {
382
+ opt.equal = (a, b) => {
383
+ if (a !== b)
384
+ return false;
385
+ return false; // opt out for same refs
386
+ };
387
+ }
388
+ let freeMap = new Map();
389
+ const createRecord = (i) => {
390
+ const idx = signal(i, ...(ngDevMode ? [{ debugName: "idx" }] : []));
391
+ const value = derived(writableData, {
392
+ from: (v) => v[idx()],
393
+ onChange: (next) => setter(next, untracked(idx)),
394
+ }, opt);
395
+ return {
396
+ source: {
397
+ idx,
398
+ value,
399
+ },
400
+ computation: map(value, idx),
401
+ };
402
+ };
403
+ const internal = linkedSignal({ ...(ngDevMode ? { debugName: "internal" } : {}), source: () => writableData(),
404
+ computation: (src, prev) => {
405
+ const prevCache = prev?.value.cache ?? new Map();
406
+ const nextCache = freeMap;
407
+ const nextValues = [];
408
+ let changed = false;
409
+ for (let i = 0; i < src.length; i++) {
410
+ const k = untracked(() => keyFn(src[i]));
411
+ let record = prevCache.get(k);
412
+ if (!record) {
413
+ changed = true;
414
+ record = createRecord(i);
415
+ }
416
+ prevCache.delete(k);
417
+ nextCache.set(k, record);
418
+ nextValues.push(record.computation);
419
+ if (untracked(record.source.idx) !== i) {
420
+ record.source.idx.set(i);
421
+ changed = true;
422
+ }
423
+ }
424
+ if (prevCache.size > 0)
425
+ changed = true;
426
+ if (opt.onDestroy)
427
+ prevCache.values().forEach((v) => opt.onDestroy?.(v.computation));
428
+ // clear for next run
429
+ prevCache.clear();
430
+ freeMap = prevCache;
431
+ return {
432
+ cache: nextCache,
433
+ values: changed ? nextValues : (prev?.value.values ?? []),
434
+ };
435
+ } });
436
+ return computed(() => internal().values);
437
+ }
480
438
 
481
439
  const frameStack = [];
482
440
  function current() {
@@ -698,6 +656,205 @@ function piped(initial, opt) {
698
656
  return pipeable(signal(initial, opt));
699
657
  }
700
658
 
659
+ function observerSupported$1() {
660
+ return typeof ResizeObserver !== 'undefined';
661
+ }
662
+ /**
663
+ * Creates a read-only signal that tracks the size of a target DOM element.
664
+ *
665
+ * By default, it observes the `border-box` size to align with `getBoundingClientRect()`,
666
+ * which is used to provide a synchronous initial value if possible.
667
+ *
668
+ * @param target The DOM element (or `ElementRef`, or a `Signal` resolving to one) to observe.
669
+ * @param options Optional configuration including `box` (defaults to 'border-box') and `debugName`.
670
+ * @returns A `Signal<ElementSize | undefined>`.
671
+ *
672
+ * @example
673
+ * ```ts
674
+ * const size = elementSize(elementRef);
675
+ * effect(() => {
676
+ * console.log('Size:', size()?.width, size()?.height);
677
+ * });
678
+ * ```
679
+ */
680
+ function elementSize(target = inject(ElementRef), opt) {
681
+ const getElement = () => {
682
+ if (isSignal(target)) {
683
+ try {
684
+ const val = target();
685
+ return val instanceof ElementRef ? val.nativeElement : val;
686
+ }
687
+ catch {
688
+ return null;
689
+ }
690
+ }
691
+ return target instanceof ElementRef ? target.nativeElement : target;
692
+ };
693
+ const resolveInitialValue = () => {
694
+ if (!observerSupported$1())
695
+ return undefined;
696
+ const el = getElement();
697
+ if (el && el.getBoundingClientRect) {
698
+ const rect = el.getBoundingClientRect();
699
+ return { width: rect.width, height: rect.height };
700
+ }
701
+ return undefined;
702
+ };
703
+ if (isPlatformServer(inject(PLATFORM_ID))) {
704
+ return computed(() => untracked(resolveInitialValue), {
705
+ debugName: opt?.debugName,
706
+ });
707
+ }
708
+ const state = signal(untracked(resolveInitialValue), {
709
+ debugName: opt?.debugName,
710
+ equal: (a, b) => a?.width === b?.width && a?.height === b?.height,
711
+ });
712
+ const targetSignal = isSignal(target) ? target : computed(() => target);
713
+ effect((cleanup) => {
714
+ const el = targetSignal();
715
+ if (el) {
716
+ const nativeEl = el instanceof ElementRef ? el.nativeElement : el;
717
+ const rect = nativeEl.getBoundingClientRect();
718
+ untracked(() => state.set({ width: rect.width, height: rect.height }));
719
+ }
720
+ else {
721
+ untracked(() => state.set(undefined));
722
+ return;
723
+ }
724
+ if (!observerSupported$1())
725
+ return;
726
+ let observer = null;
727
+ observer = new ResizeObserver(([entry]) => {
728
+ let width = 0;
729
+ let height = 0;
730
+ const boxOption = opt?.box ?? 'border-box';
731
+ if (boxOption === 'border-box' && entry.borderBoxSize?.length > 0) {
732
+ const size = entry.borderBoxSize[0];
733
+ width = size.inlineSize;
734
+ height = size.blockSize;
735
+ }
736
+ else if (boxOption === 'content-box' && entry.contentBoxSize?.length > 0) {
737
+ width = entry.contentBoxSize[0].inlineSize;
738
+ height = entry.contentBoxSize[0].blockSize;
739
+ }
740
+ else {
741
+ width = entry.contentRect.width;
742
+ height = entry.contentRect.height;
743
+ }
744
+ state.set({ width, height });
745
+ });
746
+ observer.observe(el instanceof ElementRef ? el.nativeElement : el, {
747
+ box: opt?.box ?? 'border-box',
748
+ });
749
+ cleanup(() => {
750
+ observer?.disconnect();
751
+ });
752
+ });
753
+ return state.asReadonly();
754
+ }
755
+
756
+ function observerSupported() {
757
+ return typeof IntersectionObserver !== 'undefined';
758
+ }
759
+ /**
760
+ * Creates a read-only signal that tracks the intersection status of a target DOM element
761
+ * with the viewport or a specified root element, using the `IntersectionObserver` API.
762
+ *
763
+ * It can observe a static `ElementRef`/`Element` or a `Signal` that resolves to one,
764
+ * allowing for dynamic targets.
765
+ *
766
+ * @param target The DOM element (or `ElementRef`, or a `Signal` resolving to one) to observe.
767
+ * If the signal resolves to `null`, observation stops.
768
+ * @param options Optional `IntersectionObserverInit` options (e.g., `root`, `rootMargin`, `threshold`)
769
+ * and an optional `debugName`.
770
+ * @returns A `Signal<IntersectionObserverEntry | undefined>`. It emits `undefined` initially,
771
+ * on the server, or if the target is `null`. Otherwise, it emits the latest
772
+ * `IntersectionObserverEntry`. Consumers can derive a boolean `isVisible` from
773
+ * this entry's `isIntersecting` property.
774
+ *
775
+ * @example
776
+ * ```ts
777
+ * import { Component, effect, ElementRef, viewChild } from '@angular/core';
778
+ * import { elementVisibility } from '@mmstack/primitives';
779
+ * import { computed } from '@angular/core'; // For derived boolean
780
+ *
781
+ * @Component({
782
+ * selector: 'app-lazy-image',
783
+ * template: `
784
+ * <div #imageContainer style="height: 200px; border: 1px dashed grey;">
785
+ * @if (isVisible()) {
786
+ * <img src="your-image-url.jpg" alt="Lazy loaded image" />
787
+ * <p>Image is VISIBLE!</p>
788
+ * } @else {
789
+ * <p>Scroll down to see the image...</p>
790
+ * }
791
+ * </div>
792
+ * `
793
+ * })
794
+ * export class LazyImageComponent {
795
+ * readonly imageContainer = viewChild.required<ElementRef<HTMLDivElement>>('imageContainer');
796
+ *
797
+ * // Observe the element, get the full IntersectionObserverEntry
798
+ * readonly intersectionEntry = elementVisibility(this.imageContainer);
799
+ *
800
+ * // Derive a simple boolean for visibility
801
+ * readonly isVisible = computed(() => this.intersectionEntry()?.isIntersecting ?? false);
802
+ *
803
+ * constructor() {
804
+ * effect(() => {
805
+ * console.log('Intersection Entry:', this.intersectionEntry());
806
+ * console.log('Is Visible:', this.isVisible());
807
+ * });
808
+ * }
809
+ * }
810
+ * ```
811
+ */
812
+ function elementVisibility(target = inject(ElementRef), opt) {
813
+ if (isPlatformServer(inject(PLATFORM_ID)) || !observerSupported()) {
814
+ const base = computed(() => undefined, {
815
+ debugName: opt?.debugName,
816
+ });
817
+ base.visible = computed(() => false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
818
+ return base;
819
+ }
820
+ const state = signal(undefined, {
821
+ debugName: opt?.debugName,
822
+ equal: (a, b) => {
823
+ if (!a && !b)
824
+ return true;
825
+ if (!a || !b)
826
+ return false;
827
+ return (a.target === b.target &&
828
+ a.isIntersecting === b.isIntersecting &&
829
+ a.intersectionRatio === b.intersectionRatio &&
830
+ a.boundingClientRect.top === b.boundingClientRect.top &&
831
+ a.boundingClientRect.left === b.boundingClientRect.left &&
832
+ a.boundingClientRect.width === b.boundingClientRect.width &&
833
+ a.boundingClientRect.height === b.boundingClientRect.height);
834
+ },
835
+ });
836
+ const targetSignal = isSignal(target) ? target : computed(() => target);
837
+ effect((cleanup) => {
838
+ const el = targetSignal();
839
+ if (!el)
840
+ return state.set(undefined);
841
+ let observer = null;
842
+ observer = new IntersectionObserver(([entry]) => state.set(entry), opt);
843
+ observer.observe(el instanceof ElementRef ? el.nativeElement : el);
844
+ cleanup(() => {
845
+ observer?.disconnect();
846
+ });
847
+ });
848
+ const base = state.asReadonly();
849
+ base.visible = computed(() => {
850
+ const s = state();
851
+ if (!s)
852
+ return false;
853
+ return s.isIntersecting;
854
+ }, ...(ngDevMode ? [{ debugName: "visible" }] : []));
855
+ return base;
856
+ }
857
+
701
858
  /**
702
859
  * Creates a read-only signal that reactively tracks whether a CSS media query
703
860
  * string currently matches.
@@ -1004,9 +1161,7 @@ function networkStatus(debugName = 'networkStatus') {
1004
1161
  sig.since = computed(() => serverDate, ...(ngDevMode ? [{ debugName: "since" }] : []));
1005
1162
  return sig;
1006
1163
  }
1007
- const state = signal(navigator.onLine, ...(ngDevMode ? [{ debugName: "state", debugName }] : [{
1008
- debugName,
1009
- }]));
1164
+ const state = signal(navigator.onLine, { ...(ngDevMode ? { debugName: "state" } : {}), debugName });
1010
1165
  const since = signal(new Date(), ...(ngDevMode ? [{ debugName: "since" }] : []));
1011
1166
  const goOnline = () => {
1012
1167
  state.set(true);
@@ -1067,7 +1222,7 @@ function pageVisibility(debugName = 'pageVisibility') {
1067
1222
  if (isPlatformServer(inject(PLATFORM_ID))) {
1068
1223
  return computed(() => 'visible', { debugName });
1069
1224
  }
1070
- const visibility = signal(document.visibilityState, ...(ngDevMode ? [{ debugName: "visibility", debugName }] : [{ debugName }]));
1225
+ const visibility = signal(document.visibilityState, { ...(ngDevMode ? { debugName: "visibility" } : {}), debugName });
1071
1226
  const onVisibilityChange = () => visibility.set(document.visibilityState);
1072
1227
  document.addEventListener('visibilitychange', onVisibilityChange);
1073
1228
  inject(DestroyRef).onDestroy(() => document.removeEventListener('visibilitychange', onVisibilityChange));
@@ -1250,18 +1405,38 @@ function sensor(type, options) {
1250
1405
  return networkStatus(options?.debugName);
1251
1406
  case 'pageVisibility':
1252
1407
  return pageVisibility(options?.debugName);
1408
+ case 'darkMode':
1253
1409
  case 'dark-mode':
1254
1410
  return prefersDarkMode(options?.debugName);
1411
+ case 'reducedMotion':
1255
1412
  case 'reduced-motion':
1256
1413
  return prefersReducedMotion(options?.debugName);
1414
+ case 'mediaQuery': {
1415
+ const opt = options;
1416
+ return mediaQuery(opt.query, opt.debugName);
1417
+ }
1257
1418
  case 'windowSize':
1258
1419
  return windowSize(options);
1259
1420
  case 'scrollPosition':
1260
1421
  return scrollPosition(options);
1422
+ case 'elementVisibility': {
1423
+ const opt = options;
1424
+ return elementVisibility(opt.target, opt);
1425
+ }
1426
+ case 'elementSize': {
1427
+ const opt = options;
1428
+ return elementSize(opt.target, opt);
1429
+ }
1261
1430
  default:
1262
1431
  throw new Error(`Unknown sensor type: ${type}`);
1263
1432
  }
1264
1433
  }
1434
+ function sensors(track, opt) {
1435
+ return track.reduce((result, key) => {
1436
+ result[key] = sensor(key, opt?.[key]);
1437
+ return result;
1438
+ }, {});
1439
+ }
1265
1440
 
1266
1441
  // Internal dummy store for server-side rendering
1267
1442
  const noopStore = {
@@ -1363,23 +1538,14 @@ function stored(fallback, { key, store: providedStore, serialize = JSON.stringif
1363
1538
  equal,
1364
1539
  };
1365
1540
  const initialKey = untracked(keySig);
1366
- const internal = signal(getValue(initialKey), ...(ngDevMode ? [{ debugName: "internal", ...opt,
1367
- equal: (a, b) => {
1368
- if (a === null && b === null)
1369
- return true;
1370
- if (a === null || b === null)
1371
- return false;
1372
- return equal(a, b);
1373
- } }] : [{
1374
- ...opt,
1375
- equal: (a, b) => {
1376
- if (a === null && b === null)
1377
- return true;
1378
- if (a === null || b === null)
1379
- return false;
1380
- return equal(a, b);
1381
- },
1382
- }]));
1541
+ const internal = signal(getValue(initialKey), { ...(ngDevMode ? { debugName: "internal" } : {}), ...opt,
1542
+ equal: (a, b) => {
1543
+ if (a === null && b === null)
1544
+ return true;
1545
+ if (a === null || b === null)
1546
+ return false;
1547
+ return equal(a, b);
1548
+ } });
1383
1549
  let prevKey = initialKey;
1384
1550
  if (onKeyChange === 'store') {
1385
1551
  effect(() => {
@@ -1457,10 +1623,10 @@ class MessageBus {
1457
1623
  this.channel.removeEventListener('message', listener);
1458
1624
  this.listeners.delete(id);
1459
1625
  }
1460
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MessageBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1461
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MessageBus, providedIn: 'root' });
1626
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MessageBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1627
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MessageBus, providedIn: 'root' });
1462
1628
  }
1463
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: MessageBus, decorators: [{
1629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MessageBus, decorators: [{
1464
1630
  type: Injectable,
1465
1631
  args: [{
1466
1632
  providedIn: 'root',
@@ -1743,5 +1909,5 @@ function withHistory(source, opt) {
1743
1909
  * Generated bundle index. Do not edit.
1744
1910
  */
1745
1911
 
1746
- export { combineWith, debounce, debounced, derived, distinct, elementVisibility, filter, isDerivation, isMutable, map, mapArray, mediaQuery, mousePosition, mutable, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toWritable, until, windowSize, withHistory };
1912
+ export { combineWith, debounce, debounced, derived, distinct, elementSize, elementVisibility, filter, indexArray, isDerivation, isMutable, keyArray, map, mapArray, mediaQuery, mousePosition, mutable, nestedEffect, networkStatus, pageVisibility, pipeable, piped, prefersDarkMode, prefersReducedMotion, scrollPosition, select, sensor, sensors, stored, tabSync, tap, throttle, throttled, toFakeDerivation, toFakeSignalDerivation, toWritable, until, windowSize, withHistory };
1747
1913
  //# sourceMappingURL=mmstack-primitives.mjs.map