mutts 1.0.9 → 1.0.10

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 (204) hide show
  1. package/README.md +58 -33
  2. package/dist/browser.cjs +1371 -1206
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +2 -1392
  5. package/dist/browser.dev.cjs +110 -0
  6. package/dist/browser.dev.cjs.map +1 -0
  7. package/dist/browser.dev.d.ts +2 -0
  8. package/dist/browser.dev.esm.js +5 -0
  9. package/dist/browser.dev.esm.js.map +1 -0
  10. package/dist/browser.esm.js +6 -87
  11. package/dist/browser.esm.js.map +1 -1
  12. package/dist/chunks/{async-browser-lvzLOCgk.cjs → async-browser-BU_IfxYD.cjs} +4 -85
  13. package/dist/chunks/async-browser-BU_IfxYD.cjs.map +1 -0
  14. package/dist/chunks/{async-node-C3DeIb0y.cjs → async-node-3PrbVAbB.cjs} +3 -1
  15. package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
  16. package/dist/chunks/{index-2vea86wD.esm.js → index-CaaQQlPJ.esm.js} +1116 -953
  17. package/dist/chunks/index-CaaQQlPJ.esm.js.map +1 -0
  18. package/dist/chunks/node-nKJBk8iJ.esm.js +96 -0
  19. package/dist/chunks/node-nKJBk8iJ.esm.js.map +1 -0
  20. package/dist/chunks/{index-VTO-b2vR.cjs → proxy-Dtg-bJ3T.cjs} +2285 -2983
  21. package/dist/chunks/proxy-Dtg-bJ3T.cjs.map +1 -0
  22. package/dist/chunks/{index-CtA2AWl3.esm.js → proxy-r7lARftl.esm.js} +2273 -2948
  23. package/dist/chunks/proxy-r7lARftl.esm.js.map +1 -0
  24. package/dist/debug.cjs +973 -28
  25. package/dist/debug.cjs.map +1 -1
  26. package/dist/debug.d.ts +36 -37
  27. package/dist/debug.esm.js +949 -1
  28. package/dist/debug.esm.js.map +1 -1
  29. package/dist/devtools/manifest.json +1 -1
  30. package/dist/devtools/panel.html +1 -1
  31. package/dist/devtools/panel.js +107 -94
  32. package/dist/devtools/panel.js.map +1 -1
  33. package/dist/index.d.ts +1447 -1
  34. package/dist/mutts.umd.js +7167 -1
  35. package/dist/mutts.umd.js.map +1 -1
  36. package/dist/mutts.umd.min.js +1 -1
  37. package/dist/mutts.umd.min.js.map +1 -1
  38. package/dist/node.cjs +62 -60
  39. package/dist/node.cjs.map +1 -1
  40. package/dist/node.d.ts +2 -2
  41. package/dist/node.dev.cjs +110 -0
  42. package/dist/node.dev.cjs.map +1 -0
  43. package/dist/node.dev.d.ts +2 -0
  44. package/dist/node.dev.esm.js +6 -0
  45. package/dist/node.dev.esm.js.map +1 -0
  46. package/dist/node.esm.js +4 -97
  47. package/dist/node.esm.js.map +1 -1
  48. package/dist/{types-DaHFfhlN.d.ts → types-W5vD6m2n.d.ts} +99 -44
  49. package/docs/ai/api-reference.md +11 -18
  50. package/docs/ai/manual.md +308 -230
  51. package/docs/reactive/advanced.md +312 -2
  52. package/docs/reactive/attend.md +1 -2
  53. package/docs/reactive/collections.md +21 -39
  54. package/docs/reactive/core.md +7 -37
  55. package/docs/reactive/debugging.md +117 -10
  56. package/docs/reactive/error-handling.md +10 -10
  57. package/docs/reactive/resource.md +125 -0
  58. package/docs/reactive/scan.md +32 -1
  59. package/docs/reactive.md +3 -2
  60. package/docs/utils.md +70 -0
  61. package/package.json +59 -30
  62. package/dist/chunks/async-browser-lvzLOCgk.cjs.map +0 -1
  63. package/dist/chunks/async-node-C3DeIb0y.cjs.map +0 -1
  64. package/dist/chunks/index-2vea86wD.esm.js.map +0 -1
  65. package/dist/chunks/index-CtA2AWl3.esm.js.map +0 -1
  66. package/dist/chunks/index-VTO-b2vR.cjs.map +0 -1
  67. package/dist/debug/debug.d.ts +0 -122
  68. package/dist/debug/debug.d.ts.map +0 -1
  69. package/dist/debug/index.d.ts +0 -4
  70. package/dist/debug/index.d.ts.map +0 -1
  71. package/dist/debug/lineage-panel.d.ts +0 -5
  72. package/dist/debug/lineage-panel.d.ts.map +0 -1
  73. package/dist/debug/lineage.d.ts +0 -79
  74. package/dist/debug/lineage.d.ts.map +0 -1
  75. package/dist/src/async/browser.d.ts +0 -2
  76. package/dist/src/async/browser.d.ts.map +0 -1
  77. package/dist/src/async/index.d.ts +0 -19
  78. package/dist/src/async/index.d.ts.map +0 -1
  79. package/dist/src/async/node.d.ts +0 -2
  80. package/dist/src/async/node.d.ts.map +0 -1
  81. package/dist/src/decorator.d.ts +0 -106
  82. package/dist/src/decorator.d.ts.map +0 -1
  83. package/dist/src/destroyable.d.ts +0 -87
  84. package/dist/src/destroyable.d.ts.map +0 -1
  85. package/dist/src/entry-browser.d.ts +0 -3
  86. package/dist/src/entry-browser.d.ts.map +0 -1
  87. package/dist/src/entry-node.d.ts +0 -3
  88. package/dist/src/entry-node.d.ts.map +0 -1
  89. package/dist/src/eventful.d.ts +0 -20
  90. package/dist/src/eventful.d.ts.map +0 -1
  91. package/dist/src/flavored.d.ts +0 -33
  92. package/dist/src/flavored.d.ts.map +0 -1
  93. package/dist/src/index.d.ts +0 -14
  94. package/dist/src/index.d.ts.map +0 -1
  95. package/dist/src/indexable.d.ts +0 -243
  96. package/dist/src/indexable.d.ts.map +0 -1
  97. package/dist/src/introspection.d.ts +0 -27
  98. package/dist/src/introspection.d.ts.map +0 -1
  99. package/dist/src/iterableWeak.d.ts +0 -53
  100. package/dist/src/iterableWeak.d.ts.map +0 -1
  101. package/dist/src/mixins.d.ts +0 -25
  102. package/dist/src/mixins.d.ts.map +0 -1
  103. package/dist/src/promiseChain.d.ts +0 -20
  104. package/dist/src/promiseChain.d.ts.map +0 -1
  105. package/dist/src/reactive/array.d.ts +0 -48
  106. package/dist/src/reactive/array.d.ts.map +0 -1
  107. package/dist/src/reactive/buffer.d.ts +0 -120
  108. package/dist/src/reactive/buffer.d.ts.map +0 -1
  109. package/dist/src/reactive/change.d.ts +0 -29
  110. package/dist/src/reactive/change.d.ts.map +0 -1
  111. package/dist/src/reactive/deep-touch.d.ts +0 -28
  112. package/dist/src/reactive/deep-touch.d.ts.map +0 -1
  113. package/dist/src/reactive/deep-watch-state.d.ts +0 -25
  114. package/dist/src/reactive/deep-watch-state.d.ts.map +0 -1
  115. package/dist/src/reactive/deep-watch.d.ts +0 -20
  116. package/dist/src/reactive/deep-watch.d.ts.map +0 -1
  117. package/dist/src/reactive/describe.d.ts +0 -12
  118. package/dist/src/reactive/describe.d.ts.map +0 -1
  119. package/dist/src/reactive/effect-context.d.ts +0 -34
  120. package/dist/src/reactive/effect-context.d.ts.map +0 -1
  121. package/dist/src/reactive/effects.d.ts +0 -164
  122. package/dist/src/reactive/effects.d.ts.map +0 -1
  123. package/dist/src/reactive/index.d.ts +0 -19
  124. package/dist/src/reactive/index.d.ts.map +0 -1
  125. package/dist/src/reactive/map.d.ts +0 -28
  126. package/dist/src/reactive/map.d.ts.map +0 -1
  127. package/dist/src/reactive/memoize.d.ts +0 -28
  128. package/dist/src/reactive/memoize.d.ts.map +0 -1
  129. package/dist/src/reactive/non-reactive-state.d.ts +0 -9
  130. package/dist/src/reactive/non-reactive-state.d.ts.map +0 -1
  131. package/dist/src/reactive/non-reactive.d.ts +0 -11
  132. package/dist/src/reactive/non-reactive.d.ts.map +0 -1
  133. package/dist/src/reactive/project.d.ts +0 -40
  134. package/dist/src/reactive/project.d.ts.map +0 -1
  135. package/dist/src/reactive/proxy-state.d.ts +0 -8
  136. package/dist/src/reactive/proxy-state.d.ts.map +0 -1
  137. package/dist/src/reactive/proxy.d.ts +0 -23
  138. package/dist/src/reactive/proxy.d.ts.map +0 -1
  139. package/dist/src/reactive/record.d.ts +0 -115
  140. package/dist/src/reactive/record.d.ts.map +0 -1
  141. package/dist/src/reactive/register.d.ts +0 -125
  142. package/dist/src/reactive/register.d.ts.map +0 -1
  143. package/dist/src/reactive/registry.d.ts +0 -21
  144. package/dist/src/reactive/registry.d.ts.map +0 -1
  145. package/dist/src/reactive/set.d.ts +0 -26
  146. package/dist/src/reactive/set.d.ts.map +0 -1
  147. package/dist/src/reactive/tracking.d.ts +0 -7
  148. package/dist/src/reactive/tracking.d.ts.map +0 -1
  149. package/dist/src/reactive/types.d.ts +0 -424
  150. package/dist/src/reactive/types.d.ts.map +0 -1
  151. package/dist/src/reactive/watch.d.ts +0 -48
  152. package/dist/src/reactive/watch.d.ts.map +0 -1
  153. package/dist/src/std-decorators.d.ts +0 -45
  154. package/dist/src/std-decorators.d.ts.map +0 -1
  155. package/dist/src/utils.d.ts +0 -49
  156. package/dist/src/utils.d.ts.map +0 -1
  157. package/dist/src/zone.d.ts +0 -40
  158. package/dist/src/zone.d.ts.map +0 -1
  159. package/docs/reactive/describe.md +0 -85
  160. package/docs/reactive/project.md +0 -93
  161. package/src/async/browser.ts +0 -323
  162. package/src/async/index.ts +0 -27
  163. package/src/async/node.ts +0 -92
  164. package/src/decorator.ts +0 -272
  165. package/src/destroyable.ts +0 -199
  166. package/src/entry-browser.ts +0 -5
  167. package/src/entry-node.ts +0 -5
  168. package/src/eventful.ts +0 -110
  169. package/src/flavored.ts +0 -106
  170. package/src/index.d.ts +0 -12
  171. package/src/index.ts +0 -64
  172. package/src/indexable.ts +0 -526
  173. package/src/introspection.ts +0 -59
  174. package/src/iterableWeak.ts +0 -233
  175. package/src/mixins.ts +0 -123
  176. package/src/promiseChain.ts +0 -110
  177. package/src/reactive/array.ts +0 -500
  178. package/src/reactive/buffer.ts +0 -328
  179. package/src/reactive/change.ts +0 -131
  180. package/src/reactive/deep-touch.ts +0 -273
  181. package/src/reactive/deep-watch-state.ts +0 -82
  182. package/src/reactive/deep-watch.ts +0 -171
  183. package/src/reactive/describe.ts +0 -39
  184. package/src/reactive/effect-context.ts +0 -83
  185. package/src/reactive/effects.ts +0 -1434
  186. package/src/reactive/index.ts +0 -72
  187. package/src/reactive/map.ts +0 -142
  188. package/src/reactive/memoize.ts +0 -186
  189. package/src/reactive/non-reactive-state.ts +0 -49
  190. package/src/reactive/non-reactive.ts +0 -43
  191. package/src/reactive/project.md +0 -107
  192. package/src/reactive/project.ts +0 -430
  193. package/src/reactive/proxy-state.ts +0 -27
  194. package/src/reactive/proxy.ts +0 -282
  195. package/src/reactive/record.ts +0 -181
  196. package/src/reactive/register.ts +0 -538
  197. package/src/reactive/registry.ts +0 -72
  198. package/src/reactive/set.ts +0 -117
  199. package/src/reactive/tracking.ts +0 -41
  200. package/src/reactive/types.ts +0 -520
  201. package/src/reactive/watch.ts +0 -180
  202. package/src/std-decorators.ts +0 -256
  203. package/src/utils.ts +0 -300
  204. package/src/zone.ts +0 -142
