mutts 1.0.9 → 1.0.11

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