@mmstack/primitives 22.8.1 → 22.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.
@@ -2005,6 +2005,29 @@ function indexArray(source, map, opt = {}) {
2005
2005
  */
2006
2006
  const mapArray = indexArray;
2007
2007
 
2008
+ function effectiveKeys(arr, getKey, report) {
2009
+ const out = new Array(arr.length);
2010
+ const counts = new Map();
2011
+ const firstOrder = [];
2012
+ let duped = null;
2013
+ for (let i = 0; i < arr.length; i++) {
2014
+ const base = String(getKey(arr[i]));
2015
+ const seen = counts.get(base) ?? 0;
2016
+ if (seen === 0) {
2017
+ firstOrder.push(base);
2018
+ }
2019
+ else {
2020
+ (duped ??= new Set()).add(base);
2021
+ }
2022
+ out[i] = seen === 0 ? base : base + '#' + seen;
2023
+ counts.set(base, seen + 1);
2024
+ }
2025
+ if (report && duped) {
2026
+ const bases = firstOrder.filter((b) => duped.has(b));
2027
+ untracked(() => report(bases));
2028
+ }
2029
+ return out;
2030
+ }
2008
2031
  /**
2009
2032
  * Reactively maps items from a source array to a new array by value (identity).
2010
2033
  *
@@ -2023,6 +2046,8 @@ const mapArray = indexArray;
2023
2046
  * - `onDestroy`: A callback invoked when a mapped item is removed from the array.
2024
2047
  * - `key`: A custom key extractor for identity matching (e.g. `(item) => item.id`)
2025
2048
  * when item references change but conceptual identity is preserved.
2049
+ * - `duplicateKeys`: Opt-in policy for handling duplicate keys. When omitted,
2050
+ * behavior is unchanged (duplicates collapse). See {@link DuplicateKeyPolicy}.
2026
2051
  * @returns A `Signal<U[]>` containing the mapped array.
2027
2052
  *
2028
2053
  * @example
@@ -2057,9 +2082,11 @@ function keyArray(source, mapFn, options = {}) {
2057
2082
  const tempIndexes = [];
2058
2083
  const newIndicesNext = [];
2059
2084
  const newIndexesCache = new Array();
2085
+ const dup = options.duplicateKeys;
2060
2086
  return computed(() => {
2061
2087
  const newItems = sourceSignal() || [];
2062
2088
  return untracked(() => {
2089
+ const newKeys = dup ? effectiveKeys(newItems, getKey, dup.report) : null;
2063
2090
  let i;
2064
2091
  let j;
2065
2092
  const newLen = newItems.length;
@@ -2100,26 +2127,29 @@ function keyArray(source, mapFn, options = {}) {
2100
2127
  temp.length = 0;
2101
2128
  tempIndexes.length = 0;
2102
2129
  newIndicesNext.length = 0;
2103
- for (start = 0, end = Math.min(len, newLen); start < end && getKey(items[start]) === getKey(newItems[start]); start++) {
2130
+ const oldKeys = dup ? effectiveKeys(items, getKey) : null;
2131
+ const kNew = newKeys
2132
+ ? (idx) => newKeys[idx]
2133
+ : (idx) => getKey(newItems[idx]);
2134
+ const kOld = oldKeys
2135
+ ? (idx) => oldKeys[idx]
2136
+ : (idx) => getKey(items[idx]);
2137
+ for (start = 0, end = Math.min(len, newLen); start < end && kOld(start) === kNew(start); start++) {
2104
2138
  newMapped[start] = mapped[start];
2105
2139
  newIndexes[start] = indexes[start];
2106
2140
  }
2107
- for (end = len - 1, newEnd = newLen - 1; end >= start &&
2108
- newEnd >= start &&
2109
- getKey(items[end]) === getKey(newItems[newEnd]); end--, newEnd--) {
2141
+ for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && kOld(end) === kNew(newEnd); end--, newEnd--) {
2110
2142
  temp[newEnd] = mapped[end];
2111
2143
  tempIndexes[newEnd] = indexes[end];
2112
2144
  }
2113
2145
  for (j = newEnd; j >= start; j--) {
2114
- item = newItems[j];
2115
- key = getKey(item);
2146
+ key = kNew(j);
2116
2147
  i = newIndices.get(key) ?? -1;
2117
2148
  newIndicesNext[j] = i === undefined ? -1 : i;
2118
2149
  newIndices.set(key, j);
2119
2150
  }
2120
2151
  for (i = start; i <= end; i++) {
2121
- item = items[i];
2122
- key = getKey(item);
2152
+ key = kOld(i);
2123
2153
  j = newIndices.get(key) ?? -1;
2124
2154
  if (j !== undefined && j !== -1) {
2125
2155
  temp[j] = mapped[i];
@@ -4249,7 +4279,10 @@ const PROXY_CLEANUP_TOKEN = new InjectionToken('@mmstack/primitives:store-proxy-
4249
4279
  providedIn: 'root',
4250
4280
  factory: () => {
4251
4281
  const cache = inject(PROXY_CACHE_TOKEN);
4252
- return new FinalizationRegistry(({ target, prop }) => {
4282
+ return new FinalizationRegistry(({ targetRef, prop }) => {
4283
+ const target = targetRef.deref();
4284
+ if (!target)
4285
+ return;
4253
4286
  const store = cache.get(target);
4254
4287
  if (store)
4255
4288
  store.delete(prop);
@@ -4517,7 +4550,7 @@ function getCachedChild(target, prop, build, cache, cleanupRegistry) {
4517
4550
  const proxy = build();
4518
4551
  const ref = new WeakRef(proxy);
4519
4552
  storeCache.set(prop, ref);
4520
- cleanupRegistry.register(proxy, { target, prop }, ref);
4553
+ cleanupRegistry.register(proxy, { targetRef: new WeakRef(target), prop }, ref);
4521
4554
  return proxy;
4522
4555
  }
4523
4556
  /**
@@ -4874,7 +4907,10 @@ function mutableStore(value, opt) {
4874
4907
  */
4875
4908
  function createStoreContext() {
4876
4909
  const cache = new WeakMap();
4877
- const registry = new FinalizationRegistry(({ target, prop }) => {
4910
+ const registry = new FinalizationRegistry(({ targetRef, prop }) => {
4911
+ const target = targetRef.deref();
4912
+ if (!target)
4913
+ return;
4878
4914
  const entry = cache.get(target);
4879
4915
  if (entry)
4880
4916
  entry.delete(prop);