mutts 1.0.12 → 1.0.13

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 (55) hide show
  1. package/README.md +5 -2
  2. package/dist/browser.cjs +7 -3
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +1407 -2
  5. package/dist/browser.dev.cjs +7 -3
  6. package/dist/browser.dev.cjs.map +1 -1
  7. package/dist/browser.dev.d.ts +2 -2
  8. package/dist/browser.dev.esm.js +2 -2
  9. package/dist/browser.esm.js +3 -3
  10. package/dist/chunks/{index-yK0HVxHv.cjs → index-CAdnMJev.cjs} +202 -79
  11. package/dist/chunks/index-CAdnMJev.cjs.map +1 -0
  12. package/dist/chunks/{index-BUop6B2U.esm.js → index-XsYTUhHx.esm.js} +200 -77
  13. package/dist/chunks/index-XsYTUhHx.esm.js.map +1 -0
  14. package/dist/chunks/{node-Dd0esp5F.cjs → node-DrrphEPf.cjs} +2 -2
  15. package/dist/chunks/{node-Dd0esp5F.cjs.map → node-DrrphEPf.cjs.map} +1 -1
  16. package/dist/chunks/{node-Bo7WU5S2.esm.js → node-NEZvVo4M.esm.js} +2 -2
  17. package/dist/chunks/{node-Bo7WU5S2.esm.js.map → node-NEZvVo4M.esm.js.map} +1 -1
  18. package/dist/chunks/{proxy-D2C49sXH.esm.js → proxy-BtmPFjSr.esm.js} +307 -66
  19. package/dist/chunks/proxy-BtmPFjSr.esm.js.map +1 -0
  20. package/dist/chunks/{proxy-BvM4yewA.cjs → proxy-DBHj3kGK.cjs} +313 -66
  21. package/dist/chunks/proxy-DBHj3kGK.cjs.map +1 -0
  22. package/dist/debug.cjs +537 -166
  23. package/dist/debug.cjs.map +1 -1
  24. package/dist/debug.d.ts +96 -80
  25. package/dist/debug.esm.js +533 -166
  26. package/dist/debug.esm.js.map +1 -1
  27. package/dist/devtools/panel.js.map +1 -1
  28. package/dist/mutts.umd.js +508 -140
  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 +8 -4
  33. package/dist/node.cjs.map +1 -1
  34. package/dist/node.d.ts +2 -2
  35. package/dist/node.dev.cjs +8 -4
  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-Bx2PhORg.d.ts → types.d.ts} +12 -0
  41. package/docs/ai/api-reference.md +102 -12
  42. package/docs/ai/manual.md +60 -24
  43. package/docs/debug-getReason.md +161 -0
  44. package/docs/flavored.md +98 -1
  45. package/docs/reactive/advanced.md +15 -2
  46. package/docs/reactive/attend.md +32 -0
  47. package/docs/reactive/core.md +40 -6
  48. package/docs/reactive/debugging.md +25 -2
  49. package/docs/reactive.md +2 -0
  50. package/package.json +2 -3
  51. package/dist/chunks/index-BUop6B2U.esm.js.map +0 -1
  52. package/dist/chunks/index-yK0HVxHv.cjs.map +0 -1
  53. package/dist/chunks/proxy-BvM4yewA.cjs.map +0 -1
  54. package/dist/chunks/proxy-D2C49sXH.esm.js.map +0 -1
  55. package/dist/index.d.ts +0 -1322
@@ -1,4 +1,4 @@
1
- import { t as decorator, B as flavored, O as isReactive, $ as reactive, x as effect, a4 as touched, S as link, G as getState, aa as dependant, a5 as touched1, ab as keysOf, ac as markWithRoot, ad as objectsWithDeepWatchers, ae as effectToDeepWatchedObjects, a7 as untracked, af as deepWatchers, a0 as options, L as isNonReactive, a8 as unwrap, ag as registerDeepWatcher, V as named, ah as rootFunctionSymbol, _ as proxyToObject, ai as getRoot, u as deepCompare, aj as optionCall, a2 as root, ak as FoolProof, z as flavorOptions, al as effectHistory, p as captured, am as unreactiveProperties, g as addUnreactiveProps, an as __runInitializers, ao as __esDecorate, n as atomic, ap as batch, aq as contentRef, ar as notifyPropertyChange, as as metaProtos, at as wrapProtos, au as objectParents, av as watchers, aw as effectToReactiveObjects, W as objectToProxy } from './proxy-D2C49sXH.esm.js';
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';
2
2
 
