mutts 1.0.13 → 1.0.15

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.
Files changed (54) hide show
  1. package/BROWSER_ASYNC_POLYFILL.md +79 -0
  2. package/README.md +2 -2
  3. package/dist/browser.cjs +216 -97
  4. package/dist/browser.cjs.map +1 -1
  5. package/dist/browser.d.ts +42 -9
  6. package/dist/browser.dev.cjs +81 -71
  7. package/dist/browser.dev.cjs.map +1 -1
  8. package/dist/browser.dev.d.ts +2 -2
  9. package/dist/browser.dev.esm.js +2 -2
  10. package/dist/browser.esm.js +137 -28
  11. package/dist/browser.esm.js.map +1 -1
  12. package/dist/chunks/{proxy-BtmPFjSr.esm.js → effects-CPjf6gNA.esm.js} +2605 -2155
  13. package/dist/chunks/effects-CPjf6gNA.esm.js.map +1 -0
  14. package/dist/chunks/{proxy-DBHj3kGK.cjs → effects-oUauWxxl.cjs} +2615 -2156
  15. package/dist/chunks/effects-oUauWxxl.cjs.map +1 -0
  16. package/dist/chunks/{index-CAdnMJev.cjs → index-Dtj2qZ0d.cjs} +418 -350
  17. package/dist/chunks/index-Dtj2qZ0d.cjs.map +1 -0
  18. package/dist/chunks/{index-XsYTUhHx.esm.js → index-YGpb1te7.esm.js} +156 -88
  19. package/dist/chunks/index-YGpb1te7.esm.js.map +1 -0
  20. package/dist/chunks/node-CKuro-Uk.cjs +187 -0
  21. package/dist/chunks/node-CKuro-Uk.cjs.map +1 -0
  22. package/dist/chunks/node-sQtns7M-.esm.js +185 -0
  23. package/dist/chunks/node-sQtns7M-.esm.js.map +1 -0
  24. package/dist/debug.cjs +76 -49
  25. package/dist/debug.cjs.map +1 -1
  26. package/dist/debug.esm.js +37 -10
  27. package/dist/debug.esm.js.map +1 -1
  28. package/dist/mutts.umd.js +4169 -3532
  29. package/dist/mutts.umd.js.map +1 -1
  30. package/dist/mutts.umd.min.js +1 -1
  31. package/dist/mutts.umd.min.js.map +1 -1
  32. package/dist/node.cjs +82 -72
  33. package/dist/node.cjs.map +1 -1
  34. package/dist/node.d.ts +2 -2
  35. package/dist/node.dev.cjs +82 -72
  36. package/dist/node.dev.cjs.map +1 -1
  37. package/dist/node.dev.d.ts +2 -2
  38. package/dist/node.dev.esm.js +3 -3
  39. package/dist/node.esm.js +3 -3
  40. package/dist/types.d.ts +30 -15
  41. package/docs/ai/api-reference.md +3 -1
  42. package/docs/ai/manual.md +17 -5
  43. package/docs/reactive/advanced.md +169 -10
  44. package/docs/reactive/debugging.md +15 -13
  45. package/docs/reactive.md +2 -1
  46. package/package.json +12 -7
  47. package/dist/chunks/index-CAdnMJev.cjs.map +0 -1
  48. package/dist/chunks/index-XsYTUhHx.esm.js.map +0 -1
  49. package/dist/chunks/node-DrrphEPf.cjs +0 -98
  50. package/dist/chunks/node-DrrphEPf.cjs.map +0 -1
  51. package/dist/chunks/node-NEZvVo4M.esm.js +0 -96
  52. package/dist/chunks/node-NEZvVo4M.esm.js.map +0 -1
  53. package/dist/chunks/proxy-BtmPFjSr.esm.js.map +0 -1
  54. package/dist/chunks/proxy-DBHj3kGK.cjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { u as decorator, p as captioned, a4 as options, y as effect, af as markWithRoot, W as link, F as flavored, a7 as tag, a3 as reactive, a9 as touched1, T as isReactive, a8 as touched, K as getState, ag as dependant, J as getActiveEffect, ah as getEffectNode, ai as chainExternalReason, aj as keysOf, ak as objectsWithDeepWatchers, al as effectToDeepWatchedObjects, ab as untracked, am as deepWatchers, P as isNonReactive, ac as unwrap, an as registerDeepWatcher, _ as named, ao as rootFunctionSymbol, a2 as proxyToObject, ap as getRoot, v as deepCompare, aq as optionCall, a6 as root, ar as FoolProof, E as flavorOptions, as as effectHistory, q as captured, at as unreactiveProperties, g as addUnreactiveProps, au as __runInitializers, av as __esDecorate, n as atomic, aw as batch, ax as contentRef, ay as notifyPropertyChange, az as metaProtos, aA as wrapProtos, aB as objectParents, aC as watchers, aD as effectToReactiveObjects, $ as objectToProxy } from './proxy-BtmPFjSr.esm.js';
