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