package/dist/debug.cjs CHANGED
@@ -1,35 +1,980 @@
1
1
  'use strict';
2
2
 
3
- var debug = require('./chunks/index-VTO-b2vR.cjs');
3
+ var proxy = require('./chunks/proxy-Dtg-bJ3T.cjs');
4
4
  require('./chunks/async-core-CRLKP3l-.cjs');
5
5
 
6
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
7
+ const lineageObjects = new WeakSet();
8
+ /**
9
+ * Checks if an object is a lineage object
10
+ */
11
+ function isLineage(obj) {
12
+ return obj && typeof obj === 'object' && lineageObjects.has(obj);
13
+ }
14
+ /**
15
+ * Parses a single stack line into a structured frame.
16
+ * @param line - A line from Error.stack
17
+ */
18
+ function parseStackLine(line) {
19
+ // Node.js format: " at functionName (file:line:column)"
20
+ // or " at file:line:column"
21
+ const nodeMatch = line.match(/^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+))\)?$/);
22
+ // Browser format (Firefox/Safari): "functionName@file:line:column" or "file:line:column"
23
+ // Chrome/Edge use Node-like format but without "at" sometimes or with different prefixes
24
+ const browserMatch = line.match(/(?:(.+?)(?:\@|\(?))?(?:(.+?):(\d+):(\d+))(?:\@|\)?)$/);
25
+ const match = nodeMatch || browserMatch;
26
+ if (!match)
27
+ return null;
28
+ const [, functionName = 'anonymous', fileName, lineNumber, columnNumber] = match;
29
+ return {
30
+ functionName: functionName.trim(),
31
+ fileName,
32
+ lineNumber: parseInt(lineNumber, 10),
33
+ columnNumber: parseInt(columnNumber, 10),
34
+ raw: line.trim(),
35
+ };
36
+ }
37
+ let internalFile;
38
+ /**
39
+ * Gets the current call stack
40
+ * @param skipFrames - Number of frames to skip
41
+ * @param error - Optional error to use as source of stack
42
+ */
43
+ function getStackFrame(error = new Error()) {
44
+ if (!error.stack)
45
+ return [];
46
+ const lines = error.stack.split('\n');
47
+ const lastLine = lines.findIndex((line) => line.includes(proxy.effectMarker.enter));
48
+ if (lastLine !== -1)
49
+ lines.splice(lastLine);
50
+ const firstLine = lines.findLastIndex((line) => line.includes(proxy.effectMarker.leave));
51
+ if (firstLine !== -1)
52
+ lines.splice(0, firstLine + 1);
53
+ else {
54
+ // Dynamically identify the library's internal files if not already done
55
+ if (!internalFile && lines[1]) {
56
+ const selfFrame = parseStackLine(lines[1]);
57
+ if (selfFrame) {
58
+ internalFile = selfFrame.fileName;
59
+ }
60
+ }
61
+ // Determine the "base" directory of the library to skip other internal files
62
+ // We look for "src" or "dist" to be more specific than just the project root
63
+ const srcIndex = internalFile ? internalFile.lastIndexOf('/src/') : -1;
64
+ const distIndex = internalFile ? internalFile.lastIndexOf('/dist/') : -1;
65
+ internalFile ? (srcIndex !== -1 ? internalFile.substring(0, srcIndex + 5) :
66
+ (distIndex !== -1 ? internalFile.substring(0, distIndex + 6) :
67
+ internalFile.substring(0, internalFile.lastIndexOf('/') + 1))) : undefined;
68
+ let l;
69
+ for (l = 1; l < lines.length; l++) {
70
+ const frame = parseStackLine(lines[l]);
71
+ if (!frame)
72
+ continue;
73
+ // Robust skipping: if we are still in the internal area, skip it.
74
+ const isInternal = /Lineage$/.test(frame.functionName) ||
75
+ frame.functionName === 'eval' ||
76
+ [`getStackFrame`, `captureLineage`].includes(frame.functionName);
77
+ if (!isInternal)
78
+ break;
79
+ }
80
+ lines.splice(0, l);
81
+ }
82
+ return lines.map(parseStackLine).filter(Boolean);
83
+ }
84
+ /**
85
+ * Traces the lineage of the current execution through nested effects
86
+ * @param effect - Starting effect (defaults to active effect)
87
+ */
88
+ function getLineage(effect) {
89
+ const currentEffect = effect ?? proxy.getActiveEffect();
90
+ const currentStack = getStackFrame(); // Robustly skips internal mutts frames
91
+ const segments = [];
92
+ if (!currentEffect) {
93
+ segments.push({
94
+ effect: undefined,
95
+ effectName: 'root',
96
+ stack: filterNodeModules(currentStack),
97
+ });
98
+ return segments;
99
+ }
100
+ let current = currentEffect;
101
+ let lastStack = currentStack;
102
+ while (current) {
103
+ const rootFn = proxy.getRoot(current);
104
+ // Too aggressive for now
105
+ //if(!rootFn.name) debugger
106
+ const filteredStack = filterNodeModules(lastStack);
107
+ segments.push({
108
+ effect: rootFn,
109
+ effectName: rootFn.name || 'anonymous',
110
+ stack: filteredStack,
111
+ });
112
+ // Move to parent
113
+ const node = proxy.getEffectNode(current);
114
+ const parent = node.parent;
115
+ const creationStack = node.creationStack;
116
+ if (parent) {
117
+ current = parent;
118
+ lastStack = creationStack ?? [];
119
+ }
120
+ else if (creationStack) {
121
+ segments.push({
122
+ effect: undefined,
123
+ effectName: 'root',
124
+ stack: filterNodeModules(creationStack),
125
+ });
126
+ break;
127
+ }
128
+ else {
129
+ break;
130
+ }
131
+ }
132
+ return segments;
133
+ }
134
+ /**
135
+ * Filters out node_modules frames and groups them
136
+ * @param frames - Array of stack frames
137
+ */
138
+ function filterNodeModules(frames) {
139
+ const result = [];
140
+ let inNodeModules = false;
141
+ for (const frame of frames) {
142
+ const isNodeModule = frame.fileName.includes('/node_modules/'); //|| frame.fileName.includes('/dist/')
143
+ if (isNodeModule && !inNodeModules) {
144
+ // Start of node_modules block
145
+ inNodeModules = true;
146
+ result.push({
147
+ functionName: '...node_modules...',
148
+ fileName: '[filtered]',
149
+ lineNumber: 0,
150
+ columnNumber: 0,
151
+ raw: ' at ...node_modules...'
152
+ });
153
+ }
154
+ else if (!isNodeModule && inNodeModules) {
155
+ // End of node_modules block
156
+ inNodeModules = false;
157
+ result.push(frame);
158
+ }
159
+ else if (!isNodeModule) {
160
+ // Regular frame
161
+ result.push(frame);
162
+ }
163
+ // Skip frames inside node_modules
164
+ }
165
+ return result;
166
+ }
167
+ /**
168
+ * Formats lineage segments into a single stack-like string
169
+ * @param segments - Lineage segments
170
+ */
171
+ function formatLineage(segments) {
172
+ const result = [];
173
+ for (let i = 0; i < segments.length; i++) {
174
+ const segment = segments[i];
175
+ if (i > 0) {
176
+ const triggerEffect = segments[i - 1].effect;
177
+ const triggerName = triggerEffect ? (proxy.getRoot(triggerEffect).name || 'anonymous') : 'root';
178
+ result.push(` --- effect: ${triggerName} ---`);
179
+ }
180
+ for (const frame of segment.stack) {
181
+ result.push(` ${frame.raw}`);
182
+ }
183
+ }
184
+ return result.join('\n');
185
+ }
186
+ /**
187
+ * Logs lineage segments to console with grouping
188
+ * @param segments - Lineage segments
189
+ */
190
+ function logLineage(segments) {
191
+ console.groupCollapsed(`🦴 Effect Lineage Trace (${segments.length} segment${segments.length === 1 ? '' : 's'})`);
192
+ for (let i = 0; i < segments.length; i++) {
193
+ const segment = segments[i];
194
+ // Add segment header
195
+ const isLast = i === segments.length - 1;
196
+ const prefix = i === 0 ? '📍' : isLast ? '└─' : '├─';
197
+ console.groupCollapsed(`${prefix} Effect:`, segment.effect ? segment.effect.name || 'anonymous' : 'root');
198
+ // Add stack frames
199
+ for (let j = 0; j < segment.stack.length; j++) {
200
+ const frame = segment.stack[j];
201
+ const isLastFrame = j === segment.stack.length - 1;
202
+ const framePrefix = isLastFrame ? '└─' : '├─';
203
+ if (frame.functionName === '...node_modules...') {
204
+ console.log(`%c${framePrefix} ${frame.functionName}`, 'color: #888; font-style: italic;');
205
+ }
206
+ else {
207
+ const fnStyle = frame.functionName === 'anonymous' ? 'color: #888;' : 'color: #1a7f37; font-weight: bold;';
208
+ const fileStyle = 'color: #0550ae;';
209
+ console.log(`%c${framePrefix} %c${frame.functionName} %c(${frame.fileName}:${frame.lineNumber}:${frame.columnNumber})`, 'color: #888;', fnStyle, fileStyle);
210
+ }
211
+ }
212
+ console.groupEnd();
213
+ }
214
+ console.groupEnd();
215
+ }
216
+ /**
217
+ * Captures and logs lineage to console
218
+ */
219
+ function captureNodeLineage() {
220
+ logLineage(getLineage());
221
+ }
222
+ /**
223
+ * Custom formatter for Chrome DevTools to render lineage data nicely.
224
+ */
225
+ const lineageFormatter = {
226
+ header: (obj) => {
227
+ if (isLineage(obj)) {
228
+ // Try to detect DevTools theme - default to dark colors if uncertain
229
+ const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
230
+ const headerColor = isDark ? '#cd9d5d' : '#704214';
231
+ return [
232
+ 'span',
233
+ { style: `color: ${headerColor}; font-weight: bold;` },
234
+ `🦴 Effect Lineage (${obj.segments.length} segments)`,
235
+ ];
236
+ }
237
+ return null;
238
+ },
239
+ hasBody: (obj) => isLineage(obj),
240
+ body: (obj) => {
241
+ if (!isLineage(obj))
242
+ return null;
243
+ const segments = obj.segments;
244
+ // Try to detect DevTools theme
245
+ const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
246
+ const colors = isDark ? {
247
+ frameText: '#ccc',
248
+ functionName: '#ffffff',
249
+ segmentBg: '#2d2d2d',
250
+ } : {
251
+ frameText: '#555',
252
+ functionName: '#222',
253
+ segmentBg: '#eee',
254
+ };
255
+ const children = segments.map((segment, i) => {
256
+ const frames = segment.stack.map((frame) => [
257
+ 'div',
258
+ { style: `margin-left: 20px; color: ${colors.frameText}; font-family: monospace; font-size: 11px;` },
259
+ ['span', { style: `color: ${colors.functionName};` }, `at ${frame.functionName} `],
260
+ ['span', {}, `${frame.fileName}:${frame.lineNumber}:${frame.columnNumber}`],
261
+ ]);
262
+ segment.effect;
263
+ const segmentHeader = [
264
+ 'div',
265
+ { style: `margin-top: 5px; padding: 2px 5px; background: ${colors.segmentBg}; border-radius: 3px; font-weight: bold;` },
266
+ i === 0 ? '📍 Current: ' : '↖ Effect: ',
267
+ segment.effect?.name || 'anonymous',
268
+ segment.effect ? ['object', { object: segment.effect }] : 'root',
269
+ ];
270
+ return ['div', {}, segmentHeader, ...frames];
271
+ });
272
+ return ['div', { style: 'padding: 5px; line-height: 1.4;' }, ...children];
273
+ },
274
+ };
275
+ /**
276
+ * Wraps lineage data in a way that the Chrome Formatter can recognize.
277
+ */
278
+ function wrapLineageForDebug(segments) {
279
+ const lineage = {
280
+ segments,
281
+ toString: () => formatLineage(segments ?? getLineage()),
282
+ };
283
+ lineageObjects.add(lineage);
284
+ return lineage;
285
+ }
6
286
 