1
+ import { u as decorator, p as captioned, aa as options, y as effect, ap as markWithRoot, Y as link, F as flavored, af as tag, a9 as reactive, ai as touched1, U as isReactive, ah as touched, K as getState, aq as dependant, J as getActiveEffect, ar as getEffectNode, as as chainExternalReason, at as keysOf, au as objectsWithDeepWatchers, av as effectToDeepWatchedObjects, ak as untracked, aw as deepWatchers, Q as isNonReactive, al as unwrap, ax as registerDeepWatcher, a2 as named, ay as rootFunctionSymbol, a8 as proxyToObject, az as getRoot, aA as inertDepth, v as deepCompare, aB as optionCall, ad as root, aC as FoolProof, E as flavorOptions, aD as effectHistory, q as captured, aE as unreactiveProperties, g as addUnreactiveProps, aF as __runInitializers, aG as __esDecorate, n as atomic, aH as contentRef, aI as notifyPropertyChange, aJ as metaProtos, aK as wrapProtos, aL as objectParents, aM as watchers, aN as effectToReactiveObjects, a3 as objectToProxy } from './effects-CPjf6gNA.esm.js';
2
2
 
3
3
  // Integrated with `using` statement via Symbol.dispose
4
4
  const fr = new FinalizationRegistry((f) => f());
@@ -893,22 +893,26 @@ const lift = captioned(function lift(cb) {
893
893
  */
894
894
  function morphArray(source, fn, options) {
895
895
  if (typeof source !== 'function' && !isReactive(source) && options?.pure === true) {
896
- return source.map((i) => fn(i));
896
+ return source.map((i) => fn(i, { index: 0 }));
897
897
  }
898
898
  let track;
899
899
  const itemEffects = new Map();
900
900
  const cache = tag(`morph:${fn.name || 'anonymous'}`, []);
901
901
  let input = [];
902
+ function currentCleanupReason() {
903
+ const activeEffect = getActiveEffect();
904
+ if (!activeEffect)
905
+ return undefined;
906
+ const node = getEffectNode(activeEffect);
907
+ return node.currentReason;
908
+ }
909
+ function stopEntry(entry) {
910
+ entry.stop(chainExternalReason({ type: 'stopped', chain: currentCleanupReason() }));
911
+ }
902
912
  function stopItem(key) {
903
913
  const entry = itemEffects.get(key);
904
914
  if (entry) {
905
- const activeEffect = getActiveEffect();
906
- let chain;
907
- if (activeEffect) {
908
- const node = getEffectNode(activeEffect);
909
- chain = node.currentReason;
910
- }
911
- entry.stop(chainExternalReason({ type: 'stopped', chain }));
915
+ stopEntry(entry);
912
916
  itemEffects.delete(key);
913
917
  }
914
918
  }
@@ -916,15 +920,14 @@ function morphArray(source, fn, options) {
916
920
  const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(input));
917
921
  if (isPure) {
918
922
  track(() => {
919
- cache[key] = fn(input);
923
+ cache[key] = fn(input, { index: key });
920
924
  });
921
925
  }
922
926
  else {
923
- const indexRef = { value: key };
927
+ const position = reactive({ index: key });
924
928
  const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
925
- cache[indexRef.value] = fn(input, access);
926
- return (reason) => {
927
- delete cache[indexRef.value];
929
+ if (access.reaction) {
930
+ delete cache[position.index];
928
931
  touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
929
932
  const activeEffect = getActiveEffect();
930
933
  let chain;
@@ -934,12 +937,16 @@ function morphArray(source, fn, options) {
934
937
  }
935
938
  stop?.({
936
939
  type: 'invalidate',
937
- cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
940
+ cause: chainExternalReason(!access.reaction || access.reaction === true
941
+ ? { type: 'stopped', chain }
942
+ : access.reaction),
938
943
  chain: chainExternalReason(chain),
939
944
  });
940
- };
945
+ }
946
+ else
947
+ cache[position.index] = fn(input, position, access);
941
948
  }));
942
- itemEffects.set(key, { stop, index: indexRef });
949
+ itemEffects.set(key, { stop, position });
943
950
  }
