@mmstack/primitives 21.8.1 → 21.8.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.
@@ -1985,6 +1985,29 @@ function indexArray(source, map, opt = {}) {
1985
1985
  */
1986
1986
  const mapArray = indexArray;
1987
1987
 
1988
+ function effectiveKeys(arr, getKey, report) {
1989
+ const out = new Array(arr.length);
1990
+ const counts = new Map();
1991
+ const firstOrder = [];
1992
+ let duped = null;
1993
+ for (let i = 0; i < arr.length; i++) {
1994
+ const base = String(getKey(arr[i]));
1995
+ const seen = counts.get(base) ?? 0;
1996
+ if (seen === 0) {
1997
+ firstOrder.push(base);
1998
+ }
1999
+ else {
2000
+ (duped ??= new Set()).add(base);
2001
+ }
2002
+ out[i] = seen === 0 ? base : base + '#' + seen;
2003
+ counts.set(base, seen + 1);
2004
+ }
2005
+ if (report && duped) {
2006
+ const bases = firstOrder.filter((b) => duped.has(b));
2007
+ untracked(() => report(bases));
2008
+ }
2009
+ return out;
2010
+ }
1988
2011
  /**
1989
2012
  * Reactively maps items from a source array to a new array by value (identity).
1990
2013
  *
@@ -2003,6 +2026,8 @@ const mapArray = indexArray;
2003
2026
  * - `onDestroy`: A callback invoked when a mapped item is removed from the array.
2004
2027
  * - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
2005
2028
  * when item references change but conceptual identity is preserved.
2029
+ * - `duplicateKeys`: Opt-in policy for handling duplicate keys. When omitted,
2030
+ * behavior is unchanged (duplicates collapse). See {@link DuplicateKeyPolicy}.
2006
2031
  * @returns A `Signal<U[]>` containing the mapped array.
2007
2032
  *
2008
2033
  * @example
@@ -2037,9 +2062,11 @@ function keyArray(source, mapFn, options = {}) {
2037
2062
  const tempIndexes = [];
2038
2063
  const newIndicesNext = [];
2039
2064
  const newIndexesCache = new Array();
2065
+ const dup = options.duplicateKeys;
2040
2066
  return computed(() => {
2041
2067
  const newItems = sourceSignal() || [];
2042
2068
  return untracked(() => {
2069
+ const newKeys = dup ? effectiveKeys(newItems, getKey, dup.report) : null;
2043
2070
  let i;
2044
2071
  let j;
2045
2072
  const newLen = newItems.length;
@@ -2079,26 +2106,29 @@ function keyArray(source, mapFn, options = {}) {
2079
2106
  temp.length = 0;
2080
2107
  tempIndexes.length = 0;
2081
2108
  newIndicesNext.length = 0;
2082
- for (start = 0, end = Math.min(len, newLen); start < end && getKey(items[start]) === getKey(newItems[start]); start++) {
2109
+ const oldKeys = dup ? effectiveKeys(items, getKey) : null;
2110
+ const kNew = newKeys
2111
+ ? (idx) => newKeys[idx]
2112
+ : (idx) => getKey(newItems[idx]);
2113
+ const kOld = oldKeys
2114
+ ? (idx) => oldKeys[idx]
2115
+ : (idx) => getKey(items[idx]);
2116
+ for (start = 0, end = Math.min(len, newLen); start < end && kOld(start) === kNew(start); start++) {
2083
2117
  newMapped[start] = mapped[start];
2084
2118
  newIndexes[start] = indexes[start];
2085
2119
  }
2086
- for (end = len - 1, newEnd = newLen - 1; end >= start &&
2087
- newEnd >= start &&
2088
- getKey(items[end]) === getKey(newItems[newEnd]); end--, newEnd--) {
2120
+ for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && kOld(end) === kNew(newEnd); end--, newEnd--) {
2089
2121
  temp[newEnd] = mapped[end];
2090
2122
  tempIndexes[newEnd] = indexes[end];
2091
2123
  }
2092
2124
  for (j = newEnd; j >= start; j--) {
2093
- item = newItems[j];
2094
- key = getKey(item);
2125
+ key = kNew(j);
2095
2126
  i = newIndices.get(key) ?? -1;
2096
2127
  newIndicesNext[j] = i === undefined ? -1 : i;
2097
2128
  newIndices.set(key, j);
2098
2129
  }
2099
2130
  for (i = start; i <= end; i++) {
2100
- item = items[i];
2101
- key = getKey(item);
2131
+ key = kOld(i);
2102
2132
  j = newIndices.get(key) ?? -1;
2103
2133
  if (j !== undefined && j !== -1) {
2104
2134
  temp[j] = mapped[i];
@@ -4212,7 +4242,10 @@ const PROXY_CLEANUP_TOKEN = new InjectionToken('@mmstack/primitives:store-proxy-
4212
4242
  providedIn: 'root',
4213
4243
  factory: () => {
4214
4244
  const cache = inject(PROXY_CACHE_TOKEN);
4215
- return new FinalizationRegistry(({ target, prop }) => {
4245
+ return new FinalizationRegistry(({ targetRef, prop }) => {
4246
+ const target = targetRef.deref();
4247
+ if (!target)
4248
+ return;
4216
4249
  const store = cache.get(target);
4217
4250
  if (store)
4218
4251
  store.delete(prop);
@@ -4479,7 +4512,7 @@ function getCachedChild(target, prop, build, cache, cleanupRegistry) {
4479
4512
  const proxy = build();
4480
4513
  const ref = new WeakRef(proxy);
4481
4514
  storeCache.set(prop, ref);
4482
- cleanupRegistry.register(proxy, { target, prop }, ref);
4515
+ cleanupRegistry.register(proxy, { targetRef: new WeakRef(target), prop }, ref);
4483
4516
  return proxy;
4484
4517
  }
4485
4518
  /**
@@ -4834,7 +4867,10 @@ function mutableStore(value, opt) {
4834
4867
  */
4835
4868
  function createStoreContext() {
4836
4869
  const cache = new WeakMap();
4837
- const registry = new FinalizationRegistry(({ target, prop }) => {
4870
+ const registry = new FinalizationRegistry(({ targetRef, prop }) => {
4871
+ const target = targetRef.deref();
4872
+ if (!target)
4873
+ return;
4838
4874
  const entry = cache.get(target);
4839
4875
  if (entry)
4840
4876
  entry.delete(prop);