287
+ /**
288
+ * Displays a floating reactive panel in the browser showing the lineage of triggered effects.
289
+ */
290
+ function showLineagePanel() {
291
+ if (typeof document === 'undefined')
292
+ return;
293
+ // Detect color scheme preference
294
+ const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
295
+ // Color schemes
296
+ const colors = isDarkMode ? {
297
+ panelBg: 'rgba(30, 30, 30, 0.95)',
298
+ panelBorder: '#444',
299
+ headerBg: '#8B4513',
300
+ headerText: '#ffffff',
301
+ closeBtn: '#ffffff',
302
+ placeholderText: '#999',
303
+ segmentBorder: '#8B4513',
304
+ titleText: '#ffffff',
305
+ frameText: '#ccc',
306
+ atText: '#888',
307
+ linkText: '#58a6ff',
308
+ } : {
309
+ panelBg: 'rgba(255, 255, 255, 0.95)',
310
+ panelBorder: '#ddd',
311
+ headerBg: '#704214',
312
+ headerText: '#ffffff',
313
+ closeBtn: '#ffffff',
314
+ placeholderText: '#666',
315
+ segmentBorder: '#704214',
316
+ titleText: '#222',
317
+ frameText: '#555',
318
+ atText: '#999',
319
+ linkText: '#005cc5',
320
+ };
321
+ const state = proxy.reactive({
322
+ activeLineage: [],
323
+ isVisible: true,
324
+ });
325
+ // onEffectTrigger tracks "this effect" (here nothing) — not useful for a global panel.
326
+ // Would need zone-based events to track all triggers. Panel is cosmetic/experimental (260206).
327
+ // UI Creation
328
+ const panel = document.createElement('div');
329
+ panel.id = 'mutts-lineage-panel';
330
+ Object.assign(panel.style, {
331
+ position: 'fixed',
332
+ bottom: '20px',
333
+ right: '20px',
334
+ width: '400px',
335
+ maxHeight: '80vh',
336
+ backgroundColor: colors.panelBg,
337
+ backdropFilter: 'blur(10px)',
338
+ border: `1px solid ${colors.panelBorder}`,
339
+ borderRadius: '12px',
340
+ boxShadow: '0 10px 25px rgba(0,0,0,0.15)',
341
+ zIndex: '999999',
342
+ fontFamily: 'Inter, system-ui, sans-serif',
343
+ fontSize: '12px',
344
+ display: 'flex',
345
+ flexDirection: 'column',
346
+ overflow: 'hidden',
347
+ transition: 'opacity 0.3s, transform 0.3s',
348
+ });
349
+ const header = document.createElement('div');
350
+ Object.assign(header.style, {
351
+ padding: '12px 16px',
352
+ background: colors.headerBg,
353
+ color: colors.headerText,
354
+ fontWeight: '600',
355
+ display: 'flex',
356
+ justifyContent: 'space-between',
357
+ alignItems: 'center',
358
+ cursor: 'grab',
359
+ });
360
+ header.innerHTML = '<span><span style="font-size: 14px; margin-right: 5px;">🦴</span> Mutts Lineage</span>';
361
+ const closeBtn = document.createElement('button');
362
+ closeBtn.innerText = '×';
363
+ Object.assign(closeBtn.style, {
364
+ background: 'none',
365
+ border: 'none',
366
+ color: colors.closeBtn,
367
+ fontSize: '20px',
368
+ cursor: 'pointer',
369
+ padding: '0 5px',
370
+ });
371
+ closeBtn.onclick = () => {
372
+ state.isVisible = false;
373
+ };
374
+ header.appendChild(closeBtn);
375
+ panel.appendChild(header);
376
+ const content = document.createElement('div');
377
+ Object.assign(content.style, {
378
+ padding: '16px',
379
+ overflowY: 'auto',
380
+ flex: '1',
381
+ });
382
+ panel.appendChild(content);
383
+ document.body.appendChild(panel);
384
+ // Reactivity: Update Visibility
385
+ proxy.effect(() => {
386
+ panel.style.opacity = state.isVisible ? '1' : '0';
387
+ panel.style.pointerEvents = state.isVisible ? 'all' : 'none';
388
+ panel.style.transform = state.isVisible ? 'translateY(0)' : 'translateY(20px)';
389
+ });
390
+ // Reactivity: Update Content
391
+ proxy.effect(() => {
392
+ if (state.activeLineage.length === 0) {
393
+ content.innerHTML = `<div style="color: ${colors.placeholderText}; font-style: italic; text-align: center; margin-top: 20px;">No effect triggered yet...</div>`;
394
+ return;
395
+ }
396
+ content.innerHTML = '';
397
+ state.activeLineage.forEach((segment, i) => {
398
+ const segDiv = document.createElement('div');
399
+ Object.assign(segDiv.style, {
400
+ marginBottom: '16px',
401
+ borderLeft: `2px solid ${colors.segmentBorder}`,
402
+ paddingLeft: '12px',
403
+ });
404
+ const title = document.createElement('div');
405
+ Object.assign(title.style, {
406
+ fontWeight: 'bold',
407
+ marginBottom: '6px',
408
+ color: colors.titleText,
409
+ });
410
+ const effectName = segment.effect ? (proxy.getRoot(segment.effect).name || 'anonymous') : 'root';
411
+ title.innerText = i === 0 ? `📍 Current: ${effectName}` : `↖ Effect: ${effectName}`;
412
+ segDiv.appendChild(title);
413
+ segment.stack.forEach(frame => {
414
+ const frameDiv = document.createElement('div');
415
+ Object.assign(frameDiv.style, {
416
+ color: colors.frameText,
417
+ marginBottom: '3px',
418
+ fontSize: '11px',
419
+ whiteSpace: 'nowrap',
420
+ overflow: 'hidden',
421
+ textOverflow: 'ellipsis',
422
+ cursor: 'pointer',
423
+ });
424
+ frameDiv.title = frame.raw;
425
+ frameDiv.innerHTML = `<span style="color: ${colors.atText}">at</span> ${frame.functionName} <span style="color: ${colors.linkText}; text-decoration: underline;">(${frame.fileName.split('/').pop()}:${frame.lineNumber})</span>`;
426
+ segDiv.appendChild(frameDiv);
427
+ });
428
+ content.appendChild(segDiv);
429
+ });
430
+ });
431
+ }
7
432
 
