mutts 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/dist/browser.cjs +237 -2596
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +1407 -2
- package/dist/browser.dev.cjs +44 -43
- 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 +3 -3
- package/dist/chunks/index-CAdnMJev.cjs +2735 -0
- package/dist/chunks/index-CAdnMJev.cjs.map +1 -0
- package/dist/chunks/{index-Sf74wXTV.esm.js → index-XsYTUhHx.esm.js} +200 -77
- package/dist/chunks/index-XsYTUhHx.esm.js.map +1 -0
- package/dist/chunks/{async-node-3PrbVAbB.cjs → node-DrrphEPf.cjs} +4 -4
- package/dist/chunks/node-DrrphEPf.cjs.map +1 -0
- package/dist/chunks/{node-Bo7WU5S2.esm.js → node-NEZvVo4M.esm.js} +2 -2
- package/dist/chunks/{node-Bo7WU5S2.esm.js.map → node-NEZvVo4M.esm.js.map} +1 -1
- package/dist/chunks/{proxy-D2C49sXH.esm.js → proxy-BtmPFjSr.esm.js} +307 -66
- package/dist/chunks/proxy-BtmPFjSr.esm.js.map +1 -0
- package/dist/chunks/{proxy-Cc79Lrzj.cjs → proxy-DBHj3kGK.cjs} +341 -70
- package/dist/chunks/proxy-DBHj3kGK.cjs.map +1 -0
- package/dist/debug.cjs +537 -167
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.d.ts +96 -80
- package/dist/debug.esm.js +533 -166
- package/dist/debug.esm.js.map +1 -1
- package/dist/devtools/panel.js.map +1 -1
- package/dist/mutts.umd.js +508 -140
- 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 +44 -42
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.dev.cjs +44 -42
- 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} +12 -0
- package/docs/ai/api-reference.md +102 -12
- package/docs/ai/manual.md +60 -24
- package/docs/debug-getReason.md +161 -0
- package/docs/flavored.md +98 -1
- package/docs/reactive/advanced.md +15 -2
- package/docs/reactive/attend.md +32 -0
- package/docs/reactive/core.md +40 -6
- package/docs/reactive/debugging.md +25 -2
- package/docs/reactive.md +2 -0
- package/package.json +2 -3
- package/dist/chunks/async-browser-Dgr5CreQ.cjs +0 -218
- package/dist/chunks/async-browser-Dgr5CreQ.cjs.map +0 -1
- package/dist/chunks/async-core-CRLKP3l-.cjs +0 -29
- package/dist/chunks/async-core-CRLKP3l-.cjs.map +0 -1
- package/dist/chunks/async-node-3PrbVAbB.cjs.map +0 -1
- package/dist/chunks/index-Sf74wXTV.esm.js.map +0 -1
- package/dist/chunks/proxy-Cc79Lrzj.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,26 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var proxy = require('./chunks/proxy-
|
|
4
|
-
require('./chunks/async-core-CRLKP3l-.cjs');
|
|
3
|
+
var proxy = require('./chunks/proxy-DBHj3kGK.cjs');
|
|
5
4
|
|
|
6
5
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
7
6
|
const lineageObjects = new WeakSet();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
7
|
+
const lineageSegmentObjects = new WeakSet();
|
|
8
|
+
const lineageFrameObjects = new WeakSet();
|
|
11
9
|
function isLineage(obj) {
|
|
12
|
-
return obj && typeof obj === 'object' && lineageObjects.has(obj);
|
|
10
|
+
return !!obj && typeof obj === 'object' && lineageObjects.has(obj);
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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;
|
|
18
19
|
function parseStackLine(line) {
|
|
19
|
-
// Node.js format: " at functionName (file:line:column)"
|
|
20
|
-
// or " at file:line:column"
|
|
21
20
|
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
21
|
const browserMatch = line.match(/(?:(.+?)(?:\@|\(?))?(?:(.+?):(\d+):(\d+))(?:\@|\)?)$/);
|
|
25
22
|
const match = nodeMatch || browserMatch;
|
|
26
23
|
if (!match)
|
|
@@ -34,16 +31,23 @@ function parseStackLine(line) {
|
|
|
34
31
|
raw: line.trim(),
|
|
35
32
|
};
|
|
36
33
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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)
|
|
45
49
|
return [];
|
|
46
|
-
const lines =
|
|
50
|
+
const lines = rawStack.split('\n');
|
|
47
51
|
const lastLine = lines.findIndex((line) => line.includes(proxy.effectMarker.enter));
|
|
48
52
|
if (lastLine !== -1)
|
|
49
53
|
lines.splice(lastLine);
|
|
@@ -51,150 +55,164 @@ function getStackFrame(error = new Error()) {
|
|
|
51
55
|
if (firstLine !== -1)
|
|
52
56
|
lines.splice(0, firstLine + 1);
|
|
53
57
|
else {
|
|
54
|
-
// Dynamically identify the library's internal files if not already done
|
|
55
58
|
if (!internalFile && lines[1]) {
|
|
56
59
|
const selfFrame = parseStackLine(lines[1]);
|
|
57
|
-
if (selfFrame)
|
|
60
|
+
if (selfFrame)
|
|
58
61
|
internalFile = selfFrame.fileName;
|
|
59
|
-
}
|
|
60
62
|
}
|
|
61
|
-
|
|
62
|
-
|
|
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++) {
|
|
63
|
+
let l = 1;
|
|
64
|
+
for (; l < lines.length; l++) {
|
|
70
65
|
const frame = parseStackLine(lines[l]);
|
|
71
66
|
if (!frame)
|
|
72
67
|
continue;
|
|
73
|
-
// Robust skipping: if we are still in the internal area, skip it.
|
|
74
68
|
const isInternal = /Lineage$/.test(frame.functionName) ||
|
|
75
69
|
frame.functionName === 'eval' ||
|
|
76
|
-
[`getStackFrame`, `captureLineage`].includes(frame.functionName);
|
|
70
|
+
[`getStackFrame`, `captureLineage`, `getLineage`, `digestLineage`, `formatLineage`].includes(frame.functionName);
|
|
77
71
|
if (!isInternal)
|
|
78
72
|
break;
|
|
79
73
|
}
|
|
80
74
|
lines.splice(0, l);
|
|
81
75
|
}
|
|
82
|
-
return lines.map(parseStackLine).filter(
|
|
76
|
+
return filterNodeModules(lines.map(parseStackLine).filter((frame) => !!frame));
|
|
83
77
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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);
|
|
90
152
|
const segments = [];
|
|
91
153
|
if (!currentEffect) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
154
|
+
lineage.digested = [
|
|
155
|
+
{
|
|
156
|
+
effect: undefined,
|
|
157
|
+
effectName: 'root',
|
|
158
|
+
stack: currentStack,
|
|
159
|
+
},
|
|
160
|
+
];
|
|
161
|
+
return lineage.digested;
|
|
98
162
|
}
|
|
99
163
|
let current = currentEffect;
|
|
100
164
|
let lastStack = currentStack;
|
|
101
165
|
while (current) {
|
|
102
166
|
const rootFn = proxy.getRoot(current);
|
|
103
|
-
// Too aggressive for now
|
|
104
|
-
//if(!rootFn.name) debugger
|
|
105
|
-
const filteredStack = filterNodeModules(lastStack);
|
|
106
167
|
segments.push({
|
|
107
168
|
effect: rootFn,
|
|
108
169
|
effectName: rootFn.name || 'anonymous',
|
|
109
|
-
stack:
|
|
170
|
+
stack: lastStack,
|
|
110
171
|
});
|
|
111
|
-
// Move to parent
|
|
112
172
|
const node = proxy.getEffectNode(current);
|
|
113
173
|
const parent = node.parent;
|
|
114
|
-
const creationStack = node.creationStack;
|
|
174
|
+
const creationStack = parseStackFrames(node.creationStack);
|
|
115
175
|
if (parent) {
|
|
116
176
|
current = parent;
|
|
117
|
-
lastStack = creationStack
|
|
177
|
+
lastStack = creationStack;
|
|
118
178
|
}
|
|
119
|
-
else if (creationStack) {
|
|
179
|
+
else if (node.creationStack) {
|
|
120
180
|
segments.push({
|
|
121
181
|
effect: undefined,
|
|
122
182
|
effectName: 'root',
|
|
123
|
-
stack:
|
|
183
|
+
stack: creationStack,
|
|
124
184
|
});
|
|
125
185
|
break;
|
|
126
186
|
}
|
|
127
|
-
else
|
|
187
|
+
else
|
|
128
188
|
break;
|
|
129
|
-
}
|
|
130
189
|
}
|
|
190
|
+
lineage.digested = segments;
|
|
131
191
|
return segments;
|
|
132
192
|
}
|
|
133
|
-
|
|
134
|
-
|
|
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) {
|
|
193
|
+
function formatLineage(lineage) {
|
|
194
|
+
const segments = Array.isArray(lineage) ? lineage : digestLineage(lineage);
|
|
171
195
|
const result = [];
|
|
172
196
|
for (let i = 0; i < segments.length; i++) {
|
|
173
197
|
const segment = segments[i];
|
|
174
198
|
if (i > 0) {
|
|
175
199
|
const triggerEffect = segments[i - 1].effect;
|
|
176
|
-
const triggerName = triggerEffect ?
|
|
200
|
+
const triggerName = triggerEffect ? proxy.getRoot(triggerEffect).name || 'anonymous' : 'root';
|
|
177
201
|
result.push(` --- effect: ${triggerName} ---`);
|
|
178
202
|
}
|
|
179
|
-
for (const frame of segment.stack)
|
|
203
|
+
for (const frame of segment.stack)
|
|
180
204
|
result.push(` ${frame.raw}`);
|
|
181
|
-
}
|
|
182
205
|
}
|
|
183
206
|
return result.join('\n');
|
|
184
207
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
* @param segments - Lineage segments
|
|
188
|
-
*/
|
|
189
|
-
function logLineage(segments) {
|
|
208
|
+
function logLineage(lineage) {
|
|
209
|
+
const segments = Array.isArray(lineage) ? lineage : digestLineage(lineage);
|
|
190
210
|
console.groupCollapsed(`🦴 Effect Lineage Trace (${segments.length} segment${segments.length === 1 ? '' : 's'})`);
|
|
191
211
|
for (let i = 0; i < segments.length; i++) {
|
|
192
212
|
const segment = segments[i];
|
|
193
|
-
// Add segment header
|
|
194
213
|
const isLast = i === segments.length - 1;
|
|
195
214
|
const prefix = i === 0 ? '📍' : isLast ? '└─' : '├─';
|
|
196
215
|
console.groupCollapsed(`${prefix} Effect:`, segment.effect ? segment.effect.name || 'anonymous' : 'root');
|
|
197
|
-
// Add stack frames
|
|
198
216
|
for (let j = 0; j < segment.stack.length; j++) {
|
|
199
217
|
const frame = segment.stack[j];
|
|
200
218
|
const isLastFrame = j === segment.stack.length - 1;
|
|
@@ -212,76 +230,408 @@ function logLineage(segments) {
|
|
|
212
230
|
}
|
|
213
231
|
console.groupEnd();
|
|
214
232
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Captures and logs lineage to console
|
|
217
|
-
*/
|
|
218
233
|
function captureNodeLineage() {
|
|
219
234
|
logLineage(getLineage());
|
|
220
235
|
}
|
|
221
|
-
/**
|
|
222
|
-
* Custom formatter for Chrome DevTools to render lineage data nicely.
|
|
223
|
-
*/
|
|
224
236
|
const lineageFormatter = {
|
|
225
237
|
header: (obj) => {
|
|
226
|
-
if (
|
|
227
|
-
// Try to detect DevTools theme - default to dark colors if uncertain
|
|
238
|
+
if (isLineageSegmentView(obj)) {
|
|
228
239
|
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
229
|
-
const
|
|
240
|
+
const frameColor = isDark ? '#8b949e' : '#57606a';
|
|
230
241
|
return [
|
|
231
242
|
'span',
|
|
232
|
-
{ style:
|
|
233
|
-
|
|
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
|
+
],
|
|
234
251
|
];
|
|
235
252
|
}
|
|
236
|
-
|
|
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
|
+
];
|
|
237
280
|
},
|
|
238
|
-
hasBody: (obj) => isLineage(obj),
|
|
281
|
+
hasBody: (obj) => isLineage(obj) || isLineageSegmentView(obj),
|
|
239
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
|
+
}
|
|
240
294
|
if (!isLineage(obj))
|
|
241
295
|
return null;
|
|
242
|
-
const segments = obj
|
|
243
|
-
|
|
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);
|
|
244
580
|
const isDark = typeof window !== 'undefined' && window.matchMedia?.('(prefers-color-scheme: dark)').matches;
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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 [
|
|
588
|
+
'div',
|
|
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 [
|
|
256
602
|
'div',
|
|
257
|
-
{ style:
|
|
258
|
-
[
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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 [
|
|
263
626
|
'div',
|
|
264
|
-
{ style: `margin-top: 5px; padding:
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
segment.effect ? ['object', { object: segment.effect }] : 'root',
|
|
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],
|
|
268
630
|
];
|
|
269
|
-
return ['div', {}, segmentHeader, ...frames];
|
|
270
631
|
});
|
|
271
632
|
return ['div', { style: 'padding: 5px; line-height: 1.4;' }, ...children];
|
|
272
633
|
},
|
|
273
634
|
};
|
|
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
|
-
}
|
|
285
635
|
|
|
286
636
|
/**
|
|
287
637
|
* Displays a floating reactive panel in the browser showing the lineage of triggered effects.
|
|
@@ -759,7 +1109,6 @@ function buildReactivityGraph() {
|
|
|
759
1109
|
},
|
|
760
1110
|
};
|
|
761
1111
|
}
|
|
762
|
-
const captureLineage = () => wrapLineageForDebug(getLineage());
|
|
763
1112
|
/**
|
|
764
1113
|
* Enables the DevTools bridge and exposes the debug API on window/global.
|
|
765
1114
|
* Call as early as possible in development builds.
|
|
@@ -786,24 +1135,40 @@ function enableDevTools() {
|
|
|
786
1135
|
logLineage(getLineage());
|
|
787
1136
|
return '🦴 Effect Lineage Trace' + (tag ? ` (${tag})` : '');
|
|
788
1137
|
},
|
|
789
|
-
|
|
790
|
-
|
|
1138
|
+
logReason(reason, tag) {
|
|
1139
|
+
const reasonToLog = reason ?? globalScope.__MUTTS_DEBUG__.reason;
|
|
1140
|
+
if (!reasonToLog)
|
|
1141
|
+
return '🧹 Cleanup Reason';
|
|
1142
|
+
return logReason(reasonToLog, tag);
|
|
1143
|
+
},
|
|
1144
|
+
get lineage() {
|
|
1145
|
+
return getLineage();
|
|
791
1146
|
},
|
|
792
|
-
getLineage,
|
|
793
|
-
captureLineage: getStackFrame,
|
|
794
|
-
formatLineage,
|
|
795
1147
|
showLineagePanel,
|
|
796
1148
|
setEffectName,
|
|
797
1149
|
setObjectName,
|
|
798
1150
|
registerEffect: registerEffectForDebug,
|
|
799
1151
|
registerObject: registerObjectForDebug,
|
|
1152
|
+
/**
|
|
1153
|
+
* Returns the reason why the current effect is being executed.
|
|
1154
|
+
* Returns undefined if not in an effect or if this is the first run.
|
|
1155
|
+
* @type {() => import('../src/reactive/types').CleanupReason | undefined}
|
|
1156
|
+
*/
|
|
1157
|
+
get reason() {
|
|
1158
|
+
const activeEffect = proxy.getActiveEffect();
|
|
1159
|
+
if (!activeEffect)
|
|
1160
|
+
return undefined;
|
|
1161
|
+
const node = proxy.getEffectNode(activeEffect);
|
|
1162
|
+
return node?.currentReason;
|
|
1163
|
+
},
|
|
800
1164
|
};
|
|
801
1165
|
// @ts-ignore - devtoolsFormatters is a Chrome-specific array
|
|
802
1166
|
if (globalScope.devtoolsFormatters) {
|
|
803
1167
|
globalScope.devtoolsFormatters.push(lineageFormatter);
|
|
1168
|
+
globalScope.devtoolsFormatters.push(reasonFormatter);
|
|
804
1169
|
}
|
|
805
1170
|
else {
|
|
806
|
-
globalScope.devtoolsFormatters = [lineageFormatter];
|
|
1171
|
+
globalScope.devtoolsFormatters = [lineageFormatter, reasonFormatter];
|
|
807
1172
|
}
|
|
808
1173
|
proxy.setDebugHooks({
|
|
809
1174
|
isDevtoolsEnabled: () => devtoolsEnabled,
|
|
@@ -812,6 +1177,8 @@ function enableDevTools() {
|
|
|
812
1177
|
captureStack: getStackFrame,
|
|
813
1178
|
captureLineage,
|
|
814
1179
|
formatStack: (stack) => {
|
|
1180
|
+
if (isCleanupReason(stack))
|
|
1181
|
+
return [stack];
|
|
815
1182
|
if (typeof stack === 'string') {
|
|
816
1183
|
return [
|
|
817
1184
|
`\n ${stack
|
|
@@ -833,8 +1200,7 @@ function enableDevTools() {
|
|
|
833
1200
|
typeof error === 'object' &&
|
|
834
1201
|
'stack' in error &&
|
|
835
1202
|
!('lineage' in error)) {
|
|
836
|
-
|
|
837
|
-
error.lineage = wrapLineageForDebug(lineage);
|
|
1203
|
+
error.lineage = wrapLineageForDebug(getLineage(trigger, error));
|
|
838
1204
|
}
|
|
839
1205
|
},
|
|
840
1206
|
});
|
|
@@ -967,9 +1333,11 @@ if (isDevelopmentMode() && !devtoolsEnabled) {
|
|
|
967
1333
|
exports.buildReactivityGraph = buildReactivityGraph;
|
|
968
1334
|
exports.captureLineage = captureLineage;
|
|
969
1335
|
exports.captureNodeLineage = captureNodeLineage;
|
|
1336
|
+
exports.digestLineage = digestLineage;
|
|
970
1337
|
exports.enableDevTools = enableDevTools;
|
|
971
1338
|
exports.forceEnableGraphTracking = forceEnableGraphTracking;
|
|
972
1339
|
exports.formatLineage = formatLineage;
|
|
1340
|
+
exports.getCleanupReasonChain = getCleanupReasonChain;
|
|
973
1341
|
exports.getDependencies = getDependencies;
|
|
974
1342
|
exports.getDependencyGraph = getDependencyGraph;
|
|
975
1343
|
exports.getDependents = getDependents;
|
|
@@ -977,12 +1345,14 @@ exports.getLineage = getLineage;
|
|
|
977
1345
|
exports.getMutationHistory = getMutationHistory;
|
|
978
1346
|
exports.getStackFrame = getStackFrame;
|
|
979
1347
|
exports.getTriggerChain = getTriggerChain;
|
|
1348
|
+
exports.isCleanupReason = isCleanupReason;
|
|
980
1349
|
exports.isDevtoolsEnabled = isDevtoolsEnabled;
|
|
981
1350
|
exports.isLineage = isLineage;
|
|
982
1351
|
exports.lineageFormatter = lineageFormatter;
|
|
983
1352
|
exports.logError = logError;
|
|
984
1353
|
exports.logLineage = logLineage;
|
|
985
|
-
exports.
|
|
1354
|
+
exports.logReason = logReason;
|
|
1355
|
+
exports.reasonFormatter = reasonFormatter;
|
|
986
1356
|
exports.recordTriggerLink = recordTriggerLink;
|
|
987
1357
|
exports.registerEffectForDebug = registerEffectForDebug;
|
|
988
1358
|
exports.registerObjectForDebug = registerObjectForDebug;
|