mutts 1.0.12 → 1.0.14
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.
- package/BROWSER_ASYNC_POLYFILL.md +79 -0
- package/README.md +7 -4
- package/dist/browser.cjs +150 -27
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +1440 -2
- package/dist/browser.dev.cjs +17 -3
- package/dist/browser.dev.cjs.map +1 -1
- package/dist/browser.dev.d.ts +2 -2
- package/dist/browser.dev.esm.js +2 -2
- package/dist/browser.esm.js +137 -28
- package/dist/browser.esm.js.map +1 -1
- package/dist/chunks/{index-yK0HVxHv.cjs → index-BnTNC9eC.cjs} +347 -156
- package/dist/chunks/index-BnTNC9eC.cjs.map +1 -0
- package/dist/chunks/{index-BUop6B2U.esm.js → index-CAWVZL7P.esm.js} +345 -154
- package/dist/chunks/index-CAWVZL7P.esm.js.map +1 -0
- package/dist/chunks/node-Df_5r_WA.cjs +187 -0
- package/dist/chunks/node-Df_5r_WA.cjs.map +1 -0
- package/dist/chunks/node-DuIduHw3.esm.js +185 -0
- package/dist/chunks/node-DuIduHw3.esm.js.map +1 -0
- package/dist/chunks/{proxy-D2C49sXH.esm.js → proxy-C2lnvvbx.esm.js} +943 -272
- package/dist/chunks/proxy-C2lnvvbx.esm.js.map +1 -0
- package/dist/chunks/{proxy-BvM4yewA.cjs → proxy-HA_QQnd5.cjs} +959 -273
- package/dist/chunks/proxy-HA_QQnd5.cjs.map +1 -0
- package/dist/debug.cjs +571 -173
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.d.ts +96 -80
- package/dist/debug.esm.js +567 -173
- package/dist/debug.esm.js.map +1 -1
- package/dist/devtools/panel.js.map +1 -1
- package/dist/mutts.umd.js +4351 -3366
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.cjs +18 -4
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.dev.cjs +18 -4
- package/dist/node.dev.cjs.map +1 -1
- package/dist/node.dev.d.ts +2 -2
- package/dist/node.dev.esm.js +3 -3
- package/dist/node.esm.js +3 -3
- package/dist/{types-Bx2PhORg.d.ts → types.d.ts} +42 -15
- package/docs/ai/api-reference.md +105 -13
- package/docs/ai/manual.md +77 -29
- package/docs/debug-getReason.md +161 -0
- package/docs/flavored.md +98 -1
- package/docs/reactive/advanced.md +184 -12
- package/docs/reactive/attend.md +32 -0
- package/docs/reactive/core.md +40 -6
- package/docs/reactive/debugging.md +40 -15
- package/docs/reactive.md +4 -1
- package/package.json +13 -9
- package/dist/chunks/index-BUop6B2U.esm.js.map +0 -1
- package/dist/chunks/index-yK0HVxHv.cjs.map +0 -1
- package/dist/chunks/node-Bo7WU5S2.esm.js +0 -96
- package/dist/chunks/node-Bo7WU5S2.esm.js.map +0 -1
- package/dist/chunks/node-Dd0esp5F.cjs +0 -98
- package/dist/chunks/node-Dd0esp5F.cjs.map +0 -1
- package/dist/chunks/proxy-BvM4yewA.cjs.map +0 -1
- package/dist/chunks/proxy-D2C49sXH.esm.js.map +0 -1
- package/dist/index.d.ts +0 -1322
package/dist/debug.cjs
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var proxy = require('./chunks/proxy-
|
|
3
|
+
var proxy = require('./chunks/proxy-HA_QQnd5.cjs');
|
|
4
4
|
|
|
5
5
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
6
6
|
const lineageObjects = new WeakSet();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*/
|
|
7
|
+
const lineageSegmentObjects = new WeakSet();
|
|
8
|
+
const lineageFrameObjects = new WeakSet();
|
|
10
9
|
function isLineage(obj) {
|
|
11
|
-
return obj && typeof obj === 'object' && lineageObjects.has(obj);
|
|
10
|
+
return !!obj && typeof obj === 'object' && lineageObjects.has(obj);
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
function isLineageSegmentView(obj) {
|
|
13
|
+
return !!obj && typeof obj === 'object' && lineageSegmentObjects.has(obj);
|
|
14
|
+
}
|
|
15
|
+
function isLineageFrameView(obj) {
|
|
16
|
+
return !!obj && typeof obj === 'object' && lineageFrameObjects.has(obj);
|
|
17
|
+
}
|
|
18
|
+
let internalFile;
|
|
17
19
|
function parseStackLine(line) {
|
|
18
|
-
// Node.js format: " at functionName (file:line:column)"
|
|
19
|
-
// or " at file:line:column"
|
|
20
20
|
const nodeMatch = line.match(/^\s*at\s+(?:(.+?)\s+\()?(?:(.+?):(\d+):(\d+))\)?$/);
|
|
21
|
-
// Browser format (Firefox/Safari): "functionName@file:line:column" or "file:line:column"
|
|
22
|
-
// Chrome/Edge use Node-like format but without "at" sometimes or with different prefixes
|
|
23
21
|
const browserMatch = line.match(/(?:(.+?)(?:\@|\(?))?(?:(.+?):(\d+):(\d+))(?:\@|\)?)$/);
|
|
24
22
|
const match = nodeMatch || browserMatch;
|
|
25
23
|
if (!match)
|
|
@@ -33,16 +31,23 @@ function parseStackLine(line) {
|
|
|
33
31
|
raw: line.trim(),
|
|
34
32
|
};
|
|
35
33
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
function extractRawStack(error = new Error()) {
|
|
35
|
+
if (typeof error === 'string')
|
|
36
|
+
return error;
|
|
37
|
+
if (error && typeof error === 'object' && 'stack' in error) {
|
|
38
|
+
const stack = error.stack;
|
|
39
|
+
return typeof stack === 'string' ? stack : undefined;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
function getStackFrame(error) {
|
|
44
|
+
return extractRawStack(error);
|
|
45
|
+
}
|
|
46
|
+
function parseStackFrames(stack) {
|
|
47
|
+
const rawStack = extractRawStack(stack);
|
|
48
|
+
if (!rawStack)
|
|
44
49
|
return [];
|
|
45
|
-
const lines =
|
|
50
|
+
const lines = rawStack.split('\n');
|
|
46
51
|
const lastLine = lines.findIndex((line) => line.includes(proxy.effectMarker.enter));
|
|
47
52
|
if (lastLine !== -1)
|
|
48
53
|
lines.splice(lastLine);
|
|
@@ -50,150 +55,164 @@ function getStackFrame(error = new Error()) {
|
|
|
50
55
|
if (firstLine !== -1)
|
|
51
56
|
lines.splice(0, firstLine + 1);
|
|
52
57
|
else {
|
|
53
|
-
// Dynamically identify the library's internal files if not already done
|
|
54
58
|
if (!internalFile && lines[1]) {
|
|
55
59
|
const selfFrame = parseStackLine(lines[1]);
|
|
56
|
-
if (selfFrame)
|
|
60
|
+
if (selfFrame)
|
|
57
61
|
internalFile = selfFrame.fileName;
|
|
58
|
-
}
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const srcIndex = internalFile ? internalFile.lastIndexOf('/src/') : -1;
|
|
63
|
-
const distIndex = internalFile ? internalFile.lastIndexOf('/dist/') : -1;
|
|
64
|
-
internalFile ? (srcIndex !== -1 ? internalFile.substring(0, srcIndex + 5) :
|
|
65
|
-
(distIndex !== -1 ? internalFile.substring(0, distIndex + 6) :
|
|
66
|
-
internalFile.substring(0, internalFile.lastIndexOf('/') + 1))) : undefined;
|
|
67
|
-
let l;
|
|
68
|
-
for (l = 1; l < lines.length; l++) {
|
|
63
|
+
let l = 1;
|
|
64
|
+
for (; l < lines.length; l++) {
|
|
69
65
|
const frame = parseStackLine(lines[l]);
|
|
70
66
|
if (!frame)
|
|
71
67
|
continue;
|
|
72
|
-
// Robust skipping: if we are still in the internal area, skip it.
|
|
73
68
|
const isInternal = /Lineage$/.test(frame.functionName) ||
|
|
74
69
|
frame.functionName === 'eval' ||
|
|
75
|
-
[`getStackFrame`, `captureLineage`].includes(frame.functionName);
|
|
70
|
+
[`getStackFrame`, `captureLineage`, `getLineage`, `digestLineage`, `formatLineage`].includes(frame.functionName);
|
|
76
71
|
if (!isInternal)
|
|
77
72
|
break;
|
|
78
73
|
}
|
|
79
74
|
lines.splice(0, l);
|
|
80
75
|
}
|
|
81
|
-
return lines.map(parseStackLine).filter(
|
|
76
|
+
return filterNodeModules(lines.map(parseStackLine).filter((frame) => !!frame));
|
|
82
77
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
function filterNodeModules(frames) {
|
|
79
|
+
const result = [];
|
|
80
|
+
let inNodeModules = false;
|
|
81
|
+
for (const frame of frames) {
|
|
82
|
+
const isNodeModule = frame.fileName.includes('/node_modules/');
|
|
83
|
+
if (isNodeModule && !inNodeModules) {
|
|
84
|
+
inNodeModules = true;
|
|
85
|
+
result.push({
|
|
86
|
+
functionName: '...node_modules...',
|
|
87
|
+
fileName: '[filtered]',
|
|
88
|
+
lineNumber: 0,
|
|
89
|
+
columnNumber: 0,
|
|
90
|
+
raw: 'at ...node_modules...',
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (!isNodeModule && inNodeModules)
|
|
95
|
+
inNodeModules = false;
|
|
96
|
+
if (!isNodeModule)
|
|
97
|
+
result.push(frame);
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
function formatEffectPreview(effect) {
|
|
102
|
+
if (!effect)
|
|
103
|
+
return 'root';
|
|
104
|
+
return `[effect ${effect.name || 'anonymous'}]`;
|
|
105
|
+
}
|
|
106
|
+
function formatSegmentTitle(segment, index, total) {
|
|
107
|
+
const isLast = index === total - 1;
|
|
108
|
+
const prefix = index === 0 ? '📍' : isLast ? '└─' : '├─';
|
|
109
|
+
return `${prefix} Effect: ${segment.effect?.name || 'root'}`;
|
|
110
|
+
}
|
|
111
|
+
function wrapLineageSegmentView(segment, index, total) {
|
|
112
|
+
const view = { index, total, segment };
|
|
113
|
+
lineageSegmentObjects.add(view);
|
|
114
|
+
return view;
|
|
115
|
+
}
|
|
116
|
+
function wrapLineageFrameView(frame) {
|
|
117
|
+
const view = { frame };
|
|
118
|
+
lineageFrameObjects.add(view);
|
|
119
|
+
return view;
|
|
120
|
+
}
|
|
121
|
+
function formatLocationHref(frame) {
|
|
122
|
+
return `${frame.fileName}:${frame.lineNumber}:${frame.columnNumber}`;
|
|
123
|
+
}
|
|
124
|
+
function ensureSignature(signature) {
|
|
125
|
+
if (!isLineage(signature)) {
|
|
126
|
+
Object.defineProperty(signature, 'toString', {
|
|
127
|
+
value: () => formatLineage(signature),
|
|
128
|
+
enumerable: false,
|
|
129
|
+
configurable: true,
|
|
130
|
+
});
|
|
131
|
+
lineageObjects.add(signature);
|
|
132
|
+
}
|
|
133
|
+
return signature;
|
|
134
|
+
}
|
|
135
|
+
function getLineage(effect, currentStack = new Error()) {
|
|
136
|
+
return ensureSignature({
|
|
137
|
+
effect: effect ?? proxy.getActiveEffect(),
|
|
138
|
+
stack: currentStack,
|
|
139
|
+
toString() {
|
|
140
|
+
return formatLineage(this);
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function captureLineage(effect, currentStack) {
|
|
145
|
+
return getLineage(effect, currentStack ?? new Error());
|
|
146
|
+
}
|
|
147
|
+
function digestLineage(lineage) {
|
|
148
|
+
if (lineage.digested)
|
|
149
|
+
return lineage.digested;
|
|
150
|
+
const currentEffect = lineage.effect;
|
|
151
|
+
const currentStack = parseStackFrames(lineage.stack);
|
|
89
152
|
const segments = [];
|
|
90
153
|
if (!currentEffect) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
154
|
+
lineage.digested = [
|
|
155
|
+
{
|
|
156
|
+
effect: undefined,
|
|
157
|
+
effectName: 'root',
|
|
158
|
+
stack: currentStack,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
return lineage.digested;
|
|
97
162
|
}
|
|
98
163
|
let current = currentEffect;
|
|
99
164
|
let lastStack = currentStack;
|
|
100
165
|
while (current) {
|
|
101
166
|
const rootFn = proxy.getRoot(current);
|
|
102
|
-
// Too aggressive for now
|
|
103
|
-
//if(!rootFn.name) debugger
|
|
104
|
-
const filteredStack = filterNodeModules(lastStack);
|
|
105
167
|
segments.push({
|
|
106
168
|
effect: rootFn,
|
|
107
169
|
effectName: rootFn.name || 'anonymous',
|
|
108
|
-
stack:
|
|
170
|
+
stack: lastStack,
|
|
109
171
|
});
|
|
110
|
-
// Move to parent
|
|
111
172
|
const node = proxy.getEffectNode(current);
|
|
112
173
|
const parent = node.parent;
|
|
113
|
-
const creationStack = node.creationStack;
|
|
174
|
+
const creationStack = parseStackFrames(node.creationStack);
|
|
114
175
|
if (parent) {
|
|
115
176
|
current = parent;
|
|
116
|
-
lastStack = creationStack
|
|
177
|
+
lastStack = creationStack;
|
|
117
178
|
}
|
|
118
|
-
else if (creationStack) {
|
|
179
|
+
else if (node.creationStack) {
|
|
119
180
|
segments.push({
|
|
120
181
|
effect: undefined,
|
|
121
182
|
effectName: 'root',
|
|
122
|
-
stack:
|
|
183
|
+
stack: creationStack,
|
|
123
184
|
});
|
|
124
185
|
break;
|
|
125
186
|
}
|
|
126
|
-
else
|
|
187
|
+
else
|
|
127
188
|
break;
|
|
128
|
-
}
|
|
129
189
|
}
|
|
190
|
+
lineage.digested = segments;
|
|
130
191
|
return segments;
|
|
131
192
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* @param frames - Array of stack frames
|
|
135
|
-
*/
|
|
136
|
-
function filterNodeModules(frames) {
|
|
137
|
-
const result = [];
|
|
138
|
-
let inNodeModules = false;
|
|
139
|
-
for (const frame of frames) {
|
|
140
|
-
const isNodeModule = frame.fileName.includes('/node_modules/'); //|| frame.fileName.includes('/dist/')
|
|
141
|
-
if (isNodeModule && !inNodeModules) {
|
|
142
|
-
// Start of node_modules block
|
|
143
|
-
inNodeModules = true;
|
|
144
|
-
result.push({
|
|
145
|
-
functionName: '...node_modules...',
|
|
146
|
-
fileName: '[filtered]',
|
|
147
|
-
lineNumber: 0,
|
|
148
|
-
columnNumber: 0,
|
|
149
|
-
raw: ' at ...node_modules...'
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
else if (!isNodeModule && inNodeModules) {
|
|
153
|
-
// End of node_modules block
|
|
154
|
-
inNodeModules = false;
|
|
155
|
-
result.push(frame);
|
|
156
|
-
}
|
|
157
|
-
else if (!isNodeModule) {
|
|
158
|
-
// Regular frame
|
|
159
|
-
result.push(frame);
|
|
160
|
-
}
|
|
161
|
-
// Skip frames inside node_modules
|
|
162
|
-
}
|
|
163
|
-
return result;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Formats lineage segments into a single stack-like string
|
|
167
|
-
* @param segments - Lineage segments
|
|
168
|
-
*/
|
|
169
|
-
function formatLineage(segments) {
|
|
193
|
+
function formatLineage(lineage) {
|
|
194
|
+
const segments = Array.isArray(lineage) ? lineage : digestLineage(lineage);
|
|
170
195
|
const result = [];
|
|
171
196
|
for (let i = 0; i < segments.length; i++) {
|
|
172
197
|
const segment = segments[i];
|
|
173
198
|
if (i > 0) {
|
|
174
199
|
const triggerEffect = segments[i - 1].effect;
|
|
175
|
-
const triggerName = triggerEffect ?
|
|
200
|
+
const triggerName = triggerEffect ? proxy.getRoot(triggerEffect).name || 'anonymous' : 'root';
|
|
176
201
|
result.push(` --- effect: ${triggerName} ---`);
|
|
177
202
|
}
|
|
178
|
-
for (const frame of segment.stack)
|
|
203
|
+
for (const frame of segment.stack)
|
|
179
204
|
result.push(` ${frame.raw}`);
|
|
180
|
-
}
|
|
181
205
|
}
|
|
182
206
|
return result.join('\n');
|
|
183
207
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
* @param segments - Lineage segments
|
|
187
|
-
*/
|
|
188
|
-
function logLineage(segments) {
|
|
208
|
+
function logLineage(lineage) {
|
|
209
|
+
const segments = Array.isArray(lineage) ? lineage : digestLineage(lineage);
|
|
189
210
|
console.groupCollapsed(`🦴 Effect Lineage Trace (${segments.length} segment${segments.length === 1 ? '' : 's'})`);
|
|
190
211
|
for (let i = 0; i < segments.length; i++) {
|
|
191
212
|
const segment = segments[i];
|
|
192
|
-
// Add segment header
|
|
193
213
|
const isLast = i === segments.length - 1;
|
|
194
214
|
const prefix = i === 0 ? '📍' : isLast ? '└─' : '├─';
|
|
195
215
|
console.groupCollapsed(`${prefix} Effect:`, segment.effect ? segment.effect.name || 'anonymous' : 'root');
|
|
196
|
-
// Add stack frames
|
|
197
216
|
for (let j = 0; j < segment.stack.length; j++) {
|
|
198
217
|
const frame = segment.stack[j];
|
|
199
218
|
const isLastFrame = j === segment.stack.length - 1;
|
|
@@ -211,76 +230,408 @@ function logLineage(segments) {
|
|
|
211
230
|
}
|
|
212
231
|
console.groupEnd();
|
|
213
232
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Captures and logs lineage to console
|
|
216
|
-
*/
|
|
217
233
|
function captureNodeLineage() {
|
|
218
234
|
logLineage(getLineage());
|
|
219
235
|
}
|
|
220
|
-
/**
|
|
221
|
-
* Custom formatter for Chrome DevTools to render lineage data nicely.
|
|
222
|
-
*/
|
|
223
236
|
const lineageFormatter = {
|
|
224
237
|
header: (obj) => {
|
|
225
|
-
if (
|
|
226
|
-
// Try to detect DevTools theme - default to dark colors if uncertain
|
|
238
|
+
if (isLineageSegmentView(obj)) {
|
|
227
239
|
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
228
|
-
const
|
|
240
|
+
const frameColor = isDark ? '#8b949e' : '#57606a';
|
|
229
241
|
return [
|
|
230
242
|
'span',
|
|
231
|
-
{ style:
|
|
232
|
-
|
|
243
|
+
{ style: 'font-weight: bold;' },
|
|
244
|
+
formatSegmentTitle(obj.segment, obj.index, obj.total),
|
|
245
|
+
['span', { style: `color: ${frameColor}; margin-left: 6px; font-weight: normal;` }, formatEffectPreview(obj.segment.effect)],
|
|
246
|
+
[
|
|
247
|
+
'span',
|
|
248
|
+
{ style: `color: ${frameColor}; margin-left: 6px; font-weight: normal;` },
|
|
249
|
+
`${obj.segment.stack.length} frame${obj.segment.stack.length === 1 ? '' : 's'}`,
|
|
250
|
+
],
|
|
233
251
|
];
|
|
234
252
|
}
|
|
235
|
-
|
|
253
|
+
if (isLineageFrameView(obj)) {
|
|
254
|
+
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
255
|
+
const fnColor = obj.frame.functionName === 'anonymous' ? '#8b949e' : isDark ? '#ffffff' : '#222';
|
|
256
|
+
const linkColor = isDark ? '#58a6ff' : '#005cc5';
|
|
257
|
+
return [
|
|
258
|
+
'div',
|
|
259
|
+
{ style: 'display: block;' },
|
|
260
|
+
['span', { style: `color: ${fnColor}; font-weight: bold;` }, obj.frame.functionName],
|
|
261
|
+
['span', { style: 'color: #8b949e; margin: 0 6px;' }, '@'],
|
|
262
|
+
[
|
|
263
|
+
'span',
|
|
264
|
+
{ style: `color: ${linkColor}; text-decoration: underline;` },
|
|
265
|
+
formatLocationHref(obj.frame),
|
|
266
|
+
],
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
if (!isLineage(obj))
|
|
270
|
+
return null;
|
|
271
|
+
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
272
|
+
const headerColor = isDark ? '#cd9d5d' : '#704214';
|
|
273
|
+
return [
|
|
274
|
+
'span',
|
|
275
|
+
{ style: `color: ${headerColor}; font-weight: bold;` },
|
|
276
|
+
obj.digested
|
|
277
|
+
? `🦴 Effect Lineage (${obj.digested.length} segments)`
|
|
278
|
+
: '🦴 Effect Lineage (lazy)',
|
|
279
|
+
];
|
|
236
280
|
},
|
|
237
|
-
hasBody: (obj) => isLineage(obj),
|
|
281
|
+
hasBody: (obj) => isLineage(obj) || isLineageSegmentView(obj),
|
|
238
282
|
body: (obj) => {
|
|
283
|
+
if (isLineageSegmentView(obj)) {
|
|
284
|
+
return [
|
|
285
|
+
'div',
|
|
286
|
+
{ style: 'padding: 4px 0;' },
|
|
287
|
+
...obj.segment.stack.map((frame) => [
|
|
288
|
+
'div',
|
|
289
|
+
{ style: 'display: block; margin-top: 2px;' },
|
|
290
|
+
['object', { object: wrapLineageFrameView(frame) }],
|
|
291
|
+
]),
|
|
292
|
+
];
|
|
293
|
+
}
|
|
239
294
|
if (!isLineage(obj))
|
|
240
295
|
return null;
|
|
241
|
-
const segments = obj
|
|
242
|
-
|
|
296
|
+
const segments = digestLineage(obj);
|
|
297
|
+
return [
|
|
298
|
+
'div',
|
|
299
|
+
{ style: 'padding: 5px; line-height: 1.4;' },
|
|
300
|
+
...segments.map((segment, index) => [
|
|
301
|
+
'div',
|
|
302
|
+
{ style: 'display: block; margin-top: 4px;' },
|
|
303
|
+
['object', { object: wrapLineageSegmentView(segment, index, segments.length) }],
|
|
304
|
+
]),
|
|
305
|
+
];
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
function wrapLineageForDebug(lineage) {
|
|
309
|
+
return ensureSignature(lineage ?? getLineage());
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const reasonTypes = new Set(['propChange', 'invalidate', 'external', 'stopped', 'gc', 'lineage', 'error', 'multiple']);
|
|
313
|
+
const reasonCauseLists = new WeakSet();
|
|
314
|
+
function hasOwn(value, key) {
|
|
315
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
316
|
+
}
|
|
317
|
+
function isCleanupReason(value) {
|
|
318
|
+
if (!value || typeof value !== 'object')
|
|
319
|
+
return false;
|
|
320
|
+
if (!hasOwn(value, 'type'))
|
|
321
|
+
return false;
|
|
322
|
+
const type = value.type;
|
|
323
|
+
if (typeof type !== 'string' || !reasonTypes.has(type))
|
|
324
|
+
return false;
|
|
325
|
+
switch (type) {
|
|
326
|
+
case 'propChange':
|
|
327
|
+
return Array.isArray(value.triggers);
|
|
328
|
+
case 'invalidate':
|
|
329
|
+
return hasOwn(value, 'cause');
|
|
330
|
+
case 'lineage':
|
|
331
|
+
return hasOwn(value, 'parent');
|
|
332
|
+
case 'error':
|
|
333
|
+
return hasOwn(value, 'error');
|
|
334
|
+
case 'multiple':
|
|
335
|
+
return Array.isArray(value.reasons);
|
|
336
|
+
default:
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const reasonCauseEntries = new WeakSet();
|
|
341
|
+
const reasonCauseDetails = new WeakSet();
|
|
342
|
+
function isReasonCauseList(value) {
|
|
343
|
+
return !!value && typeof value === 'object' && reasonCauseLists.has(value);
|
|
344
|
+
}
|
|
345
|
+
function isReasonCauseEntry(value) {
|
|
346
|
+
return !!value && typeof value === 'object' && reasonCauseEntries.has(value);
|
|
347
|
+
}
|
|
348
|
+
function isReasonCauseDetail(value) {
|
|
349
|
+
return !!value && typeof value === 'object' && reasonCauseDetails.has(value);
|
|
350
|
+
}
|
|
351
|
+
function wrapReasonCauseEntry(entry) {
|
|
352
|
+
reasonCauseEntries.add(entry);
|
|
353
|
+
return entry;
|
|
354
|
+
}
|
|
355
|
+
function wrapReasonCauseDetail(detail) {
|
|
356
|
+
reasonCauseDetails.add(detail);
|
|
357
|
+
return detail;
|
|
358
|
+
}
|
|
359
|
+
function wrapReasonCauseList(list) {
|
|
360
|
+
reasonCauseLists.add(list);
|
|
361
|
+
return list;
|
|
362
|
+
}
|
|
363
|
+
function getCleanupReasonChain(reason) {
|
|
364
|
+
const chain = [];
|
|
365
|
+
let current = reason;
|
|
366
|
+
while (current) {
|
|
367
|
+
chain.push(current);
|
|
368
|
+
current = current.chain;
|
|
369
|
+
}
|
|
370
|
+
return chain;
|
|
371
|
+
}
|
|
372
|
+
function formatReasonSummary(reason) {
|
|
373
|
+
switch (reason.type) {
|
|
374
|
+
case 'propChange':
|
|
375
|
+
return `propChange: ${formatTriggerSummary(reason)}`;
|
|
376
|
+
case 'invalidate':
|
|
377
|
+
return `invalidate ← ${formatReasonSummary(reason.cause)}`;
|
|
378
|
+
case 'stopped':
|
|
379
|
+
return reason.detail ? `stopped (${reason.detail})` : 'stopped';
|
|
380
|
+
case 'external':
|
|
381
|
+
return `external: ${reason.detail}`;
|
|
382
|
+
case 'gc':
|
|
383
|
+
return 'gc';
|
|
384
|
+
case 'lineage':
|
|
385
|
+
return `lineage ← ${formatReasonSummary(reason.parent)}`;
|
|
386
|
+
case 'error':
|
|
387
|
+
return `error: ${formatUnknownSummary(reason.error)}`;
|
|
388
|
+
case 'multiple':
|
|
389
|
+
return `multiple: ${reason.reasons.map(formatReasonSummary).join(' | ')}`;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function formatTriggerSummary(reason) {
|
|
393
|
+
const groups = new Map();
|
|
394
|
+
const ordered = [];
|
|
395
|
+
for (const { obj, evolution } of reason.triggers) {
|
|
396
|
+
let group = groups.get(obj);
|
|
397
|
+
if (!group) {
|
|
398
|
+
group = { target: describeTarget(obj), parts: [] };
|
|
399
|
+
groups.set(obj, group);
|
|
400
|
+
ordered.push(group);
|
|
401
|
+
}
|
|
402
|
+
group.parts.push(evolution.type === 'bunch'
|
|
403
|
+
? `${evolution.type} ${String(evolution.method)}`
|
|
404
|
+
: `${evolution.type} ${String(evolution.prop)}`);
|
|
405
|
+
}
|
|
406
|
+
return ordered.map(({ target, parts }) => `${target}: ${parts.join(', ')}`).join(' | ');
|
|
407
|
+
}
|
|
408
|
+
function formatUnknownSummary(value) {
|
|
409
|
+
if (typeof value === 'string')
|
|
410
|
+
return value;
|
|
411
|
+
if (typeof value === 'number' || typeof value === 'boolean' || value == null)
|
|
412
|
+
return String(value);
|
|
413
|
+
if (value instanceof Error)
|
|
414
|
+
return value.message;
|
|
415
|
+
if (typeof value === 'function')
|
|
416
|
+
return value.name || 'anonymous';
|
|
417
|
+
if (typeof value === 'object')
|
|
418
|
+
return value.constructor?.name || 'object';
|
|
419
|
+
return typeof value;
|
|
420
|
+
}
|
|
421
|
+
function describeTarget(value) {
|
|
422
|
+
if (!value || typeof value !== 'object')
|
|
423
|
+
return formatUnknownSummary(value);
|
|
424
|
+
const tag = value[Symbol.toStringTag];
|
|
425
|
+
if (typeof tag === 'string' && tag && tag !== 'Object' && tag !== 'Array')
|
|
426
|
+
return tag;
|
|
427
|
+
if (Array.isArray(value))
|
|
428
|
+
return 'Array';
|
|
429
|
+
if (value instanceof Map)
|
|
430
|
+
return 'Map';
|
|
431
|
+
if (value instanceof Set)
|
|
432
|
+
return 'Set';
|
|
433
|
+
return value.constructor?.name || 'object';
|
|
434
|
+
}
|
|
435
|
+
function formatPreview(value) {
|
|
436
|
+
if (typeof value === 'string')
|
|
437
|
+
return value;
|
|
438
|
+
if (typeof value === 'number' || typeof value === 'boolean' || value == null)
|
|
439
|
+
return String(value);
|
|
440
|
+
if (typeof value === 'function')
|
|
441
|
+
return `[function ${value.name || 'anonymous'}]`;
|
|
442
|
+
if (value instanceof Error)
|
|
443
|
+
return `[Error: ${value.message}]`;
|
|
444
|
+
if (value && typeof value === 'object' && 'stack' in value)
|
|
445
|
+
return '[lineage]';
|
|
446
|
+
if (typeof value === 'object')
|
|
447
|
+
return `[${value.constructor?.name || 'object'}]`;
|
|
448
|
+
return `[${typeof value}]`;
|
|
449
|
+
}
|
|
450
|
+
function renderDetailValue(value, color) {
|
|
451
|
+
if (value && typeof value === 'object' && 'stack' in value) {
|
|
452
|
+
return ['object', { object: value }];
|
|
453
|
+
}
|
|
454
|
+
return ['span', { style: `color: ${color}; white-space: pre-wrap;` }, formatPreview(value)];
|
|
455
|
+
}
|
|
456
|
+
function getReasonLineageDetails(reason) {
|
|
457
|
+
switch (reason.type) {
|
|
458
|
+
case 'propChange': {
|
|
459
|
+
const details = [];
|
|
460
|
+
for (const trigger of reason.triggers) {
|
|
461
|
+
const target = trigger.evolution.type === 'bunch'
|
|
462
|
+
? String(trigger.evolution.method)
|
|
463
|
+
: String(trigger.evolution.prop);
|
|
464
|
+
const label = trigger.evolution.type === 'bunch'
|
|
465
|
+
? `${trigger.evolution.type} ${String(trigger.evolution.method)}`
|
|
466
|
+
: `${trigger.evolution.type} ${String(trigger.evolution.prop)}`;
|
|
467
|
+
if (trigger.touch)
|
|
468
|
+
details.push({ kind: 'touch', target, label, value: trigger.touch });
|
|
469
|
+
if (trigger.dependency)
|
|
470
|
+
details.push({ kind: 'dependency', target, label, value: trigger.dependency });
|
|
471
|
+
}
|
|
472
|
+
return details;
|
|
473
|
+
}
|
|
474
|
+
case 'invalidate':
|
|
475
|
+
return getReasonLineageDetails(reason.cause);
|
|
476
|
+
case 'lineage':
|
|
477
|
+
return getReasonLineageDetails(reason.parent);
|
|
478
|
+
case 'multiple':
|
|
479
|
+
return reason.reasons.flatMap(getReasonLineageDetails);
|
|
480
|
+
case 'external':
|
|
481
|
+
default:
|
|
482
|
+
return [];
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function renderLineageList(details, colors) {
|
|
486
|
+
const grouped = new Map();
|
|
487
|
+
for (const { kind, label, value } of details) {
|
|
488
|
+
let entry = grouped.get(label);
|
|
489
|
+
if (!entry) {
|
|
490
|
+
entry = { label };
|
|
491
|
+
grouped.set(label, entry);
|
|
492
|
+
}
|
|
493
|
+
if (kind === 'touch')
|
|
494
|
+
entry.touch = [...(entry.touch ?? []), value];
|
|
495
|
+
if (kind === 'dependency')
|
|
496
|
+
entry.dependency = [...(entry.dependency ?? []), value];
|
|
497
|
+
}
|
|
498
|
+
const listObject = wrapReasonCauseList({
|
|
499
|
+
count: grouped.size,
|
|
500
|
+
entries: Array.from(grouped.values(), wrapReasonCauseEntry),
|
|
501
|
+
});
|
|
502
|
+
return [
|
|
503
|
+
'div',
|
|
504
|
+
{
|
|
505
|
+
style: `margin-top: 4px; padding: 3px 5px; background: ${colors.block}; border-radius: 3px;`,
|
|
506
|
+
},
|
|
507
|
+
['object', { object: listObject }],
|
|
508
|
+
];
|
|
509
|
+
}
|
|
510
|
+
function formatReasonWithoutChain(reason) {
|
|
511
|
+
switch (reason.type) {
|
|
512
|
+
case 'propChange':
|
|
513
|
+
return proxy.formatCleanupReason({ type: 'propChange', triggers: reason.triggers });
|
|
514
|
+
case 'invalidate':
|
|
515
|
+
return proxy.formatCleanupReason({ type: 'invalidate', cause: reason.cause });
|
|
516
|
+
case 'stopped':
|
|
517
|
+
return proxy.formatCleanupReason(reason.detail ? { type: 'stopped', detail: reason.detail } : { type: 'stopped' });
|
|
518
|
+
case 'external':
|
|
519
|
+
return proxy.formatCleanupReason({ type: 'external', detail: reason.detail });
|
|
520
|
+
case 'gc':
|
|
521
|
+
return proxy.formatCleanupReason({ type: 'gc' });
|
|
522
|
+
case 'lineage':
|
|
523
|
+
return proxy.formatCleanupReason({ type: 'lineage', parent: reason.parent });
|
|
524
|
+
case 'error':
|
|
525
|
+
return proxy.formatCleanupReason({ type: 'error', error: reason.error });
|
|
526
|
+
case 'multiple':
|
|
527
|
+
return proxy.formatCleanupReason({ type: 'multiple', reasons: reason.reasons });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function logReason(reason, tag) {
|
|
531
|
+
const chain = getCleanupReasonChain(reason);
|
|
532
|
+
console.groupCollapsed(`🧹 Cleanup Reason${tag ? ` (${tag})` : ''}`);
|
|
533
|
+
for (let i = 0; i < chain.length; i++) {
|
|
534
|
+
console.groupCollapsed(`${i === 0 ? '📍' : '↖'} ${formatReasonSummary(chain[i])}`);
|
|
535
|
+
const details = getReasonLineageDetails(chain[i]);
|
|
536
|
+
if (details.length > 0) {
|
|
537
|
+
for (const { kind, label, value } of details)
|
|
538
|
+
console.log(`${kind} ${label}`, value);
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
console.log(...formatReasonWithoutChain(chain[i]));
|
|
542
|
+
}
|
|
543
|
+
console.groupEnd();
|
|
544
|
+
}
|
|
545
|
+
console.groupEnd();
|
|
546
|
+
return `🧹 Cleanup Reason${tag ? ` (${tag})` : ''}`;
|
|
547
|
+
}
|
|
548
|
+
const reasonFormatter = {
|
|
549
|
+
header: (obj) => {
|
|
550
|
+
if (isReasonCauseList(obj)) {
|
|
551
|
+
return ['span', { style: 'font-weight: bold;' }, `causes: ${obj.count}`];
|
|
552
|
+
}
|
|
553
|
+
if (isReasonCauseEntry(obj)) {
|
|
554
|
+
return [
|
|
555
|
+
'span',
|
|
556
|
+
{ style: 'font-weight: bold;' },
|
|
557
|
+
`${obj.label}:`,
|
|
558
|
+
...(obj.touch?.length
|
|
559
|
+
? [
|
|
560
|
+
['span', { style: 'margin-left: 6px;' }, ['object', { object: wrapReasonCauseDetail({ kind: 'touch', values: obj.touch }) }]],
|
|
561
|
+
]
|
|
562
|
+
: []),
|
|
563
|
+
...(obj.dependency?.length
|
|
564
|
+
? [
|
|
565
|
+
['span', { style: 'margin-left: 6px;' }, ['object', { object: wrapReasonCauseDetail({ kind: 'dependency', values: obj.dependency }) }]],
|
|
566
|
+
]
|
|
567
|
+
: []),
|
|
568
|
+
];
|
|
569
|
+
}
|
|
570
|
+
if (isReasonCauseDetail(obj)) {
|
|
571
|
+
return [
|
|
572
|
+
'span',
|
|
573
|
+
{ style: 'font-weight: normal;' },
|
|
574
|
+
obj.values.length === 1 ? obj.kind : `${obj.values.length} ${obj.kind}`,
|
|
575
|
+
];
|
|
576
|
+
}
|
|
577
|
+
if (!isCleanupReason(obj))
|
|
578
|
+
return null;
|
|
579
|
+
const chain = getCleanupReasonChain(obj);
|
|
243
580
|
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
segmentBg: '#eee',
|
|
252
|
-
};
|
|
253
|
-
const children = segments.map((segment, i) => {
|
|
254
|
-
const frames = segment.stack.map((frame) => [
|
|
581
|
+
const color = isDark ? '#8fb3ff' : '#0b5fff';
|
|
582
|
+
return ['span', { style: `color: ${color}; font-weight: bold;` }, `🧹 ${formatReasonSummary(obj)}${chain.length > 1 ? ` ×${chain.length}` : ''}`];
|
|
583
|
+
},
|
|
584
|
+
hasBody: (obj) => isCleanupReason(obj) || isReasonCauseList(obj) || isReasonCauseDetail(obj),
|
|
585
|
+
body: (obj) => {
|
|
586
|
+
if (isReasonCauseList(obj)) {
|
|
587
|
+
return [
|
|
255
588
|
'div',
|
|
256
|
-
{ style:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
589
|
+
{ style: 'padding: 4px 0;' },
|
|
590
|
+
...obj.entries.map((entry) => [
|
|
591
|
+
'div',
|
|
592
|
+
{ style: 'display: block; margin-top: 2px;' },
|
|
593
|
+
['object', { object: entry }],
|
|
594
|
+
]),
|
|
595
|
+
];
|
|
596
|
+
}
|
|
597
|
+
if (isReasonCauseDetail(obj)) {
|
|
598
|
+
const detailValue = obj.values.length === 1
|
|
599
|
+
? obj.values[0]
|
|
600
|
+
: Object.fromEntries(obj.values.map((value, index) => [`${obj.kind} ${index + 1}`, value]));
|
|
601
|
+
return [
|
|
262
602
|
'div',
|
|
263
|
-
{ style:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
603
|
+
{ style: 'padding: 4px 0; line-height: 1.4;' },
|
|
604
|
+
[
|
|
605
|
+
'div',
|
|
606
|
+
{ style: 'margin-top: 2px;' },
|
|
607
|
+
renderDetailValue(detailValue, 'inherit'),
|
|
608
|
+
],
|
|
609
|
+
];
|
|
610
|
+
}
|
|
611
|
+
if (!isCleanupReason(obj))
|
|
612
|
+
return null;
|
|
613
|
+
const chain = getCleanupReasonChain(obj);
|
|
614
|
+
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
615
|
+
const colors = isDark
|
|
616
|
+
? { block: '#2d2d2d', title: '#ffffff', meta: '#8b949e' }
|
|
617
|
+
: { block: '#eee', title: '#222', meta: '#57606a' };
|
|
618
|
+
const children = chain.map((reason, index) => {
|
|
619
|
+
const lineageDetails = getReasonLineageDetails(reason);
|
|
620
|
+
const renderedDetails = lineageDetails.length > 0
|
|
621
|
+
? [renderLineageList(lineageDetails, colors)]
|
|
622
|
+
: formatReasonWithoutChain(reason).map((part) => typeof part === 'string'
|
|
623
|
+
? ['span', { style: `color: ${colors.meta}; white-space: pre-wrap;` }, part]
|
|
624
|
+
: renderDetailValue(part, colors.meta));
|
|
625
|
+
return [
|
|
626
|
+
'div',
|
|
627
|
+
{ style: `margin-top: 5px; padding: 4px 6px; background: ${colors.block}; border-radius: 3px;` },
|
|
628
|
+
['div', { style: `font-weight: bold; color: ${colors.title}; margin-bottom: 3px;` }, `${index === 0 ? '📍' : '↖'} ${formatReasonSummary(reason)}`],
|
|
629
|
+
['div', { style: 'display: inline-block;' }, ...renderedDetails],
|
|
267
630
|
];
|
|
268
|
-
return ['div', {}, segmentHeader, ...frames];
|
|
269
631
|
});
|
|
270
632
|
return ['div', { style: 'padding: 5px; line-height: 1.4;' }, ...children];
|
|
271
633
|
},
|
|
272
634
|
};
|
|
273
|
-
/**
|
|
274
|
-
* Wraps lineage data in a way that the Chrome Formatter can recognize.
|
|
275
|
-
*/
|
|
276
|
-
function wrapLineageForDebug(segments) {
|
|
277
|
-
const lineage = {
|
|
278
|
-
segments,
|
|
279
|
-
toString: () => formatLineage(segments ?? getLineage()),
|
|
280
|
-
};
|
|
281
|
-
lineageObjects.add(lineage);
|
|
282
|
-
return lineage;
|
|
283
|
-
}
|
|
284
635
|
|
|
285
636
|
/**
|
|
286
637
|
* Displays a floating reactive panel in the browser showing the lineage of triggered effects.
|
|
@@ -380,13 +731,13 @@ function showLineagePanel() {
|
|
|
380
731
|
panel.appendChild(content);
|
|
381
732
|
document.body.appendChild(panel);
|
|
382
733
|
// Reactivity: Update Visibility
|
|
383
|
-
proxy.effect(() => {
|
|
734
|
+
proxy.effect `lineage-panel:visibility`(() => {
|
|
384
735
|
panel.style.opacity = state.isVisible ? '1' : '0';
|
|
385
736
|
panel.style.pointerEvents = state.isVisible ? 'all' : 'none';
|
|
386
737
|
panel.style.transform = state.isVisible ? 'translateY(0)' : 'translateY(20px)';
|
|
387
738
|
});
|
|
388
739
|
// Reactivity: Update Content
|
|
389
|
-
proxy.effect(() => {
|
|
740
|
+
proxy.effect `lineage-panel:content`(() => {
|
|
390
741
|
if (state.activeLineage.length === 0) {
|
|
391
742
|
content.innerHTML = `<div style="color: ${colors.placeholderText}; font-style: italic; text-align: center; margin-top: 20px;">No effect triggered yet...</div>`;
|
|
392
743
|
return;
|
|
@@ -428,6 +779,35 @@ function showLineagePanel() {
|
|
|
428
779
|
});
|
|
429
780
|
}
|
|
430
781
|
|
|
782
|
+
function getColors() {
|
|
783
|
+
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
784
|
+
return {
|
|
785
|
+
accent: isDark ? '#b392f0' : '#6f42c1',
|
|
786
|
+
meta: isDark ? '#8b949e' : '#57606a',
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
function getReactiveTarget(obj) {
|
|
790
|
+
if (!obj || typeof obj !== 'object' || !proxy.isReactive(obj))
|
|
791
|
+
return undefined;
|
|
792
|
+
return proxy.unwrap(obj);
|
|
793
|
+
}
|
|
794
|
+
const reactiveFormatter = {
|
|
795
|
+
header: (obj) => {
|
|
796
|
+
const target = getReactiveTarget(obj);
|
|
797
|
+
if (!target)
|
|
798
|
+
return null;
|
|
799
|
+
const colors = getColors();
|
|
800
|
+
return [
|
|
801
|
+
'span',
|
|
802
|
+
{},
|
|
803
|
+
['span', { style: `color: ${colors.accent}; font-weight: bold;` }, 'Reactive<'],
|
|
804
|
+
['object', { object: target }],
|
|
805
|
+
['span', { style: `color: ${colors.accent}; font-weight: bold;` }, '>'],
|
|
806
|
+
];
|
|
807
|
+
},
|
|
808
|
+
hasBody: () => false,
|
|
809
|
+
};
|
|
810
|
+
|
|
431
811
|
/**
|
|
432
812
|
* Debug utilities for the reactivity system
|
|
433
813
|
* - Captures effect metadata (names, parent relationships)
|
|
@@ -758,7 +1138,6 @@ function buildReactivityGraph() {
|
|
|
758
1138
|
},
|
|
759
1139
|
};
|
|
760
1140
|
}
|
|
761
|
-
const captureLineage = () => wrapLineageForDebug(getLineage());
|
|
762
1141
|
/**
|
|
763
1142
|
* Enables the DevTools bridge and exposes the debug API on window/global.
|
|
764
1143
|
* Call as early as possible in development builds.
|
|
@@ -785,25 +1164,38 @@ function enableDevTools() {
|
|
|
785
1164
|
logLineage(getLineage());
|
|
786
1165
|
return '🦴 Effect Lineage Trace' + (tag ? ` (${tag})` : '');
|
|
787
1166
|
},
|
|
788
|
-
|
|
789
|
-
|
|
1167
|
+
logReason(reason, tag) {
|
|
1168
|
+
const reasonToLog = reason ?? globalScope.__MUTTS_DEBUG__.reason;
|
|
1169
|
+
if (!reasonToLog)
|
|
1170
|
+
return '🧹 Cleanup Reason';
|
|
1171
|
+
return logReason(reasonToLog, tag);
|
|
1172
|
+
},
|
|
1173
|
+
get lineage() {
|
|
1174
|
+
return getLineage();
|
|
790
1175
|
},
|
|
791
|
-
getLineage,
|
|
792
|
-
captureLineage: getStackFrame,
|
|
793
|
-
formatLineage,
|
|
794
1176
|
showLineagePanel,
|
|
795
1177
|
setEffectName,
|
|
796
1178
|
setObjectName,
|
|
797
1179
|
registerEffect: registerEffectForDebug,
|
|
798
1180
|
registerObject: registerObjectForDebug,
|
|
1181
|
+
/**
|
|
1182
|
+
* Returns the reason why the current effect is being executed.
|
|
1183
|
+
* Returns undefined if not in an effect or if this is the first run.
|
|
1184
|
+
* @type {() => import('../src/reactive/types').CleanupReason | undefined}
|
|
1185
|
+
*/
|
|
1186
|
+
get reason() {
|
|
1187
|
+
const activeEffect = proxy.getActiveEffect();
|
|
1188
|
+
if (!activeEffect)
|
|
1189
|
+
return undefined;
|
|
1190
|
+
const node = proxy.getEffectNode(activeEffect);
|
|
1191
|
+
return node?.currentReason;
|
|
1192
|
+
},
|
|
799
1193
|
};
|
|
800
1194
|
// @ts-ignore - devtoolsFormatters is a Chrome-specific array
|
|
801
|
-
if (globalScope.devtoolsFormatters)
|
|
802
|
-
globalScope.devtoolsFormatters.push(lineageFormatter);
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
globalScope.devtoolsFormatters = [lineageFormatter];
|
|
806
|
-
}
|
|
1195
|
+
if (globalScope.devtoolsFormatters)
|
|
1196
|
+
globalScope.devtoolsFormatters.push(lineageFormatter, reasonFormatter, reactiveFormatter);
|
|
1197
|
+
else
|
|
1198
|
+
globalScope.devtoolsFormatters = [lineageFormatter, reasonFormatter, reactiveFormatter];
|
|
807
1199
|
proxy.setDebugHooks({
|
|
808
1200
|
isDevtoolsEnabled: () => devtoolsEnabled,
|
|
809
1201
|
registerEffect: registerEffectForDebug,
|
|
@@ -811,6 +1203,8 @@ function enableDevTools() {
|
|
|
811
1203
|
captureStack: getStackFrame,
|
|
812
1204
|
captureLineage,
|
|
813
1205
|
formatStack: (stack) => {
|
|
1206
|
+
if (isCleanupReason(stack))
|
|
1207
|
+
return [stack];
|
|
814
1208
|
if (typeof stack === 'string') {
|
|
815
1209
|
return [
|
|
816
1210
|
`\n ${stack
|
|
@@ -832,8 +1226,7 @@ function enableDevTools() {
|
|
|
832
1226
|
typeof error === 'object' &&
|
|
833
1227
|
'stack' in error &&
|
|
834
1228
|
!('lineage' in error)) {
|
|
835
|
-
|
|
836
|
-
error.lineage = wrapLineageForDebug(lineage);
|
|
1229
|
+
error.lineage = wrapLineageForDebug(getLineage(trigger, error));
|
|
837
1230
|
}
|
|
838
1231
|
},
|
|
839
1232
|
});
|
|
@@ -955,6 +1348,7 @@ function isDevelopmentMode() {
|
|
|
955
1348
|
}
|
|
956
1349
|
// Auto-enable devtools when the module loads in development
|
|
957
1350
|
if (isDevelopmentMode() && !devtoolsEnabled) {
|
|
1351
|
+
Object.assign(proxy.options, proxy.devPreset);
|
|
958
1352
|
enableDevTools();
|
|
959
1353
|
// Optional: Log that devtools were enabled (only in development)
|
|
960
1354
|
if (typeof console !== 'undefined' && console.info) {
|
|
@@ -966,9 +1360,11 @@ if (isDevelopmentMode() && !devtoolsEnabled) {
|
|
|
966
1360
|
exports.buildReactivityGraph = buildReactivityGraph;
|
|
967
1361
|
exports.captureLineage = captureLineage;
|
|
968
1362
|
exports.captureNodeLineage = captureNodeLineage;
|
|
1363
|
+
exports.digestLineage = digestLineage;
|
|
969
1364
|
exports.enableDevTools = enableDevTools;
|
|
970
1365
|
exports.forceEnableGraphTracking = forceEnableGraphTracking;
|
|
971
1366
|
exports.formatLineage = formatLineage;
|
|
1367
|
+
exports.getCleanupReasonChain = getCleanupReasonChain;
|
|
972
1368
|
exports.getDependencies = getDependencies;
|
|
973
1369
|
exports.getDependencyGraph = getDependencyGraph;
|
|
974
1370
|
exports.getDependents = getDependents;
|
|
@@ -976,12 +1372,14 @@ exports.getLineage = getLineage;
|
|
|
976
1372
|
exports.getMutationHistory = getMutationHistory;
|
|
977
1373
|
exports.getStackFrame = getStackFrame;
|
|
978
1374
|
exports.getTriggerChain = getTriggerChain;
|
|
1375
|
+
exports.isCleanupReason = isCleanupReason;
|
|
979
1376
|
exports.isDevtoolsEnabled = isDevtoolsEnabled;
|
|
980
1377
|
exports.isLineage = isLineage;
|
|
981
1378
|
exports.lineageFormatter = lineageFormatter;
|
|
982
1379
|
exports.logError = logError;
|
|
983
1380
|
exports.logLineage = logLineage;
|
|
984
|
-
exports.
|
|
1381
|
+
exports.logReason = logReason;
|
|
1382
|
+
exports.reasonFormatter = reasonFormatter;
|
|
985
1383
|
exports.recordTriggerLink = recordTriggerLink;
|
|
986
1384
|
exports.registerEffectForDebug = registerEffectForDebug;
|
|
987
1385
|
exports.registerObjectForDebug = registerObjectForDebug;
|