8
- exports.buildReactivityGraph = debug.buildReactivityGraph;
9
- exports.captureNodeLineage = debug.captureNodeLineage;
10
- exports.debugOptions = debug.debugOptions;
11
- exports.effectMarker = debug.effectMarker;
12
- exports.enableDevTools = debug.enableDevTools;
13
- exports.forceEnableGraphTracking = debug.forceEnableGraphTracking;
14
- exports.formatLineage = debug.formatLineage;
15
- exports.getDependencies = debug.getDependencies;
16
- exports.getDependencyGraph = debug.getDependencyGraph;
17
- exports.getDependents = debug.getDependents;
18
- exports.getLineage = debug.getLineage;
19
- exports.getMutationHistory = debug.getMutationHistory;
20
- exports.getStackFrame = debug.getStackFrame;
21
- exports.getTriggerChain = debug.getTriggerChain;
22
- exports.isDevtoolsEnabled = debug.isDevtoolsEnabled;
23
- exports.lineageFormatter = debug.lineageFormatter;
24
- exports.logError = debug.logError;
25
- exports.nodeLineage = debug.nodeLineage;
26
- exports.nodeLineageLegacy = debug.nodeLineageLegacy;
27
- exports.parseStackLine = debug.parseStackLine;
28
- exports.recordTriggerLink = debug.recordTriggerLink;
29
- exports.registerEffectForDebug = debug.registerEffectForDebug;
30
- exports.registerObjectForDebug = debug.registerObjectForDebug;
31
- exports.setEffectName = debug.setEffectName;
32
- exports.setObjectName = debug.setObjectName;
33
- exports.showLineagePanel = debug.showLineagePanel;
34
- exports.wrapLineageForDebug = debug.wrapLineageForDebug;
433
+ /**
434
+ * Debug utilities for the reactivity system
435
+ * - Captures effect metadata (names, parent relationships)
436
+ * - Records cause → consequence edges with object/prop labels
437
+ * - Provides graph data for tooling (DevTools panel, etc.)
438
+ */
439
+ /**
440
+ * Log an error with detailed context if error logging is enabled
441
+ */
442
+ function logError(error, context, effect) {
443
+ if (!proxy.options.introspection?.logErrors)
444
+ return;
445
+ const contextStr = context === 'throw' ? '🔴 Thrown' : '🟡 Caught';
446
+ console.group(`${contextStr} Error: ${error.message}`);
447
+ console.error('Error:', error);
448
+ // Try to get effect name if available
449
+ if (effect && typeof effect === 'object' && effect.name) {
450
+ console.log('Effect:', effect.name);
451
+ }
452
+ console.groupEnd();
453
+ }
454
+ // Log initialization for debugging
455
+ const EXTERNAL_SOURCE = Symbol('external-source');
456
+ let devtoolsEnabled = false;
457
+ // Registry for debugging (populated lazily when DevTools are enabled)
458
+ const debugEffectRegistry = new Set();
459
+ const debugObjectRegistry = new Set();
460
+ // Human-friendly names
461
+ const effectNames = new WeakMap();
462
+ const objectNames = new WeakMap();
463
+ let effectCounter = 0;
464
+ let objectCounter = 0;
465
+ const triggerGraph = new Map();
466
+ function ensureEffectName(effect) {
467
+ let name = effectNames.get(effect);
468
+ if (!name) {
469
+ const root = proxy.getRoot(effect);
470
+ name = root?.name?.trim() || `effect_${++effectCounter}`;
471
+ }
472
+ effectNames.set(effect, name);
473
+ return name;
474
+ }
475
+ function ensureObjectName(obj) {
476
+ let name = objectNames.get(obj);
477
+ if (!name) {
478
+ // Try Symbol.toStringTag first (standard way to customize object string representation)
479
+ const toStringTag = obj?.[Symbol.toStringTag];
480
+ const ctorName = obj?.constructor?.name;
481
+ const base = toStringTag || (ctorName && ctorName !== 'Object' ? ctorName : 'object');
482
+ name = `${base}_${++objectCounter}`;
483
+ objectNames.set(obj, name);
484
+ }
485
+ return name;
486
+ }
487
+ function describeProp(obj, prop) {
488
+ const objectName = ensureObjectName(obj);
489
+ if (prop === proxy.allProps)
490
+ return `${objectName}.*`;
491
+ if (typeof prop === 'symbol')
492
+ return `${objectName}.${prop.description ?? prop.toString()}`;
493
+ return `${objectName}.${String(prop)}`;
494
+ }
495
+ function addEffectToRegistry(effect) {
496
+ if (!effect || debugEffectRegistry.has(effect))
497
+ return;
498
+ debugEffectRegistry.add(effect);
499
+ const deps = proxy.effectToReactiveObjects.get(effect);
500
+ if (deps) {
501
+ for (const obj of deps) {
502
+ documentObject(obj);
503
+ }
504
+ }
505
+ }
506
+ function documentObject(obj) {
507
+ if (!debugObjectRegistry.has(obj)) {
508
+ dbRegisterObject(obj);
509
+ }
510
+ }
511
+ function dbRegisterObject(obj) {
512
+ debugObjectRegistry.add(obj);
513
+ ensureObjectName(obj);
514
+ }
515
+ function ensureParentChains(effects) {
516
+ const queue = Array.from(effects);
517
+ for (let i = 0; i < queue.length; i++) {
518
+ const effect = queue[i];
519
+ const parent = proxy.getEffectNode(effect).parent;
520
+ if (parent && !effects.has(parent)) {
521
+ effects.add(parent);
522
+ queue.push(parent);
523
+ }
524
+ }
525
+ }
526
+ function ensureTriggerContainers(source) {
527
+ let targetMap = triggerGraph.get(source);
528
+ if (!targetMap) {
529
+ targetMap = new Map();
530
+ triggerGraph.set(source, targetMap);
531
+ }
532
+ return targetMap;
533
+ }
534
+ function ensureTriggerRecord(source, target, label, obj, prop, evolution) {
535
+ const targetMap = ensureTriggerContainers(source);
536
+ let labelMap = targetMap.get(target);
537
+ if (!labelMap) {
538
+ labelMap = new Map();
539
+ targetMap.set(target, labelMap);
540
+ }
541
+ let record = labelMap.get(label);
542
+ if (!record) {
543
+ record = { label, object: obj, prop, evolution, count: 0, lastTriggered: Date.now() };
544
+ labelMap.set(label, record);
545
+ }
546
+ return record;
547
+ }
548
+ /**
549
+ * Assign a debug-friendly name to an effect (shown in DevTools)
550
+ */
551
+ function setEffectName(effect, name) {
552
+ effectNames.set(effect, name);
553
+ }
554
+ /**
555
+ * Assign a debug-friendly name to a reactive object
556
+ */
557
+ function setObjectName(obj, name) {
558
+ objectNames.set(obj, name);
559
+ debugObjectRegistry.add(obj);
560
+ }
561
+ /**
562
+ * Register an effect so it appears in the DevTools graph
563
+ */
564
+ function registerEffectForDebug(effect) {
565
+ if (!effect || !devtoolsEnabled)
566
+ return;
567
+ addEffectToRegistry(effect);
568
+ }
569
+ /**
570
+ * Register a reactive object so it appears in the DevTools graph
571
+ */
572
+ function registerObjectForDebug(obj) {
573
+ if (!devtoolsEnabled)
574
+ return;
575
+ documentObject(obj);
576
+ }
577
+ /**
578
+ * Records a cause → consequence relationship between effects.
579
+ * @param source - The effect performing the write (undefined if external/user input)
580
+ * @param target - The effect that re-ran because of the write
581
+ * @param obj - The reactive object that changed
582
+ * @param prop - The property that changed
583
+ * @param evolution - The type of change (set/add/del/bunch)
584
+ */
585
+ function recordTriggerLink(source, target, obj, prop, evolution) {
586
+ if (proxy.options.introspection?.enableHistory) {
587
+ addToMutationHistory(source, target, obj, prop, evolution);
588
+ }
589
+ if (!devtoolsEnabled)
590
+ return;
591
+ addEffectToRegistry(target);
592
+ if (source)
593
+ addEffectToRegistry(source);
594
+ const descriptor = describeProp(obj, prop);
595
+ const record = ensureTriggerRecord(source ?? EXTERNAL_SOURCE, target, descriptor, obj, prop, evolution);
596
+ record.count += 1;
597
+ record.lastTriggered = Date.now();
598
+ documentObject(obj);
599
+ }
600
+ /**
601
+ * Traces back the chain of triggers that led to a specific effect
602
+ * @param effect The effect to trace back
603
+ * @param limit Max depth
604
+ */
605
+ function getTriggerChain(effect, limit = 5) {
606
+ const chain = [];
607
+ let current = effect;
608
+ for (let i = 0; i < limit; i++) {
609
+ // Find who triggered 'current'
610
+ // We need to reverse search the triggerGraph (source -> target)
611
+ // This is expensive O(Edges) but okay for error reporting
612
+ let foundSource;
613
+ let foundReason = '';
614
+ search: for (const [source, targetMap] of triggerGraph) {
615
+ for (const [target, labelMap] of targetMap) {
616
+ if (target === current) {
617
+ // Found a source! Use the most recent trigger record
618
+ let lastTime = 0;
619
+ for (const record of labelMap.values()) {
620
+ if (record.lastTriggered > lastTime) {
621
+ lastTime = record.lastTriggered;
622
+ foundReason = record.label;
623
+ foundSource = source === EXTERNAL_SOURCE ? undefined : source;
624
+ }
625
+ }
626
+ if (foundSource || foundReason)
627
+ break search;
628
+ }
629
+ }
630
+ }
631
+ if (foundSource) {
632
+ chain.push(`${ensureEffectName(foundSource)} -> (${foundReason}) -> ${ensureEffectName(current)}`);
633
+ current = foundSource;
634
+ }
635
+ else if (foundReason) {
636
+ chain.push(`External -> (${foundReason}) -> ${ensureEffectName(current)}`);
637
+ break;
638
+ }
639
+ else {
640
+ break;
641
+ }
642
+ }
643
+ return chain.reverse();
644
+ }
645
+ function buildEffectNodes(allEffects) {
646
+ const nodes = [];
647
+ const nodeByEffect = new Map();
648
+ const ordered = Array.from(allEffects);
649
+ for (const effect of ordered) {
650
+ const label = ensureEffectName(effect);
651
+ const node = {
652
+ id: `effect_${nodes.length}`,
653
+ label,
654
+ type: 'effect',
655
+ depth: 0,
656
+ debugName: label,
657
+ };
658
+ nodes.push(node);
659
+ nodeByEffect.set(effect, node);
660
+ }
661
+ const depthCache = new Map();
662
+ const computeDepth = (effect) => {
663
+ if (!effect)
664
+ return 0;
665
+ const cached = depthCache.get(effect);
666
+ if (cached !== undefined)
667
+ return cached;
668
+ const parent = proxy.getEffectNode(effect).parent;
669
+ const depth = computeDepth(parent) + (parent ? 1 : 0);
670
+ depthCache.set(effect, depth);
671
+ return depth;
672
+ };
673
+ for (const [effect, node] of nodeByEffect) {
674
+ node.depth = computeDepth(effect);
675
+ const parent = proxy.getEffectNode(effect).parent;
676
+ if (parent) {
677
+ const parentNode = nodeByEffect.get(parent);
678
+ if (parentNode) {
679
+ node.parentId = parentNode.id;
680
+ }
681
+ }
682
+ }
683
+ return { nodes, nodeByEffect };
684
+ }
685
+ /**
686
+ * Builds a graph representing current reactive state (effects, objects, and trigger edges)
687
+ */
688
+ function buildReactivityGraph() {
689
+ const nodes = [];
690
+ const edges = [];
691
+ const nodeIds = new Map();
692
+ const allEffects = new Set(debugEffectRegistry);
693
+ ensureParentChains(allEffects);
694
+ const { nodes: effectNodes, nodeByEffect } = buildEffectNodes(allEffects);
695
+ for (const node of effectNodes)
696
+ nodes.push(node);
697
+ for (const [effect, node] of nodeByEffect) {
698
+ nodeIds.set(effect, node.id);
699
+ }
700
+ // Object nodes (optional, used for dependency inspection)
701
+ for (const obj of debugObjectRegistry) {
702
+ const id = `object_${nodes.length}`;
703
+ nodes.push({ id, label: ensureObjectName(obj), type: 'state', debugName: objectNames.get(obj) });
704
+ nodeIds.set(obj, id);
705
+ }
706
+ // External source node (user/system outside of effects)
707
+ if (triggerGraph.has(EXTERNAL_SOURCE)) {
708
+ const externalId = `effect_external`;
709
+ nodes.push({ id: externalId, label: 'External', type: 'external', depth: 0 });
710
+ nodeIds.set(EXTERNAL_SOURCE, externalId);
711
+ }
712
+ // Dependency edges (effect → object)
713
+ for (const effect of allEffects) {
714
+ const effectId = nodeIds.get(effect);
715
+ if (!effectId)
716
+ continue;
717
+ const deps = proxy.effectToReactiveObjects.get(effect);
718
+ if (!deps)
719
+ continue;
720
+ for (const obj of deps) {
721
+ const objId = nodeIds.get(obj);
722
+ if (!objId)
723
+ continue;
724
+ edges.push({
725
+ id: `${effectId}->${objId}`,
726
+ source: effectId,
727
+ target: objId,
728
+ type: 'dependency',
729
+ label: 'depends',
730
+ });
731
+ }
732
+ }
733
+ // Cause edges (effect/object/prop → effect)
734
+ for (const [source, targetMap] of triggerGraph) {
735
+ for (const [targetEffect, labelMap] of targetMap) {
736
+ const targetId = nodeIds.get(targetEffect);
737
+ if (!targetId)
738
+ continue;
739
+ const sourceId = nodeIds.get(source);
740
+ if (!sourceId)
741
+ continue;
742
+ for (const record of labelMap.values()) {
743
+ edges.push({
744
+ id: `${sourceId}->${targetId}:${record.label}`,
745
+ source: sourceId,
746
+ target: targetId,
747
+ type: 'cause',
748
+ label: record.count > 1 ? `${record.label} (${record.count})` : record.label,
749
+ count: record.count,
750
+ });
751
+ }
752
+ }
753
+ }
754
+ return {
755
+ nodes,
756
+ edges,
757
+ meta: {
758
+ generatedAt: Date.now(),
759
+ devtoolsEnabled,
760
+ },
761
+ };
762
+ }
763
+ const captureLineage = () => wrapLineageForDebug(getLineage());
764
+ /**
765
+ * Enables the DevTools bridge and exposes the debug API on window/global.
766
+ * Call as early as possible in development builds.
767
+ */
768
+ function enableDevTools() {
769
+ const globalScope = (typeof globalThis !== 'undefined'
770
+ ? globalThis
771
+ : typeof window !== 'undefined'
772
+ ? window
773
+ : typeof global !== 'undefined'
774
+ ? global
775
+ : undefined);
776
+ if (!globalScope)
777
+ return;
778
+ if (devtoolsEnabled)
779
+ return;
780
+ devtoolsEnabled = true;
781
+ if (proxy.options.introspection) {
782
+ proxy.options.introspection.logErrors = true;
783
+ }
784
+ globalScope.__MUTTS_DEBUG__ = {
785
+ getGraph: buildReactivityGraph,
786
+ logLineage(tag) {
787
+ logLineage(getLineage());
788
+ return '🦴 Effect Lineage Trace' + (tag ? ` (${tag})` : '');
789
+ },
790
+ get browserLineage() {
791
+ return captureLineage();
792
+ },
793
+ getLineage,
794
+ captureLineage: getStackFrame,
795
+ formatLineage,
796
+ showLineagePanel,
797
+ setEffectName,
798
+ setObjectName,
799
+ registerEffect: registerEffectForDebug,
800
+ registerObject: registerObjectForDebug,
801
+ };
802
+ // @ts-ignore - devtoolsFormatters is a Chrome-specific array
803
+ if (globalScope.devtoolsFormatters) {
804
+ globalScope.devtoolsFormatters.push(lineageFormatter);
805
+ }
806
+ else {
807
+ globalScope.devtoolsFormatters = [lineageFormatter];
808
+ }
809
+ proxy.setDebugHooks({
810
+ isDevtoolsEnabled: () => devtoolsEnabled,
811
+ registerEffect: registerEffectForDebug,
812
+ getTriggerChain: getTriggerChain,
813
+ captureStack: getStackFrame,
814
+ captureLineage,
815
+ formatStack: (stack) => {
816
+ if (typeof stack === 'string') {
817
+ return [
818
+ `\n ${stack
819
+ .split('\n')
820
+ .slice(1)
821
+ .map((l) => l.trim())
822
+ .join('\n ')}`,
823
+ ];
824
+ }
825
+ if (isLineage(stack)) {
826
+ return ['\n', stack];
827
+ }
828
+ return ['\n', stack];
829
+ },
830
+ recordTriggerLink: recordTriggerLink,
831
+ });
832
+ }
833
+ function forceEnableGraphTracking() {
834
+ devtoolsEnabled = true;
835
+ }
836
+ function isDevtoolsEnabled() {
837
+ return devtoolsEnabled;
838
+ }
839
+ /**
840
+ * Returns the raw dependency graph data structure.
841
+ * This is useful for programmatic analysis of the reactive system.
842
+ */
843
+ function getDependencyGraph() {
844
+ return {
845
+ nodes: buildReactivityGraph().nodes,
846
+ edges: buildReactivityGraph().edges,
847
+ };
848
+ }
849
+ /**
850
+ * Capture a complete snapshot of the current reactive state
851
+ */
852
+ function snapshot() {
853
+ return {
854
+ graph: getDependencyGraph(),
855
+ history: getMutationHistory(),
856
+ timestamp: Date.now(),
857
+ };
858
+ }
859
+ /**
860
+ * Returns a list of effects that depend on the given object.
861
+ */
862
+ function getDependents(obj) {
863
+ const dependents = [];
864
+ // Scan the trigger graph for effects triggered by this object
865
+ // This is O(E) where E is the number of edges, might need optimization for large graphs
866
+ // but acceptable for introspection
867
+ for (const [_source, targetMap] of triggerGraph) {
868
+ for (const [targetEffect, labelMap] of targetMap) {
869
+ for (const record of labelMap.values()) {
870
+ if (record.object === obj) {
871
+ dependents.push(targetEffect);
872
+ }
873
+ }
874
+ }
875
+ }
876
+ // Also check direct dependencies (dependency graph)
877
+ // We don't have a direct obj -> effect map without walking all effects
878
+ // unless we use `watchers` from tracking.ts but that's internal
879
+ return [...new Set(dependents)];
880
+ }
881
+ /**
882
+ * Returns a list of objects that the given effect depends on.
883
+ */
884
+ function getDependencies(effect) {
885
+ const deps = proxy.effectToReactiveObjects.get(effect);
886
+ return deps ? Array.from(deps) : [];
887
+ }
888
+ const mutationHistory = [];
889
+ let mutationCounter = 0;
890
+ function addToMutationHistory(source, target, obj, prop, evolution) {
891
+ const record = {
892
+ id: ++mutationCounter,
893
+ timestamp: Date.now(),
894
+ source: source ? ensureEffectName(source) : 'External',
895
+ target: ensureEffectName(target),
896
+ objectName: ensureObjectName(obj),
897
+ prop: String(prop),
898
+ type: evolution.type,
899
+ };
900
+ mutationHistory.push(record);
901
+ if (mutationHistory.length > (proxy.options.introspection?.historySize ?? 50)) {
902
+ mutationHistory.shift();
903
+ }
904
+ }
905
+ /**
906
+ * Get the recent mutation history
907
+ */
908
+ function getMutationHistory() {
909
+ return [...mutationHistory];
910
+ }
911
+ // --- Auto DevTools Initialization ---
912
+ // Automatically enable devtools in development environments
913
+ /**
914
+ * Checks if we're in a development environment
915
+ * Detection order:
916
+ * 1. process.env.NODE_ENV (Node.js)
917
+ * 2. import.meta.env.DEV (Vite)
918
+ * 3. import.meta.env.PROD === false (Vite alternative)
919
+ * 4. Assumes development if none of the above are set (safe default)
920
+ */
921
+ function isDevelopmentMode() {
922
+ // Check for explicit production flag first
923
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'production') {
924
+ return false;
925
+ }
926
+ // Check for Vite's DEV flag
927
+ if (typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('debug.cjs', document.baseURI).href)) }) !== 'undefined') {
928
+ const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('debug.cjs', document.baseURI).href)) });
929
+ if (meta.env?.PROD === true) {
930
+ return false;
931
+ }
932
+ if (meta.env?.DEV === true) {
933
+ return true;
934
+ }
935
+ }
936
+ // Check for custom global override
937
+ if (typeof globalThis !== 'undefined' && '__MUTTS_DEV_MODE__' in globalThis) {
938
+ return globalThis.__MUTTS_DEV_MODE__ !== false;
939
+ }
940
+ // Default to development (safer to enable in dev than disable in prod)
941
+ return true;
942
+ }
943
+ // Auto-enable devtools when the module loads in development
944
+ if (isDevelopmentMode() && !devtoolsEnabled) {
945
+ enableDevTools();
946
+ // Optional: Log that devtools were enabled (only in development)
947
+ if (typeof console !== 'undefined' && console.info) {
948
+ // Do not log these in when mode === 'test'
949
+ console.info('🦴 Mutts DevTools enabled automatically in development mode');
950
+ }
951
+ }
952
+
953
+ exports.buildReactivityGraph = buildReactivityGraph;
954
+ exports.captureLineage = captureLineage;
955
+ exports.captureNodeLineage = captureNodeLineage;
956
+ exports.enableDevTools = enableDevTools;
957
+ exports.forceEnableGraphTracking = forceEnableGraphTracking;
958
+ exports.formatLineage = formatLineage;
959
+ exports.getDependencies = getDependencies;
960
+ exports.getDependencyGraph = getDependencyGraph;
961
+ exports.getDependents = getDependents;
962
+ exports.getLineage = getLineage;
963
+ exports.getMutationHistory = getMutationHistory;
964
+ exports.getStackFrame = getStackFrame;
965
+ exports.getTriggerChain = getTriggerChain;
966
+ exports.isDevtoolsEnabled = isDevtoolsEnabled;
967
+ exports.isLineage = isLineage;
968
+ exports.lineageFormatter = lineageFormatter;
969
+ exports.logError = logError;
970
+ exports.logLineage = logLineage;
971
+ exports.parseStackLine = parseStackLine;
972
+ exports.recordTriggerLink = recordTriggerLink;
973
+ exports.registerEffectForDebug = registerEffectForDebug;
974
+ exports.registerObjectForDebug = registerObjectForDebug;
975
+ exports.setEffectName = setEffectName;
976
+ exports.setObjectName = setObjectName;
977
+ exports.showLineagePanel = showLineagePanel;
978
+ exports.snapshot = snapshot;
979
+ exports.wrapLineageForDebug = wrapLineageForDebug;
35
980
  //# sourceMappingURL=debug.cjs.map