944
951
  }
945
952
  const proxy = reactive(cache, {
@@ -960,35 +967,59 @@ function morphArray(source, fn, options) {
960
967
  const newInput = [...(typeof source === 'function' ? source() : source)];
961
968
  const diffs = arrayDiff(input, newInput).toSorted((a, b) => b.indexA - a.indexA);
962
969
  if (diffs.length > 0) {
970
+ const reusable = new Map();
971
+ for (const [index, entry] of itemEffects) {
972
+ const value = input[index];
973
+ const entries = reusable.get(value);
974
+ if (entries)
975
+ entries.push({ index, entry });
976
+ else
977
+ reusable.set(value, [{ index, entry }]);
978
+ }
979
+ const nextEffects = new Map();
980
+ const reused = new Set();
981
+ const nextCache = new Map();
982
+ for (let index = 0; index < newInput.length; index++) {
983
+ const entries = reusable.get(newInput[index]);
984
+ const reusedEntry = entries?.shift();
985
+ if (!reusedEntry)
986
+ continue;
987
+ const { index: previousIndex, entry } = reusedEntry;
988
+ reused.add(previousIndex);
989
+ nextEffects.set(index, entry);
990
+ if (entry.position.index !== index)
991
+ entry.position.index = index;
992
+ else if (Object.hasOwn(cache, previousIndex))
993
+ nextCache.set(index, cache[previousIndex]);
994
+ }
995
+ for (const [index, entry] of itemEffects) {
996
+ if (!reused.has(index))
997
+ stopEntry(entry);
998
+ }
999
+ itemEffects.clear();
1000
+ for (const [index, entry] of nextEffects)
1001
+ itemEffects.set(index, entry);
1002
+ const previousLength = cache.length;
1003
+ cache.length = newInput.length;
1004
+ for (let i = 0; i < Math.max(previousLength, newInput.length); i++)
1005
+ delete cache[i];
1006
+ for (const [index, value] of nextCache)
1007
+ cache[index] = value;
1008
+ const eagerIndices = new Set();
963
1009
  for (const diff of diffs) {
964
- // Stop items in removed range
965
- for (let i = diff.indexA; i < diff.indexA + diff.sliceA.length; i++)
966
- stopItem(i);
967
- // Shift existing itemEffects in the Map to match the new indices
968
- const shift = diff.sliceB.length - diff.sliceA.length;
969
- if (shift !== 0) {
970
- // We need to move entries in the Map.
971
- const entries = Array.from(itemEffects.entries()).sort((a, b) => a[0] - b[0]);
972
- // Remove entries that will be shifted
973
- for (const [idx, _entry] of entries) {
974
- if (idx >= diff.indexA + diff.sliceA.length) {
975
- itemEffects.delete(idx);
976
- }
977
- }
978
- // Re-add them with shifted indices
979
- for (const [idx, entry] of entries) {
980
- if (idx >= diff.indexA + diff.sliceA.length) {
981
- const newIdx = idx + shift;
982
- entry.index.value = newIdx;
983
- itemEffects.set(newIdx, entry);
984
- }
985
- }
986
- }
987
- // Splice the cache
988
- cache.splice(diff.indexA, diff.sliceA.length, ...new Array(diff.sliceB.length).fill(undefined));
989
- // Make holes for lazy computation
990
- for (let i = diff.indexA; i < diff.indexA + diff.sliceB.length; i++)
991
- delete cache[i];
1010
+ const max = Math.max(diff.sliceA.length, diff.sliceB.length);
1011
+ for (let i = 0; i < max; i++)
1012
+ eagerIndices.add(diff.indexA + i);
1013
+ }
1014
+ for (const index of eagerIndices) {
1015
+ if (index < 0 || index >= newInput.length || Object.hasOwn(cache, index))
1016
+ continue;
1017
+ const value = newInput[index];
1018
+ const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(value));
1019
+ if (isPure)
1020
+ continue;
1021
+ stopItem(index);
1022
+ computeItem(index, value);
992
1023
  }
993
1024
  const invalidates = new Set([keysOf]);
994
1025
  if (input.length !== newInput.length)
@@ -1422,6 +1453,9 @@ function memoizeFunction(fn, opts) {
1422
1453
  for (const arg of args) {
1423
1454
  node = getBranch(node, arg);
1424
1455
  }
1456
+ if (inertDepth > 0) {
1457
+ return fn.apply(this, args);
1458
+ }
1425
1459
  dependant(node, 'memoize');
1426
1460
  if ('result' in node) {
1427
1461
  if (options.onMemoizationDiscrepancy) {
@@ -2060,14 +2094,28 @@ let ReactiveArrayWrapper = (() => {
2060
2094
  }, thisArg);
2061
2095
  }
2062
2096
  includes(searchElement, fromIndex) {
2063
- return arguments.length > 1
2064
- ? super.includes(unwrap(searchElement), fromIndex)
2065
- : super.includes(unwrap(searchElement));
2097
+ const target = unwrap(searchElement);
2098
+ const length = this.length;
2099
+ let start = fromIndex ?? 0;
2100
+ if (start < 0)
2101
+ start = Math.max(length + start, 0);
2102
+ for (let i = start; i < length; i++) {
2103
+ const value = unwrap(this[i]);
2104
+ if (value === target)
2105
+ return true;
2106
+ }
2107
+ return false;
2066
2108
  }
2067
2109
  indexOf(searchElement, fromIndex) {
2068
- return arguments.length > 1
2069
- ? super.indexOf(unwrap(searchElement), fromIndex)
2070
- : super.indexOf(unwrap(searchElement));
2110
+ const target = unwrap(searchElement);
2111
+ const length = this.length;
2112
+ let start = fromIndex ?? 0;
2113
+ if (start < 0)
2114
+ start = Math.max(length + start, 0);
2115
+ for (let i = start; i < length; i++)
2116
+ if (unwrap(this[i]) === target)
2117
+ return i;
2118
+ return -1;
2071
2119
  }
2072
2120
  join(separator) {
2073
2121
  return super.join(separator);
@@ -2077,9 +2125,17 @@ let ReactiveArrayWrapper = (() => {
2077
2125
  return super.keys();
2078
2126
  }
2079
2127
  lastIndexOf(searchElement, fromIndex) {
2080
- return arguments.length > 1
2081
- ? super.lastIndexOf(unwrap(searchElement), fromIndex)
2082
- : super.lastIndexOf(unwrap(searchElement));
2128
+ const target = unwrap(searchElement);
2129
+ const length = this.length;
2130
+ let start = fromIndex ?? length - 1;
2131
+ if (start < 0)
2132
+ start = length + start;
2133
+ if (start >= length)
2134
+ start = length - 1;
2135
+ for (let i = start; i >= 0; i--)
2136
+ if (unwrap(this[i]) === target)
2137
+ return i;
2138
+ return -1;
2083
2139
  }
2084
2140
  map(callbackfn, thisArg) {
2085
2141
  return reactive(super.map((v, i, a) => unwrap(callbackfn.call(thisArg, reactive(v), i, a)), thisArg));
@@ -2119,13 +2175,25 @@ let ReactiveArrayWrapper = (() => {
2119
2175
  return super.sort(wrappedCompare);
2120
2176
  }
2121
2177
  splice(start, deleteCount, ...items) {
2178
+ const length = this.length;
2179
+ const normalizedStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
2180
+ const actualDeleteCount = arguments.length === 1
2181
+ ? length - normalizedStart
2182
+ : Math.min(Math.max(deleteCount, 0), length - normalizedStart);
2183
+ const structural = actualDeleteCount > 0 || items.length > 0;
2184
+ let result;
2122
2185
  if (arguments.length > 2)
2123
- return reactive(super.splice(start, deleteCount, ...items.map(unwrap)));
2124
- if (arguments.length === 2)
2125
- return reactive(super.splice(start, deleteCount));
2126
- if (arguments.length === 1)
2127
- return reactive(super.splice(start));
2128
- return reactive([]);
2186
+ result = super.splice(start, deleteCount, ...items.map(unwrap));
2187
+ else if (arguments.length === 2)
2188
+ result = super.splice(start, deleteCount);
2189
+ else if (arguments.length === 1)
2190
+ result = super.splice(start);
2191
+ else
2192
+ result = [];
2193
+ if (structural && this.length === length) {
2194
+ touched(this, { type: 'set', prop: 'length' }, ['length']);
2195
+ }
2196
+ return reactive(result);
2129
2197
  }
2130
2198
  unshift(...items) {
2131
2199
  return super.unshift(...items.map(unwrap));
@@ -2180,6 +2248,12 @@ let ReactiveArrayWrapper = (() => {
2180
2248
  _a;
2181
2249
  })();
2182
2250
 
2251
+ // These abstract classes are prototype tables for the reactive proxy, not classes
2252
+ // that are instantiated directly. `this` is the original Map/WeakMap target when
2253
+ // a method is reached through the proxy meta-prototype path, so calls like
2254
+ // `this.get()` or `this.entries()` intentionally re-enter the proxy machinery.
2255
+ // Do not mechanically rewrite them to `super.*` as if these were real subclass
2256
+ // instances; that would bypass parts of the designed reactive dispatch.
2183
2257
  /**
2184
2258
  * Reactive wrapper around JavaScript's WeakMap class
2185
2259
  * Only tracks individual key operations, no size tracking (WeakMap limitation)
@@ -2228,10 +2302,8 @@ class ReactiveMap extends Map {
2228
2302
  if (hadEntries) {
2229
2303
  const evolution = { type: 'bunch', method: 'clear' };
2230
2304
  // Clear triggers all effects since all keys are affected
2231
- batch(() => {
2232
- touched1(this, evolution, 'size');
2233
- touched(contentRef(this), evolution);
2234
- });
2305
+ touched1(this, evolution, 'size');
2306
+ touched(contentRef(this), evolution);
2235
2307
  }
2236
2308
  }
2237
2309
  entries() {
@@ -2269,10 +2341,8 @@ class ReactiveMap extends Map {
2269
2341
  const result = super.delete(key);
2270
2342
  if (hadKey) {
2271
2343
  const evolution = { type: 'del', prop: key };
2272
- batch(() => {
2273
- touched1(contentRef(this), evolution, key);
2274
- touched1(this, evolution, 'size');
2275
- });
2344
+ touched1(contentRef(this), evolution, key);
2345
+ touched1(this, evolution, 'size');
2276
2346
  }
2277
2347
  return result;
2278
2348
  }
@@ -2290,17 +2360,21 @@ class ReactiveMap extends Map {
2290
2360
  const reactiveValue = reactive(value);
2291
2361
  super.set(key, reactiveValue);
2292
2362
  if (!hadKey || oldValue !== reactiveValue) {
2293
- batch(() => {
2294
- notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey);
2295
- // Also notify size change for Map (WeakMap doesn't track size)
2296
- const evolution = { type: hadKey ? 'set' : 'add', prop: key };
2297
- touched1(this, evolution, 'size');
2298
- });
2363
+ notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey);
2364
+ // Also notify size change for Map (WeakMap doesn't track size)
2365
+ const evolution = { type: hadKey ? 'set' : 'add', prop: key };
2366
+ touched1(this, evolution, 'size');
2299
2367
  }
2300
2368
  return this;
2301
2369
  }
2302
2370
  }
2303
2371
 
2372
+ // These abstract classes are prototype tables for the reactive proxy, not classes
2373
+ // that are instantiated directly. `this` is the original Set/WeakSet target when
2374
+ // a method is reached through the proxy meta-prototype path, so calls like
2375
+ // `this.has()` or `this.entries()` intentionally re-enter the proxy machinery.
2376
+ // Do not mechanically rewrite them to `super.*` as if these were real subclass
2377
+ // instances; that would bypass parts of the designed reactive dispatch.
2304
2378
  /**
2305
2379
  * Reactive wrapper around JavaScript's WeakSet class
2306
2380
  * Only tracks individual value operations, no size tracking (WeakSet limitation)
@@ -2345,10 +2419,8 @@ class ReactiveSet extends Set {
2345
2419
  if (!had) {
2346
2420
  const evolution = { type: 'add', prop: reactiveValue };
2347
2421
  // touch for value-specific and aggregate dependencies
2348
- batch(() => {
2349
- touched1(contentRef(this), evolution, reactiveValue);
2350
- touched1(this, evolution, 'size');
2351
- });
2422
+ touched1(contentRef(this), evolution, reactiveValue);
2423
+ touched1(this, evolution, 'size');
2352
2424
  }
2353
2425
  return this;
2354
2426
  }
@@ -2357,10 +2429,8 @@ class ReactiveSet extends Set {
2357
2429
  super.clear();
2358
2430
  if (hadEntries) {
2359
2431
  const evolution = { type: 'bunch', method: 'clear' };
2360
- batch(() => {
2361
- touched1(this, evolution, 'size');
2362
- touched(contentRef(this), evolution);
2363
- });
2432
+ touched1(this, evolution, 'size');
2433
+ touched(contentRef(this), evolution);
2364
2434
  }
2365
2435
  }
2366
2436
  delete(value) {
@@ -2368,10 +2438,8 @@ class ReactiveSet extends Set {
2368
2438
  const res = super.delete(value);
2369
2439
  if (had) {
2370
2440
  const evolution = { type: 'del', prop: value };
2371
- batch(() => {
2372
- touched1(contentRef(this), evolution, value);
2373
- touched1(this, evolution, 'size');
2374
- });
2441
+ touched1(contentRef(this), evolution, value);
2442
+ touched1(this, evolution, 'size');
2375
2443
  }
2376
2444
  return res;
2377
2445
  }
@@ -2659,7 +2727,7 @@ function throttle(delay) {
2659
2727
  });
2660
2728
  }
2661
2729
 
2662
- var version$1 = "1.0.13";
2730
+ var version$1 = "1.0.15";
2663
2731
  var pkg = {
2664
2732
  version: version$1};
2665
2733
 
@@ -2697,4 +2765,4 @@ if (globalScope) {
2697
2765
  }
2698
2766
 
2699
2767
  export { ArrayReadForward as A, unreactive as B, watch as C, Destroyable as D, Eventful as E, when as F, Indexable as I, DestructionError as a, allocated as b, allocatedValues as c, arrayDiff as d, attend as e, cache as f, cached as g, callOnGC as h, chainPromise as i, debounce as j, deepWatch as k, deprecated as l, descriptor as m, destructor as n, forwardArray as o, getAt as p, isCached as q, lift as r, memoize as s, morph as t, organize as u, organized as v, profileInfo as w, resource as x, setAt as y, throttle as z };
2700
- //# sourceMappingURL=index-XsYTUhHx.esm.js.map
2768
+ //# sourceMappingURL=index-YGpb1te7.esm.js.map