3
3
  // Integrated with `using` statement via Symbol.dispose
4
4
  const fr = new FinalizationRegistry((f) => f());
@@ -256,46 +256,57 @@ function buildPatches(history, A, B, offset, finalX, finalY, finalD, finalK, vOf
256
256
  var _a, _b;
257
257
  const events = Symbol('events');
258
258
  const hooks = Symbol('hooks');
259
+ function getEventMap(target) {
260
+ return target[events];
261
+ }
262
+ function getHookSet(target) {
263
+ return target[hooks];
264
+ }
259
265
  const eventBehavior = {
260
266
  on(eventOrEvents, cb) {
267
+ const self = this;
268
+ const eventMap = getEventMap(self);
261
269
  if (typeof eventOrEvents === 'object') {
262
270
  for (const e of Object.keys(eventOrEvents)) {
263
271
  this.on(e, eventOrEvents[e]);
264
272
  }
265
273
  }
266
274
  else if (cb !== undefined) {
267
- const callbacks = this[events].get(eventOrEvents) ?? new Set();
275
+ const callbacks = eventMap.get(eventOrEvents) ?? new Set();
268
276
  if (!callbacks.has(cb))
269
277
  callbacks.add(cb);
270
- this[events].set(eventOrEvents, callbacks);
278
+ eventMap.set(eventOrEvents, callbacks);
271
279
  }
272
280
  return () => this.off(eventOrEvents, cb);
273
281
  },
274
282
  off(eventOrEvents, cb) {
283
+ const self = this;
284
+ const eventMap = getEventMap(self);
275
285
  if (typeof eventOrEvents === 'object') {
276
286
  for (const e of Object.keys(eventOrEvents)) {
277
287
  this.off(e, eventOrEvents[e]);
278
288
  }
279
289
  }
280
290
  else if (cb !== null && cb !== undefined) {
281
- const callbacks = this[events].get(eventOrEvents);
291
+ const callbacks = eventMap.get(eventOrEvents);
282
292
  if (callbacks) {
283
293
  callbacks.delete(cb);
284
294
  if (!callbacks.size)
285
- this[events].delete(eventOrEvents);
295
+ eventMap.delete(eventOrEvents);
286
296
  }
287
297
  }
288
298
  else {
289
299
  // Remove all listeners for this event
290
- this[events].delete(eventOrEvents);
300
+ eventMap.delete(eventOrEvents);
291
301
  }
292
302
  },
293
303
  emit(event, ...args) {
294
- const callbacks = this[events].get(event);
304
+ const self = this;
305
+ const callbacks = getEventMap(self).get(event);
295
306
  if (callbacks)
296
307
  for (const cb of callbacks)
297
308
  cb.apply(this, args);
298
- for (const cb of this[hooks])
309
+ for (const cb of getHookSet(self))
299
310
  cb.call(this, event, ...args);
300
311
  },
301
312
  };
@@ -305,7 +316,7 @@ function perEvent(eventful, fct, use) {
305
316
  get(target, prop) {
306
317
  if (typeof prop !== 'string')
307
318
  return target[prop];
308
- if (use && !eventful[events].has(prop) && !eventful[hooks].size)
319
+ if (use && !getEventMap(eventful).has(prop) && !getHookSet(eventful).size)
309
320
  return () => { };
310
321
  // Return cached function or create and cache
311
322
  let cached = cache.get(prop);
@@ -744,7 +755,7 @@ function chainPromise(given) {
744
755
  return chained;
745
756
  }
746
757
 
747
- function attend(source, callback) {
758
+ const attend = captioned(function attend(source, callback) {
748
759
  const enumerate = typeof source === 'function'
749
760
  ? source
750
761
  : Array.isArray(source)
@@ -755,14 +766,16 @@ function attend(source, callback) {
755
766
  ? () => source.values()
756
767
  : () => Object.keys(source);
757
768
  const keyEffects = new Map();
758
- const outer = effect.named('attend')(({ ascend }) => {
769
+ const callbackLabel = callback.name ? callback.name : '';
770
+ const outer = effect `attend`(({ ascend }) => {
759
771
  const keys = new Set();
760
772
  for (const key of enumerate())
761
773
  keys.add(key);
762
774
  for (const key of keys) {
763
775
  if (keyEffects.has(key))
764
776
  continue;
765
- keyEffects.set(key, ascend(() => effect.named(`attend:${key}`)((access) => callback(key, access))));
777
+ const indexRef = { value: key };
778
+ keyEffects.set(key, ascend(() => effect `attend${callbackLabel ? `:${callbackLabel}` : ''}:${key}`((access) => callback(indexRef.value, access))));
766
779
  }
767
780
  for (const key of Array.from(keyEffects.keys())) {
768
781
  if (!keys.has(key)) {
@@ -777,17 +790,50 @@ function attend(source, callback) {
777
790
  stop(reason);
778
791
  keyEffects.clear();
779
792
  };
780
- }
781
- function lift(cb) {
793
+ }, {
794
+ name: 'attend',
795
+ callbackIndex: 1,
796
+ warn: (message) => options.warn(`[reactive] ${message}`),
797
+ });
798
+ /**
799
+ * Lifts a callback that returns an object into a reactive object that automatically
800
+ * synchronizes with the source object returned by the callback.
801
+ *
802
+ * The returned reactive object will update whenever the callback's dependencies change,
803
+ * efficiently syncing only the properties that differ from the previous result using
804
+ * Object.assign(). Properties that no longer exist in the source are automatically removed.
805
+ *
806
+ * @example
807
+ * ```typescript
808
+ * const user = reactive({ name: 'John', age: 30 })
809
+ * const profile = lift(() => ({
810
+ * displayName: user.name.toUpperCase(),
811
+ * isAdult: user.age >= 18,
812
+ * description: `${user.name} is ${user.age} years old`
813
+ * }))
814
+ *
815
+ * console.log(profile.displayName) // JOHN
816
+ * console.log(profile.isAdult) // true
817
+ *
818
+ * user.name = 'Jane'
819
+ * console.log(profile.displayName) // JANE
820
+ * console.log(profile.description) // Jane is 30 years old
821
+ * ```
822
+ *
823
+ * @param cb Callback function that returns an object
824
+ * @returns A reactive object synchronized with the callback's result, with a [cleanup] property to stop tracking
825
+ */
826
+ const lift = captioned(function lift(cb) {
782
827
  let result;
783
828
  let rawResult;
784
- const liftCleanup = effect.named(`lift:${cb.name}`)(markWithRoot((access) => {
829
+ const resultName = `lift:${cb.name || 'anonymous'}`;
830
+ const liftCleanup = effect `lift:${cb.name}`(markWithRoot((access) => {
785
831
  const source = cb(access);
786
832
  if (!source || typeof source !== 'object')
787
833
  throw new Error('lift callback must return an array or object');
788
834
  const sourceProto = Object.getPrototypeOf(source);
789
835
  if (!result) {
790
- rawResult = Array.isArray(source) ? [] : Object.create(sourceProto);
836
+ rawResult = tag(resultName, Array.isArray(source) ? [] : Object.create(sourceProto));
791
837
  result = reactive(rawResult);
792
838
  }
793
839
  if (sourceProto !== Object.getPrototypeOf(result))
@@ -798,6 +844,7 @@ function lift(cb) {
798
844
  res.splice(indexA, sliceA.length, ...sliceB);
799
845
  }
800
846
  else {
847
+ const recordResult = rawResult;
801
848
  for (const key of Object.keys(source)) {
802
849
  const had = key in rawResult;
803
850
  const newDesc = Object.getOwnPropertyDescriptor(source, key);
@@ -806,7 +853,7 @@ function lift(cb) {
806
853
  const sameAccessor = oldDesc && newDesc.get && oldDesc.get === newDesc.get;
807
854
  Object.defineProperty(rawResult, key, newDesc);
808
855
  if (!sameAccessor &&
809
- rawResult[key] !==
856
+ recordResult[key] !==
810
857
  (oldDesc ? (oldDesc.get ? oldDesc.get() : oldDesc.value) : undefined))
811
858
  touched1(rawResult, { type: 'set', prop: key }, key);
812
859
  }
@@ -817,13 +864,16 @@ function lift(cb) {
817
864
  }
818
865
  for (const key of Object.keys(rawResult))
819
866
  if (!(key in source)) {
820
- delete rawResult[key];
867
+ delete recordResult[key];
821
868
  touched1(rawResult, { type: 'del', prop: key }, key);
822
869
  }
823
870
  }
824
871
  }, cb));
825
872
  return link(result, liftCleanup);
826
- }
873
+ }, {
874
+ name: 'lift',
875
+ warn: (message) => options.warn(`[reactive] ${message}`),
876
+ });
827
877
  /**
828
878
  * Reactively maps an array source through `fn`, producing a lazy reactive output array.
829
879
  *
@@ -847,12 +897,18 @@ function morphArray(source, fn, options) {
847
897
  }
848
898
  let track;
849
899
  const itemEffects = new Map();
850
- const cache = [];
900
+ const cache = tag(`morph:${fn.name || 'anonymous'}`, []);
851
901
  let input = [];
852
902
  function stopItem(key) {
853
903
  const entry = itemEffects.get(key);
854
904
  if (entry) {
855
- entry.stop({ type: 'stopped' });
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 }));
856
912
  itemEffects.delete(key);
857
913
  }
858
914
  }
@@ -865,12 +921,22 @@ function morphArray(source, fn, options) {
865
921
  }
866
922
  else {
867
923
  const indexRef = { value: key };
868
- const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
924
+ const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
869
925
  cache[indexRef.value] = fn(input, access);
870
926
  return (reason) => {
871
927
  delete cache[indexRef.value];
872
928
  touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
873
- stop?.({ type: 'invalidate', cause: reason });
929
+ const activeEffect = getActiveEffect();
930
+ let chain;
931
+ if (activeEffect) {
932
+ const node = getEffectNode(activeEffect);
933
+ chain = node.currentReason;
934
+ }
935
+ stop?.({
936
+ type: 'invalidate',
937
+ cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
938
+ chain: chainExternalReason(chain),
939
+ });
874
940
  };
875
941
  }));
876
942
  itemEffects.set(key, { stop, index: indexRef });
@@ -889,7 +955,7 @@ function morphArray(source, fn, options) {
889
955
  return Reflect.has(input, prop);
890
956
  },
891
957
  });
892
- const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
958
+ const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
893
959
  track = ascend;
894
960
  const newInput = [...(typeof source === 'function' ? source() : source)];
895
961
  const diffs = arrayDiff(input, newInput).toSorted((a, b) => b.indexA - a.indexA);
@@ -963,12 +1029,18 @@ function morphMap(source, fn, options) {
963
1029
  }
964
1030
  let track;
965
1031
  const itemEffects = new Map();
966
- const cache = new Map();
1032
+ const cache = tag(`morph:${fn.name || 'anonymous'}`, new Map());
967
1033
  Object.defineProperty(cache, 'constructor', { value: Object, enumerable: false });
968
1034
  function stopItem(key) {
969
1035
  const stop = itemEffects.get(key);
970
1036
  if (stop) {
971
- stop({ type: 'stopped' });
1037
+ const activeEffect = getActiveEffect();
1038
+ let chain;
1039
+ if (activeEffect) {
1040
+ const node = getEffectNode(activeEffect);
1041
+ chain = node.currentReason;
1042
+ }
1043
+ stop({ type: 'stopped', chain });
972
1044
  itemEffects.delete(key);
973
1045
  }
974
1046
  }
@@ -978,12 +1050,25 @@ function morphMap(source, fn, options) {
978
1050
  cache.set(key, track(() => fn(val, key)));
979
1051
  }
980
1052
  else {
981
- const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
982
- cache.set(key, fn(source.get(key), key, access));
1053
+ const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
1054
+ const next = source.get(key);
1055
+ if (next === undefined && !source.has(key))
1056
+ return;
1057
+ cache.set(key, fn(next, key, access));
983
1058
  return (reason) => {
984
1059
  cache.delete(key);
985
1060
  touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
986
- stop?.({ type: 'invalidate', cause: reason });
1061
+ const activeEffect = getActiveEffect();
1062
+ let chain;
1063
+ if (activeEffect) {
1064
+ const node = getEffectNode(activeEffect);
1065
+ chain = node.currentReason;
1066
+ }
1067
+ stop?.({
1068
+ type: 'invalidate',
1069
+ cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
1070
+ chain: chainExternalReason(chain),
1071
+ });
987
1072
  };
988
1073
  }));
989
1074
  itemEffects.set(key, stop);
@@ -1027,7 +1112,7 @@ function morphMap(source, fn, options) {
1027
1112
  },
1028
1113
  });
1029
1114
  let stateSnapshot = getState(source);
1030
- const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
1115
+ const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
1031
1116
  track = ascend;
1032
1117
  dependant(source, keysOf);
1033
1118
  while ('evolution' in stateSnapshot) {
@@ -1074,7 +1159,13 @@ function morphRecord(source, fn, options) {
1074
1159
  function stopItem(key) {
1075
1160
  const stop = itemEffects.get(key);
1076
1161
  if (stop) {
1077
- stop({ type: 'stopped' });
1162
+ const activeEffect = getActiveEffect();
1163
+ let chain;
1164
+ if (activeEffect) {
1165
+ const node = getEffectNode(activeEffect);
1166
+ chain = node.currentReason;
1167
+ }
1168
+ stop({ type: 'stopped', chain });
1078
1169
  itemEffects.delete(key);
1079
1170
  }
1080
1171
  }
@@ -1084,12 +1175,22 @@ function morphRecord(source, fn, options) {
1084
1175
  cache[key] = track(() => fn(val, key));
1085
1176
  }
1086
1177
  else {
1087
- const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
1178
+ const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
1088
1179
  cache[key] = fn(source[key], key, access);
1089
1180
  return (reason) => {
1090
1181
  delete cache[key];
1091
1182
  touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
1092
- stop?.({ type: 'invalidate', cause: reason });
1183
+ const activeEffect = getActiveEffect();
1184
+ let chain;
1185
+ if (activeEffect) {
1186
+ const node = getEffectNode(activeEffect);
1187
+ chain = node.currentReason;
1188
+ }
1189
+ stop?.({
1190
+ type: 'invalidate',
1191
+ cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
1192
+ chain: chainExternalReason(chain),
1193
+ });
1093
1194
  };
1094
1195
  }));
1095
1196
  itemEffects.set(key, stop);
@@ -1116,7 +1217,7 @@ function morphRecord(source, fn, options) {
1116
1217
  },
1117
1218
  });
1118
1219
  let stateSnapshot = getState(source);
1119
- const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
1220
+ const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
1120
1221
  track = ascend;
1121
1222
  // Track only structural changes on source
1122
1223
  dependant(source, keysOf);
@@ -1157,7 +1258,7 @@ function morphRecord(source, fn, options) {
1157
1258
  * // Changing users[0].name only recomputes names[0]
1158
1259
  * ```
1159
1260
  */
1160
- const morph = flavored(function morph(source, fn, options) {
1261
+ const morph = captioned(flavored(function morph(source, fn, options) {
1161
1262
  if (Array.isArray(source) || typeof source === 'function')
1162
1263
  return morphArray(source, fn, options);
1163
1264
  if (source instanceof Map)
@@ -1167,6 +1268,10 @@ const morph = flavored(function morph(source, fn, options) {
1167
1268
  get pure() {
1168
1269
  return (source, fn, _opt) => this(source, fn, { pure: true });
1169
1270
  },
1271
+ }), {
1272
+ name: 'morph',
1273
+ callbackIndex: 1,
1274
+ warn: (message) => options.warn(`[reactive] ${message}`),
1170
1275
  });
1171
1276
 
1172
1277
  /**
@@ -1192,7 +1297,7 @@ function deepWatch(target, callback, { immediate = false } = {}) {
1192
1297
  const wrappedCallback = markWithRoot((() => callback(target)), callback);
1193
1298
  registerDeepWatcher();
1194
1299
  // Use the existing effect system to register dependencies
1195
- return effect.named('deepWatch')(() => {
1300
+ return effect `deepWatch`(() => {
1196
1301
  // Mark the target object as having deep watchers
1197
1302
  objectsWithDeepWatchers.add(target);
1198
1303
  // Track which objects this effect is watching for cleanup
@@ -1256,7 +1361,7 @@ function deepWatch(target, callback, { immediate = false } = {}) {
1256
1361
  traverseAndTrack(target);
1257
1362
  // Only call the callback if immediate is true or if it's not the first run
1258
1363
  if (immediate) {
1259
- untracked(() => callback(target));
1364
+ untracked `deepWatch:callback`(() => callback(target));
1260
1365
  }
1261
1366
  immediate = true;
1262
1367
  // Return a cleanup function that properly removes deep watcher tracking
@@ -1323,7 +1428,7 @@ function memoizeFunction(fn, opts) {
1323
1428
  const wasVerification = options.isVerificationRun;
1324
1429
  options.isVerificationRun = true;
1325
1430
  try {
1326
- const fresh = untracked(() => fn.apply(this, args));
1431
+ const fresh = untracked `memoize:verify-calculation`(() => fn.apply(this, args));
1327
1432
  if (!deepCompare(node.result, fresh)) {
1328
1433
  optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'calculation');
1329
1434
  }
@@ -1336,7 +1441,7 @@ function memoizeFunction(fn, opts) {
1336
1441
  }
1337
1442
  // Create memoize internal effect to track dependencies and invalidate cache
1338
1443
  // Use untracked to prevent the effect creation from being affected by parent effects
1339
- node.cleanup = root(() => effect.named('memoize')(() => {
1444
+ node.cleanup = root `memoize:root`(() => effect `memoize`(() => {
1340
1445
  // Execute the function and track its dependencies
1341
1446
  // The function execution will automatically track dependencies on reactive objects
1342
1447
  node.result = fn.apply(this, args);
@@ -1347,7 +1452,17 @@ function memoizeFunction(fn, opts) {
1347
1452
  // Lazy memoization: stop the effect so it doesn't re-run immediately.
1348
1453
  // It will be re-created on next access.
1349
1454
  if (node.cleanup) {
1350
- node.cleanup({ type: 'invalidate', cause: reason });
1455
+ const activeEffect = getActiveEffect();
1456
+ let chain;
1457
+ if (activeEffect) {
1458
+ const effectNode = getEffectNode(activeEffect);
1459
+ chain = effectNode.currentReason;
1460
+ }
1461
+ node.cleanup({
1462
+ type: 'invalidate',
1463
+ cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
1464
+ chain: chainExternalReason(chain),
1465
+ });
1351
1466
  node.cleanup = undefined;
1352
1467
  }
1353
1468
  };
@@ -1356,7 +1471,7 @@ function memoizeFunction(fn, opts) {
1356
1471
  const wasVerification = options.isVerificationRun;
1357
1472
  options.isVerificationRun = true;
1358
1473
  try {
1359
- const fresh = untracked(() => fn.apply(this, args));
1474
+ const fresh = untracked `memoize:verify-comparison`(() => fn.apply(this, args));
1360
1475
  if (!deepCompare(node.result, fresh)) {
1361
1476
  optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'comparison');
1362
1477
  }
@@ -1548,12 +1663,12 @@ const memoize = flavored(makeMemoizeDecorator(), {
1548
1663
  function organized(source, apply, baseTarget = {}) {
1549
1664
  const observedSource = reactive(source);
1550
1665
  const target = reactive(baseTarget);
1551
- const stop = attend(() => {
1666
+ const stop = attend `organized:entries`(function enumerateObservedSourceKeys() {
1552
1667
  const keys = [];
1553
1668
  for (const key in observedSource)
1554
1669
  keys.push(key);
1555
1670
  return keys;
1556
- }, (key) => {
1671
+ }, function applyObservedSourceKey(key) {
1557
1672
  const sourceKey = key;
1558
1673
  const accessBase = {
1559
1674
  key: sourceKey,
@@ -1591,7 +1706,7 @@ function organize(target, property, access) {
1591
1706
 
1592
1707
  //#region watch
1593
1708
  const unsetYet = Symbol('unset-yet');
1594
- const watch = flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
1709
+ const watch = captioned(flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
1595
1710
  changed, options = {}) {
1596
1711
  return typeof value === 'function'
1597
1712
  ? watchCallBack(value, changed, options)
@@ -1607,11 +1722,14 @@ changed, options = {}) {
1607
1722
  get immediate() {
1608
1723
  return flavorOptions(this, { immediate: true });
1609
1724
  },
1725
+ }), {
1726
+ name: 'watch',
1727
+ warn: (message) => options.warn(`[reactive] ${message}`),
1610
1728
  });
1611
1729
  function watchObject(value, changed, { immediate = false, deep = false } = {}) {
1612
1730
  if (deep)
1613
1731
  return deepWatch(value, changed, { immediate });
1614
- return effect.named('watch:object')(() => {
1732
+ return effect `watch:object`(() => {
1615
1733
  dependant(value);
1616
1734
  if (immediate)
1617
1735
  changed(value);
@@ -1621,16 +1739,16 @@ function watchObject(value, changed, { immediate = false, deep = false } = {}) {
1621
1739
  function watchCallBack(value, changed, { immediate = false, deep = false } = {}) {
1622
1740
  let oldValue = unsetYet;
1623
1741
  let deepCleanup;
1624
- const cbCleanup = effect.named('watch:callback')(markWithRoot((access) => {
1742
+ const cbCleanup = effect `watch:callback`(markWithRoot((access) => {
1625
1743
  const newValue = value(access);
1626
1744
  if (oldValue !== newValue) {
1627
1745
  const old = oldValue;
1628
1746
  if (old === unsetYet) {
1629
1747
  if (immediate)
1630
- untracked(() => changed(newValue));
1748
+ untracked `watch:changed`(() => changed(newValue));
1631
1749
  }
1632
1750
  else
1633
- untracked(() => changed(newValue, old));
1751
+ untracked `watch:changed`(() => changed(newValue, old));
1634
1752
  }
1635
1753
  oldValue = newValue;
1636
1754
  if (deep) {
@@ -1657,7 +1775,7 @@ function watchCallBack(value, changed, { immediate = false, deep = false } = {})
1657
1775
  function when(predicate, timeout) {
1658
1776
  return new Promise((resolve, reject) => {
1659
1777
  let timer;
1660
- const stop = effect.named('watch:when')((access) => {
1778
+ const stop = effect `watch:when`((access) => {
1661
1779
  try {
1662
1780
  const value = predicate(access);
1663
1781
  if (value) {
@@ -1764,7 +1882,7 @@ function resource(fetcher, options = {}) {
1764
1882
  // Solve race conditions: make sure a new fast request is not overloaded by a slow old one
1765
1883
  let counter = 0;
1766
1884
  return lazyInit(resource, () => {
1767
- link(resource, effect.named('watch:resource')((access) => {
1885
+ link(resource, effect `watch:resource`((access) => {
1768
1886
  // Track reload signal to enable manual reloading
1769
1887
  void reloadSignal.value;
1770
1888
  const id = ++counter;
@@ -1930,6 +2048,7 @@ let ReactiveArrayWrapper = (() => {
1930
2048
  return super.findLastIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
1931
2049
  }
1932
2050
  flat(depth) {
2051
+ dependant(this, keysOf);
1933
2052
  return reactive(super.flat(depth));
1934
2053
  }
1935
2054
  flatMap(callbackfn, thisArg) {
@@ -2356,59 +2475,62 @@ function isCached(object, propertyKey) {
2356
2475
  function cache(object, propertyKey, value) {
2357
2476
  Object.defineProperty(object, propertyKey, { value });
2358
2477
  }
2478
+ function descriptorBase(descriptor) {
2479
+ return function descriptorDecorator(...properties) {
2480
+ return (Base) => {
2481
+ return class extends Base {
2482
+ constructor(...args) {
2483
+ super(...args);
2484
+ for (const key of properties) {
2485
+ const existing = Object.getOwnPropertyDescriptor(this, key);
2486
+ Object.defineProperty(this, key, Object.assign(existing || {}, descriptor));
2487
+ }
2488
+ }
2489
+ };
2490
+ };
2491
+ };
2492
+ }
2359
2493
  /**
2360
2494
  * Creates a decorator that modifies property descriptors for specified properties
2361
2495
  * @param descriptor - The descriptor properties to apply
2362
2496
  * @returns A class decorator that applies the descriptor to specified properties
2363
2497
  */
2364
- const descriptor = flavored(function descriptor(descriptor) {
2365
- return (...properties) => (Base) => {
2366
- return class extends Base {
2367
- constructor(...args) {
2368
- super(...args);
2369
- for (const key of properties) {
2370
- const existing = Object.getOwnPropertyDescriptor(this, key);
2371
- Object.defineProperty(this, key, Object.assign(existing || {}, descriptor));
2372
- }
2373
- }
2374
- };
2375
- };
2376
- }, {
2498
+ const descriptor = Object.assign(descriptorBase, {
2377
2499
  /**
2378
2500
  * enumerable: true
2379
2501
  */
2380
2502
  get enumerable() {
2381
- return descriptor({ enumerable: true });
2503
+ return descriptorBase({ enumerable: true });
2382
2504
  },
2383
2505
  /**
2384
2506
  * enumerable: false
2385
2507
  */
2386
2508
  get hidden() {
2387
- return descriptor({ enumerable: false });
2509
+ return descriptorBase({ enumerable: false });
2388
2510
  },
2389
2511
  /**
2390
2512
  * configurable: true
2391
2513
  */
2392
2514
  get configurable() {
2393
- return descriptor({ configurable: true });
2515
+ return descriptorBase({ configurable: true });
2394
2516
  },
2395
2517
  /**
2396
2518
  * configurable: false
2397
2519
  */
2398
2520
  get frozen() {
2399
- return descriptor({ configurable: false });
2521
+ return descriptorBase({ configurable: false });
2400
2522
  },
2401
2523
  /**
2402
2524
  * writable: true
2403
2525
  */
2404
2526
  get writable() {
2405
- return descriptor({ writable: true });
2527
+ return descriptorBase({ writable: true });
2406
2528
  },
2407
2529
  /**
2408
2530
  * writable: false
2409
2531
  */
2410
2532
  get readonly() {
2411
- return descriptor({ writable: false });
2533
+ return descriptorBase({ writable: false });
2412
2534
  },
2413
2535
  });
2414
2536
  /**
@@ -2537,24 +2659,25 @@ function throttle(delay) {
2537
2659
  });
2538
2660
  }
2539
2661
 
2540
- var version$1 = "1.0.12";
2662
+ var version$1 = "1.0.13";
2541
2663
  var pkg = {
2542
2664
  version: version$1};
2543
2665
 
2544
2666
  const { version } = pkg;
2545
2667
  const GLOBAL_MUTTS_KEY = '__MUTTS_INSTANCE__';
2668
+ const runtimeGlobals = globalThis;
2546
2669
  const globalScope = (typeof globalThis !== 'undefined'
2547
2670
  ? globalThis
2548
- : typeof window !== 'undefined'
2549
- ? window
2550
- : typeof global !== 'undefined'
2551
- ? global
2671
+ : runtimeGlobals.window
2672
+ ? runtimeGlobals.window
2673
+ : runtimeGlobals.global
2674
+ ? runtimeGlobals.global
2552
2675
  : false);
2553
2676
  if (globalScope) {
2554
2677
  let source = 'mutts/index';
2555
2678
  try {
2556
- if (typeof __filename !== 'undefined')
2557
- source = __filename;
2679
+ if (runtimeGlobals.__filename)
2680
+ source = runtimeGlobals.__filename;
2558
2681
  else if (typeof import.meta !== 'undefined' && import.meta.url) {
2559
2682
  source = import.meta.url;
2560
2683
  }
@@ -2574,4 +2697,4 @@ if (globalScope) {
2574
2697
  }
2575
2698
 
2576
2699
  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 };
2577
- //# sourceMappingURL=index-BUop6B2U.esm.js.map
2700
+ //# sourceMappingURL=index-XsYTUhHx.esm.js.map