browser-devtools-mcp 0.0.3 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +507 -59
  2. package/dist/browser.js +6 -0
  3. package/dist/browser.js.map +1 -1
  4. package/dist/config.js +60 -24
  5. package/dist/config.js.map +1 -1
  6. package/dist/context.js +28 -0
  7. package/dist/context.js.map +1 -1
  8. package/dist/otel/otel-controller.js +4 -0
  9. package/dist/otel/otel-controller.js.map +1 -1
  10. package/dist/server-info.js +133 -18
  11. package/dist/server-info.js.map +1 -1
  12. package/dist/tools/content/take-screenshot.js +183 -1
  13. package/dist/tools/content/take-screenshot.js.map +1 -1
  14. package/dist/tools/figma/compare/compare-image-embedding.js +159 -0
  15. package/dist/tools/figma/compare/compare-image-embedding.js.map +1 -0
  16. package/dist/tools/figma/compare/compare-mssim.js +98 -0
  17. package/dist/tools/figma/compare/compare-mssim.js.map +1 -0
  18. package/dist/tools/figma/compare/compare-text-embedding.js +291 -0
  19. package/dist/tools/figma/compare/compare-text-embedding.js.map +1 -0
  20. package/dist/tools/figma/compare/index.js +139 -0
  21. package/dist/tools/figma/compare/index.js.map +1 -0
  22. package/dist/tools/figma/compare/types.js +3 -0
  23. package/dist/tools/figma/compare/types.js.map +1 -0
  24. package/dist/tools/figma/compare/vector.js +46 -0
  25. package/dist/tools/figma/compare/vector.js.map +1 -0
  26. package/dist/tools/figma/compare-page-with-design.js +240 -0
  27. package/dist/tools/figma/compare-page-with-design.js.map +1 -0
  28. package/dist/tools/figma/figma-service.js +134 -0
  29. package/dist/tools/figma/figma-service.js.map +1 -0
  30. package/dist/tools/figma/index.js +6 -0
  31. package/dist/tools/figma/index.js.map +1 -0
  32. package/dist/tools/index.js +12 -2
  33. package/dist/tools/index.js.map +1 -1
  34. package/dist/tools/interaction/index.js +6 -2
  35. package/dist/tools/interaction/index.js.map +1 -1
  36. package/dist/tools/interaction/resize-viewport.js +110 -0
  37. package/dist/tools/interaction/resize-viewport.js.map +1 -0
  38. package/dist/tools/interaction/resize-window.js +261 -0
  39. package/dist/tools/interaction/resize-window.js.map +1 -0
  40. package/dist/tools/interaction/scroll.js +304 -0
  41. package/dist/tools/interaction/scroll.js.map +1 -0
  42. package/dist/tools/{monitoring → o11y}/get-console-messages.js +1 -1
  43. package/dist/tools/o11y/get-console-messages.js.map +1 -0
  44. package/dist/tools/{monitoring → o11y}/get-http-requests.js +1 -1
  45. package/dist/tools/o11y/get-http-requests.js.map +1 -0
  46. package/dist/tools/{monitoring → o11y}/get-trace-id.js +1 -1
  47. package/dist/tools/o11y/get-trace-id.js.map +1 -0
  48. package/dist/tools/o11y/get-web-vitals.js +595 -0
  49. package/dist/tools/o11y/get-web-vitals.js.map +1 -0
  50. package/dist/tools/{monitoring → o11y}/index.js +2 -0
  51. package/dist/tools/o11y/index.js.map +1 -0
  52. package/dist/tools/{monitoring → o11y}/new-trace-id.js +1 -1
  53. package/dist/tools/o11y/new-trace-id.js.map +1 -0
  54. package/dist/tools/{monitoring → o11y}/set-trace-id.js +1 -1
  55. package/dist/tools/o11y/set-trace-id.js.map +1 -0
  56. package/dist/tools/react/get-component-for-element.js +941 -0
  57. package/dist/tools/react/get-component-for-element.js.map +1 -0
  58. package/dist/tools/react/get-element-for-component.js +1190 -0
  59. package/dist/tools/react/get-element-for-component.js.map +1 -0
  60. package/dist/tools/react/index.js +10 -0
  61. package/dist/tools/react/index.js.map +1 -0
  62. package/dist/tools/run/index.js +7 -0
  63. package/dist/tools/run/index.js.map +1 -0
  64. package/dist/tools/{interaction/evaluate.js → run/js-in-browser.js} +24 -6
  65. package/dist/tools/run/js-in-browser.js.map +1 -0
  66. package/dist/tools/run/js-in-sandbox.js +175 -0
  67. package/dist/tools/run/js-in-sandbox.js.map +1 -0
  68. package/dist/tools/stub/clear.js +41 -0
  69. package/dist/tools/stub/clear.js.map +1 -0
  70. package/dist/tools/stub/index.js +14 -0
  71. package/dist/tools/stub/index.js.map +1 -0
  72. package/dist/tools/stub/intercept-http-request.js +112 -0
  73. package/dist/tools/stub/intercept-http-request.js.map +1 -0
  74. package/dist/tools/stub/list.js +75 -0
  75. package/dist/tools/stub/list.js.map +1 -0
  76. package/dist/tools/stub/mock-http-response.js +152 -0
  77. package/dist/tools/stub/mock-http-response.js.map +1 -0
  78. package/dist/tools/stub/stub-controller.js +284 -0
  79. package/dist/tools/stub/stub-controller.js.map +1 -0
  80. package/dist/tools/sync/index.js +6 -0
  81. package/dist/tools/sync/index.js.map +1 -0
  82. package/dist/tools/sync/wait-for-network-idle.js +152 -0
  83. package/dist/tools/sync/wait-for-network-idle.js.map +1 -0
  84. package/package.json +16 -3
  85. package/dist/tools/interaction/evaluate.js.map +0 -1
  86. package/dist/tools/monitoring/get-console-messages.js.map +0 -1
  87. package/dist/tools/monitoring/get-http-requests.js.map +0 -1
  88. package/dist/tools/monitoring/get-trace-id.js.map +0 -1
  89. package/dist/tools/monitoring/index.js.map +0 -1
  90. package/dist/tools/monitoring/new-trace-id.js.map +0 -1
  91. package/dist/tools/monitoring/set-trace-id.js.map +0 -1
@@ -0,0 +1,941 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetComponentForElement = void 0;
4
+ const zod_1 = require("zod");
5
+ /**
6
+ * -------------------------
7
+ * Defaults / limits
8
+ * -------------------------
9
+ */
10
+ const DEFAULT_MAX_STACK_DEPTH = 30;
11
+ const DEFAULT_MAX_PROPS_PREVIEW_CHARS = 2000;
12
+ const DEFAULT_INCLUDE_PROPS_PREVIEW = true;
13
+ const DEFAULT_MAX_ELEMENT_PATH_DEPTH = 25;
14
+ /**
15
+ * Fiber subtree search can be large on big pages.
16
+ * Keep a conservative cap to avoid freezing the page.
17
+ */
18
+ const DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN = 50_000;
19
+ class GetComponentForElement {
20
+ name() {
21
+ return 'react_get-component-for-element';
22
+ }
23
+ description() {
24
+ return `
25
+ Finds the React component(s) associated with a DOM element using React Fiber (best-effort).
26
+
27
+ How it works:
28
+ - Resolve a DOM element by CSS selector OR by (x,y) using elementFromPoint()
29
+ - Attempt to locate React Fiber pointers on that element (e.g. __reactFiber$*)
30
+ - If not present, walk up ancestor elements to find the nearest React-owned host node
31
+
32
+ Key correctness fix:
33
+ - If fiber is found on an ANCESTOR element, we scan that fiber subtree to locate the EXACT host fiber
34
+ where fiber.stateNode === target DOM element, then build the component stack from that host fiber.
35
+ This reduces unrelated components appearing in the returned stack.
36
+
37
+ What to expect (important for AI debugging):
38
+ - React Fiber is not a public API; results are best-effort and can differ by build (dev/prod).
39
+ - Component names may come from displayName, wrappers, third-party libraries, or minified production builds.
40
+ - wrappersDetected/wrapperFrames help interpret memo/forwardRef/context boundaries that can otherwise look confusing.
41
+ - If hostMapping.strategy is "ancestor-fallback", the stack may include unrelated frames; try a more specific selector
42
+ or target a deeper DOM node to improve mapping accuracy.
43
+ `.trim();
44
+ }
45
+ inputSchema() {
46
+ return {
47
+ selector: zod_1.z
48
+ .string()
49
+ .optional()
50
+ .describe('CSS selector for the target element. If provided, takes precedence over x/y.'),
51
+ x: zod_1.z
52
+ .number()
53
+ .int()
54
+ .optional()
55
+ .describe('Viewport X coordinate in CSS pixels. Used when selector is not provided.'),
56
+ y: zod_1.z
57
+ .number()
58
+ .int()
59
+ .optional()
60
+ .describe('Viewport Y coordinate in CSS pixels. Used when selector is not provided.'),
61
+ maxStackDepth: zod_1.z
62
+ .number()
63
+ .int()
64
+ .positive()
65
+ .optional()
66
+ .default(DEFAULT_MAX_STACK_DEPTH)
67
+ .describe('Maximum number of component frames to return in the component stack.'),
68
+ includePropsPreview: zod_1.z
69
+ .boolean()
70
+ .optional()
71
+ .default(DEFAULT_INCLUDE_PROPS_PREVIEW)
72
+ .describe('If true, includes a best-effort, truncated props preview for the nearest component.'),
73
+ maxPropsPreviewChars: zod_1.z
74
+ .number()
75
+ .int()
76
+ .positive()
77
+ .optional()
78
+ .default(DEFAULT_MAX_PROPS_PREVIEW_CHARS)
79
+ .describe('Maximum characters for props preview (after safe stringification).'),
80
+ };
81
+ }
82
+ outputSchema() {
83
+ return {
84
+ target: zod_1.z.object({
85
+ selector: zod_1.z.string().nullable(),
86
+ point: zod_1.z.object({
87
+ x: zod_1.z.number().int().nullable(),
88
+ y: zod_1.z.number().int().nullable(),
89
+ }),
90
+ found: zod_1.z.boolean(),
91
+ domHint: zod_1.z.string().nullable(),
92
+ elementPath: zod_1.z.string().nullable(),
93
+ }),
94
+ react: zod_1.z.object({
95
+ detected: zod_1.z.boolean(),
96
+ detectionReason: zod_1.z.string(),
97
+ fiberKey: zod_1.z.string().nullable(),
98
+ hostMapping: zod_1.z.object({
99
+ fiberOnTargetElement: zod_1.z
100
+ .boolean()
101
+ .describe('Whether the initial fiber pointer was found directly on the target element.'),
102
+ anchorDomHint: zod_1.z
103
+ .string()
104
+ .nullable()
105
+ .describe('DOM hint of the element where fiber pointer was found (target or ancestor).'),
106
+ targetDomHint: zod_1.z
107
+ .string()
108
+ .nullable()
109
+ .describe('DOM hint of the actual target element.'),
110
+ hostFiberMatchedTarget: zod_1.z
111
+ .boolean()
112
+ .describe('Whether we found the exact host fiber for target element (fiber.stateNode === targetEl) via subtree scan.'),
113
+ subtreeScanNodesScanned: zod_1.z
114
+ .number()
115
+ .int()
116
+ .describe('Number of fiber nodes scanned during subtree search.'),
117
+ subtreeScanMaxNodes: zod_1.z
118
+ .number()
119
+ .int()
120
+ .describe('Maximum fiber nodes allowed to scan (safety cap).'),
121
+ strategy: zod_1.z
122
+ .enum([
123
+ 'direct-on-target',
124
+ 'ancestor-subtree-scan',
125
+ 'ancestor-fallback',
126
+ ])
127
+ .describe('Mapping strategy used to produce the final stack.'),
128
+ }),
129
+ nearestComponent: zod_1.z
130
+ .object({
131
+ name: zod_1.z.string().nullable(),
132
+ displayName: zod_1.z.string().nullable(),
133
+ kind: zod_1.z.enum(['function', 'class', 'unknown']),
134
+ debugSource: zod_1.z
135
+ .object({
136
+ fileName: zod_1.z.string().optional(),
137
+ lineNumber: zod_1.z.number().int().optional(),
138
+ columnNumber: zod_1.z.number().int().optional(),
139
+ })
140
+ .optional(),
141
+ propsPreview: zod_1.z.string().optional(),
142
+ })
143
+ .nullable(),
144
+ componentStack: zod_1.z.array(zod_1.z.object({
145
+ name: zod_1.z.string().nullable(),
146
+ displayName: zod_1.z.string().nullable(),
147
+ kind: zod_1.z.enum(['function', 'class', 'unknown']),
148
+ debugSource: zod_1.z
149
+ .object({
150
+ fileName: zod_1.z.string().optional(),
151
+ lineNumber: zod_1.z.number().int().optional(),
152
+ columnNumber: zod_1.z.number().int().optional(),
153
+ })
154
+ .optional(),
155
+ })),
156
+ componentStackText: zod_1.z.string(),
157
+ wrappersDetected: zod_1.z.array(zod_1.z.string()),
158
+ wrapperFrames: zod_1.z.array(zod_1.z.object({
159
+ wrapper: zod_1.z.string(),
160
+ frameIndex: zod_1.z.number().int(),
161
+ frameLabel: zod_1.z.string(),
162
+ })),
163
+ notes: zod_1.z.array(zod_1.z.string()),
164
+ }),
165
+ };
166
+ }
167
+ async handle(context, args) {
168
+ const selector = typeof args.selector === 'string' && args.selector.trim()
169
+ ? args.selector.trim()
170
+ : undefined;
171
+ const x = typeof args.x === 'number' && Number.isFinite(args.x)
172
+ ? Math.floor(args.x)
173
+ : undefined;
174
+ const y = typeof args.y === 'number' && Number.isFinite(args.y)
175
+ ? Math.floor(args.y)
176
+ : undefined;
177
+ if (!selector) {
178
+ if (typeof x !== 'number' || typeof y !== 'number') {
179
+ throw new Error('Provide either selector, or both x and y for elementFromPoint.');
180
+ }
181
+ }
182
+ const maxStackDepth = typeof args.maxStackDepth === 'number' && args.maxStackDepth > 0
183
+ ? Math.floor(args.maxStackDepth)
184
+ : DEFAULT_MAX_STACK_DEPTH;
185
+ const includePropsPreview = args.includePropsPreview === undefined
186
+ ? DEFAULT_INCLUDE_PROPS_PREVIEW
187
+ : args.includePropsPreview === true;
188
+ const maxPropsPreviewChars = typeof args.maxPropsPreviewChars === 'number' &&
189
+ Number.isFinite(args.maxPropsPreviewChars) &&
190
+ args.maxPropsPreviewChars > 0
191
+ ? Math.floor(args.maxPropsPreviewChars)
192
+ : DEFAULT_MAX_PROPS_PREVIEW_CHARS;
193
+ const result = await context.page.evaluate(({ selectorEval, xEval, yEval, maxStackDepthEval, includePropsPreviewEval, maxPropsPreviewCharsEval, maxElementPathDepthEval, maxFiberNodesToScanEval, }) => {
194
+ const notes = [];
195
+ function domHint(el) {
196
+ if (!el) {
197
+ return null;
198
+ }
199
+ const tag = el.tagName.toLowerCase();
200
+ const idVal = el.id && el.id.trim()
201
+ ? '#' + el.id.trim()
202
+ : '';
203
+ const clsRaw = typeof el.className === 'string'
204
+ ? el.className
205
+ : '';
206
+ const cls = clsRaw
207
+ ? '.' + clsRaw.trim().split(/\s+/).slice(0, 4).join('.')
208
+ : '';
209
+ return tag + idVal + cls;
210
+ }
211
+ function buildElementPath(el, maxDepth) {
212
+ if (!el) {
213
+ return null;
214
+ }
215
+ const parts = [];
216
+ let cur = el;
217
+ let depth = 0;
218
+ while (cur && depth < maxDepth) {
219
+ const tag = cur.tagName
220
+ ? cur.tagName.toLowerCase()
221
+ : 'unknown';
222
+ const idVal = cur.id &&
223
+ cur.id.trim()
224
+ ? '#' + cur.id.trim()
225
+ : '';
226
+ const clsRaw = typeof cur.className === 'string'
227
+ ? cur.className
228
+ : '';
229
+ const clsList = clsRaw
230
+ ? clsRaw
231
+ .trim()
232
+ .split(/\s+/)
233
+ .filter(Boolean)
234
+ .slice(0, 3)
235
+ : [];
236
+ const cls = clsList.length > 0 ? '.' + clsList.join('.') : '';
237
+ let nth = '';
238
+ try {
239
+ const parent = cur.parentElement;
240
+ if (parent) {
241
+ const siblings = Array.from(parent.children).filter((c) => {
242
+ return c.tagName === cur.tagName;
243
+ });
244
+ if (siblings.length > 1) {
245
+ const idx = siblings.indexOf(cur) + 1;
246
+ if (idx > 0) {
247
+ nth = `:nth-of-type(${idx})`;
248
+ }
249
+ }
250
+ }
251
+ }
252
+ catch {
253
+ // ignore
254
+ }
255
+ parts.push(`${tag}${idVal}${cls}${nth}`);
256
+ cur = cur.parentElement;
257
+ depth++;
258
+ }
259
+ parts.reverse();
260
+ return parts.join(' > ');
261
+ }
262
+ function findFiberKeyOn(el) {
263
+ if (!el) {
264
+ return null;
265
+ }
266
+ const keys = Object.getOwnPropertyNames(el);
267
+ for (const k of keys) {
268
+ if (k.startsWith('__reactFiber$')) {
269
+ return k;
270
+ }
271
+ if (k.startsWith('__reactInternalInstance$')) {
272
+ return k;
273
+ }
274
+ }
275
+ return null;
276
+ }
277
+ function findFiberForElement(start) {
278
+ if (!start) {
279
+ return null;
280
+ }
281
+ const directKey = findFiberKeyOn(start);
282
+ if (directKey) {
283
+ const fiber = start[directKey];
284
+ if (fiber) {
285
+ return {
286
+ fiber,
287
+ fiberKey: directKey,
288
+ onTarget: true,
289
+ anchorEl: start,
290
+ };
291
+ }
292
+ }
293
+ let cur = start;
294
+ while (cur) {
295
+ const k = findFiberKeyOn(cur);
296
+ if (k) {
297
+ const fiber = cur[k];
298
+ if (fiber) {
299
+ return {
300
+ fiber,
301
+ fiberKey: k,
302
+ onTarget: false,
303
+ anchorEl: cur,
304
+ };
305
+ }
306
+ }
307
+ cur = cur.parentElement;
308
+ }
309
+ return null;
310
+ }
311
+ /**
312
+ * If fiber was found on an ancestor element, scan that fiber subtree
313
+ * to locate the exact host fiber where fiber.stateNode === targetEl.
314
+ *
315
+ * This improves stack correctness and avoids unrelated frames.
316
+ */
317
+ function findHostFiberForDomElement(subtreeRootFiber, targetEl, maxNodesToScan) {
318
+ if (!subtreeRootFiber) {
319
+ return { hostFiber: null, scanned: 0, found: false };
320
+ }
321
+ if (subtreeRootFiber.stateNode === targetEl) {
322
+ return {
323
+ hostFiber: subtreeRootFiber,
324
+ scanned: 1,
325
+ found: true,
326
+ };
327
+ }
328
+ const queue = [];
329
+ const visited = new Set();
330
+ queue.push(subtreeRootFiber);
331
+ visited.add(subtreeRootFiber);
332
+ let scanned = 0;
333
+ while (queue.length > 0) {
334
+ const f = queue.shift();
335
+ scanned++;
336
+ if (f && f.stateNode === targetEl) {
337
+ return { hostFiber: f, scanned, found: true };
338
+ }
339
+ if (scanned >= maxNodesToScan) {
340
+ return { hostFiber: null, scanned, found: false };
341
+ }
342
+ const child = f ? f.child : null;
343
+ if (child && !visited.has(child)) {
344
+ visited.add(child);
345
+ queue.push(child);
346
+ }
347
+ const sibling = f ? f.sibling : null;
348
+ if (sibling && !visited.has(sibling)) {
349
+ visited.add(sibling);
350
+ queue.push(sibling);
351
+ }
352
+ }
353
+ return { hostFiber: null, scanned, found: false };
354
+ }
355
+ function isClassComponentType(t) {
356
+ if (!t) {
357
+ return false;
358
+ }
359
+ const proto = t.prototype;
360
+ return Boolean(proto && proto.isReactComponent);
361
+ }
362
+ function typeDisplayName(t) {
363
+ if (!t) {
364
+ return null;
365
+ }
366
+ if (typeof t === 'function') {
367
+ const dn = t.displayName;
368
+ if (typeof dn === 'string' && dn.trim()) {
369
+ return dn.trim();
370
+ }
371
+ if (typeof t.name === 'string' && t.name.trim()) {
372
+ return t.name.trim();
373
+ }
374
+ return 'Anonymous';
375
+ }
376
+ if (typeof t === 'object') {
377
+ const dn = t.displayName;
378
+ if (typeof dn === 'string' && dn.trim()) {
379
+ return dn.trim();
380
+ }
381
+ const render = t.render;
382
+ if (typeof render === 'function') {
383
+ const rdn = render.displayName;
384
+ if (typeof rdn === 'string' && rdn.trim()) {
385
+ return rdn.trim();
386
+ }
387
+ if (typeof render.name === 'string' &&
388
+ render.name.trim()) {
389
+ return render.name.trim();
390
+ }
391
+ return 'Anonymous';
392
+ }
393
+ }
394
+ return null;
395
+ }
396
+ function typeName(t) {
397
+ if (!t) {
398
+ return null;
399
+ }
400
+ if (typeof t === 'function') {
401
+ if (typeof t.name === 'string' && t.name.trim()) {
402
+ return t.name.trim();
403
+ }
404
+ return null;
405
+ }
406
+ if (typeof t === 'object') {
407
+ const render = t.render;
408
+ if (typeof render === 'function') {
409
+ if (typeof render.name === 'string' &&
410
+ render.name.trim()) {
411
+ return render.name.trim();
412
+ }
413
+ }
414
+ return null;
415
+ }
416
+ return null;
417
+ }
418
+ function unwrapType(t) {
419
+ if (!t) {
420
+ return t;
421
+ }
422
+ if (typeof t === 'object') {
423
+ const render = t.render;
424
+ if (typeof render === 'function') {
425
+ return render;
426
+ }
427
+ }
428
+ return t;
429
+ }
430
+ function isMeaningfulComponentFiber(f) {
431
+ if (!f) {
432
+ return false;
433
+ }
434
+ const t = unwrapType(f.type ?? f.elementType);
435
+ if (typeof t === 'function') {
436
+ return true;
437
+ }
438
+ return false;
439
+ }
440
+ function inferKind(f) {
441
+ if (!f) {
442
+ return 'unknown';
443
+ }
444
+ const t = unwrapType(f.type ?? f.elementType);
445
+ if (typeof t === 'function') {
446
+ if (isClassComponentType(t)) {
447
+ return 'class';
448
+ }
449
+ return 'function';
450
+ }
451
+ return 'unknown';
452
+ }
453
+ function getDebugSource(f) {
454
+ const ds = f ? f._debugSource : undefined;
455
+ if (!ds || typeof ds !== 'object') {
456
+ return undefined;
457
+ }
458
+ const out = {};
459
+ if (typeof ds.fileName === 'string') {
460
+ out.fileName = ds.fileName;
461
+ }
462
+ if (typeof ds.lineNumber === 'number') {
463
+ out.lineNumber = ds.lineNumber;
464
+ }
465
+ if (typeof ds.columnNumber === 'number') {
466
+ out.columnNumber = ds.columnNumber;
467
+ }
468
+ return Object.keys(out).length > 0 ? out : undefined;
469
+ }
470
+ function safeStringify(v, maxChars) {
471
+ const seen = new WeakSet();
472
+ function helper(x, depth) {
473
+ if (x === null) {
474
+ return null;
475
+ }
476
+ const t = typeof x;
477
+ if (t === 'string') {
478
+ return x.length > 500 ? x.slice(0, 500) + '…' : x;
479
+ }
480
+ if (t === 'number' || t === 'boolean') {
481
+ return x;
482
+ }
483
+ if (t === 'bigint') {
484
+ return String(x);
485
+ }
486
+ if (t === 'undefined') {
487
+ return undefined;
488
+ }
489
+ if (t === 'function') {
490
+ return '[function]';
491
+ }
492
+ if (t === 'symbol') {
493
+ return String(x);
494
+ }
495
+ if (t === 'object') {
496
+ if (x instanceof Element) {
497
+ return ('[Element ' +
498
+ (x.tagName ? x.tagName.toLowerCase() : '') +
499
+ ']');
500
+ }
501
+ if (x instanceof Window) {
502
+ return '[Window]';
503
+ }
504
+ if (x instanceof Document) {
505
+ return '[Document]';
506
+ }
507
+ if (x instanceof Date) {
508
+ return x.toISOString();
509
+ }
510
+ if (seen.has(x)) {
511
+ return '[circular]';
512
+ }
513
+ seen.add(x);
514
+ if (Array.isArray(x)) {
515
+ if (depth <= 0) {
516
+ return '[array len=' + x.length + ']';
517
+ }
518
+ return x.slice(0, 20).map((it) => {
519
+ return helper(it, depth - 1);
520
+ });
521
+ }
522
+ if (depth <= 0) {
523
+ return '[object]';
524
+ }
525
+ const out = {};
526
+ const keys = Object.keys(x).slice(0, 40);
527
+ for (const k of keys) {
528
+ try {
529
+ out[k] = helper(x[k], depth - 1);
530
+ }
531
+ catch {
532
+ out[k] = '[unreadable]';
533
+ }
534
+ }
535
+ return out;
536
+ }
537
+ return String(x);
538
+ }
539
+ let s = '';
540
+ try {
541
+ s = JSON.stringify(helper(v, 2));
542
+ }
543
+ catch {
544
+ try {
545
+ s = String(v);
546
+ }
547
+ catch {
548
+ s = '[unserializable]';
549
+ }
550
+ }
551
+ if (s.length > maxChars) {
552
+ return s.slice(0, maxChars) + '…(truncated)';
553
+ }
554
+ return s;
555
+ }
556
+ function stackTextFromFrames(frames) {
557
+ const names = frames
558
+ .map((f) => {
559
+ const dn = f?.displayName;
560
+ const nm = f?.name;
561
+ if (typeof dn === 'string' && dn.trim()) {
562
+ return dn.trim();
563
+ }
564
+ if (typeof nm === 'string' && nm.trim()) {
565
+ return nm.trim();
566
+ }
567
+ return 'Anonymous';
568
+ })
569
+ .filter((x) => {
570
+ return Boolean(x);
571
+ });
572
+ return names.reverse().join(' > ');
573
+ }
574
+ function frameFromFiber(f) {
575
+ const t = unwrapType(f.type ?? f.elementType);
576
+ const name = typeName(t);
577
+ const displayName = typeDisplayName(t);
578
+ const kind = inferKind(f);
579
+ const debugSource = getDebugSource(f);
580
+ const frame = { name, displayName, kind };
581
+ if (debugSource) {
582
+ frame.debugSource = debugSource;
583
+ }
584
+ return frame;
585
+ }
586
+ function makeFrameKey(frame) {
587
+ const dn = frame.displayName
588
+ ? frame.displayName
589
+ : '';
590
+ const nm = frame.name ? frame.name : '';
591
+ const src = frame.debugSource;
592
+ const srcKey = src && typeof src === 'object'
593
+ ? `${String(src.fileName ?? '')}:${String(src.lineNumber ?? '')}:${String(src.columnNumber ?? '')}`
594
+ : '';
595
+ return `${dn}|${nm}|${frame.kind}|${srcKey}`;
596
+ }
597
+ function collapseConsecutiveDuplicates(frames) {
598
+ const out = [];
599
+ let lastKey = null;
600
+ for (const f of frames) {
601
+ const k = makeFrameKey(f);
602
+ if (lastKey && k === lastKey) {
603
+ continue;
604
+ }
605
+ out.push(f);
606
+ lastKey = k;
607
+ }
608
+ return out;
609
+ }
610
+ function detectWrappers(frames) {
611
+ const found = new Set();
612
+ function canonicalAdd(key) {
613
+ const v = key.trim().toLowerCase();
614
+ if (v) {
615
+ found.add(v);
616
+ }
617
+ }
618
+ for (const f of frames) {
619
+ const label = typeof f.displayName === 'string' &&
620
+ f.displayName.trim()
621
+ ? f.displayName.trim()
622
+ : typeof f.name === 'string' && f.name.trim()
623
+ ? f.name.trim()
624
+ : '';
625
+ if (!label) {
626
+ continue;
627
+ }
628
+ const l = label.toLowerCase();
629
+ if (/^memo(\(|$)/i.test(label)) {
630
+ canonicalAdd('memo');
631
+ }
632
+ if (l.includes('forwardref')) {
633
+ canonicalAdd('forwardref');
634
+ }
635
+ if (l.includes('.provider')) {
636
+ canonicalAdd('context-provider');
637
+ }
638
+ if (l.includes('.consumer')) {
639
+ canonicalAdd('context-consumer');
640
+ }
641
+ if (l.includes('suspense')) {
642
+ canonicalAdd('suspense');
643
+ }
644
+ if (l.includes('fragment')) {
645
+ canonicalAdd('fragment');
646
+ }
647
+ if (l.includes('strictmode')) {
648
+ canonicalAdd('strictmode');
649
+ }
650
+ if (l.includes('profiler')) {
651
+ canonicalAdd('profiler');
652
+ }
653
+ }
654
+ const out = Array.from(found);
655
+ out.sort((a, b) => {
656
+ if (a < b) {
657
+ return -1;
658
+ }
659
+ if (a > b) {
660
+ return 1;
661
+ }
662
+ return 0;
663
+ });
664
+ return out;
665
+ }
666
+ function findWrapperFrames(frames, wrappersDetected) {
667
+ const out = [];
668
+ const wrappers = new Set(wrappersDetected.map((w) => w.toLowerCase()));
669
+ for (let i = 0; i < frames.length; i++) {
670
+ const f = frames[i];
671
+ const label = f.displayName && f.displayName.trim()
672
+ ? f.displayName.trim()
673
+ : f.name && f.name.trim()
674
+ ? f.name.trim()
675
+ : 'Anonymous';
676
+ const l = label.toLowerCase();
677
+ for (const w of wrappers) {
678
+ let hit = false;
679
+ if (w === 'memo') {
680
+ hit = /^memo(\(|$)/i.test(label);
681
+ }
682
+ else if (w === 'forwardref') {
683
+ hit = l.includes('forwardref');
684
+ }
685
+ else if (w === 'context-provider') {
686
+ hit = l.includes('.provider');
687
+ }
688
+ else if (w === 'context-consumer') {
689
+ hit = l.includes('.consumer');
690
+ }
691
+ else if (w === 'suspense') {
692
+ hit = l.includes('suspense');
693
+ }
694
+ else if (w === 'fragment') {
695
+ hit = l.includes('fragment');
696
+ }
697
+ else if (w === 'strictmode') {
698
+ hit = l.includes('strictmode');
699
+ }
700
+ else if (w === 'profiler') {
701
+ hit = l.includes('profiler');
702
+ }
703
+ if (hit) {
704
+ out.push({
705
+ wrapper: w,
706
+ frameIndex: i,
707
+ frameLabel: label,
708
+ });
709
+ }
710
+ }
711
+ }
712
+ return out;
713
+ }
714
+ // Resolve target element
715
+ let targetEl = null;
716
+ if (selectorEval && selectorEval.trim()) {
717
+ try {
718
+ targetEl = document.querySelector(selectorEval);
719
+ }
720
+ catch {
721
+ targetEl = null;
722
+ }
723
+ }
724
+ else if (typeof xEval === 'number' &&
725
+ typeof yEval === 'number') {
726
+ targetEl = document.elementFromPoint(xEval, yEval);
727
+ }
728
+ const foundEl = Boolean(targetEl);
729
+ const targetHint = domHint(targetEl);
730
+ const elementPath = buildElementPath(targetEl, maxElementPathDepthEval);
731
+ if (!foundEl) {
732
+ const hostMapping = {
733
+ fiberOnTargetElement: false,
734
+ anchorDomHint: null,
735
+ targetDomHint: null,
736
+ hostFiberMatchedTarget: false,
737
+ subtreeScanNodesScanned: 0,
738
+ subtreeScanMaxNodes: maxFiberNodesToScanEval,
739
+ strategy: 'ancestor-fallback',
740
+ };
741
+ return {
742
+ target: {
743
+ selector: selectorEval ?? null,
744
+ point: {
745
+ x: typeof xEval === 'number' ? xEval : null,
746
+ y: typeof yEval === 'number' ? yEval : null,
747
+ },
748
+ found: false,
749
+ domHint: null,
750
+ elementPath: null,
751
+ },
752
+ react: {
753
+ detected: false,
754
+ detectionReason: 'Target element not found.',
755
+ fiberKey: null,
756
+ hostMapping,
757
+ nearestComponent: null,
758
+ componentStack: [],
759
+ componentStackText: '',
760
+ wrappersDetected: [],
761
+ wrapperFrames: [],
762
+ notes: [
763
+ 'No DOM element could be resolved; cannot inspect React.',
764
+ ],
765
+ },
766
+ };
767
+ }
768
+ // Find a fiber pointer (maybe on target, maybe on ancestor)
769
+ const fiberHit = findFiberForElement(targetEl);
770
+ if (!fiberHit) {
771
+ notes.push('No React fiber pointer found on the element or its ancestors.');
772
+ notes.push('This can happen if the page is not React, uses a different renderer, or runs with hardened/minified internals.');
773
+ const hostMapping = {
774
+ fiberOnTargetElement: false,
775
+ anchorDomHint: null,
776
+ targetDomHint: targetHint,
777
+ hostFiberMatchedTarget: false,
778
+ subtreeScanNodesScanned: 0,
779
+ subtreeScanMaxNodes: maxFiberNodesToScanEval,
780
+ strategy: 'ancestor-fallback',
781
+ };
782
+ return {
783
+ target: {
784
+ selector: selectorEval ?? null,
785
+ point: {
786
+ x: typeof xEval === 'number' ? xEval : null,
787
+ y: typeof yEval === 'number' ? yEval : null,
788
+ },
789
+ found: true,
790
+ domHint: targetHint,
791
+ elementPath,
792
+ },
793
+ react: {
794
+ detected: false,
795
+ detectionReason: 'React fiber not found on element/ancestors.',
796
+ fiberKey: null,
797
+ hostMapping,
798
+ nearestComponent: null,
799
+ componentStack: [],
800
+ componentStackText: '',
801
+ wrappersDetected: [],
802
+ wrapperFrames: [],
803
+ notes,
804
+ },
805
+ };
806
+ }
807
+ // Determine the best "host fiber" to build the stack from.
808
+ let hostFiber = fiberHit.fiber;
809
+ let hostMatch = fiberHit.onTarget;
810
+ let subtreeScanned = 0;
811
+ const anchorHint = domHint(fiberHit.anchorEl);
812
+ let strategy = 'direct-on-target';
813
+ if (!fiberHit.onTarget) {
814
+ strategy = 'ancestor-fallback';
815
+ const scanRes = findHostFiberForDomElement(fiberHit.fiber, targetEl, maxFiberNodesToScanEval);
816
+ subtreeScanned = scanRes.scanned;
817
+ if (scanRes.found && scanRes.hostFiber) {
818
+ hostFiber = scanRes.hostFiber;
819
+ hostMatch = true;
820
+ strategy = 'ancestor-subtree-scan';
821
+ notes.push(`Mapped target DOM element to host fiber by scanning fiber subtree (scanned=${subtreeScanned}).`);
822
+ }
823
+ else {
824
+ hostMatch = false;
825
+ notes.push(`Could not find exact host fiber for the target element in the ancestor fiber subtree (scanned=${subtreeScanned}). Falling back to ancestor fiber stack; some frames may be unrelated.`);
826
+ }
827
+ }
828
+ const hostMapping = {
829
+ fiberOnTargetElement: fiberHit.onTarget,
830
+ anchorDomHint: anchorHint,
831
+ targetDomHint: targetHint,
832
+ hostFiberMatchedTarget: hostMatch,
833
+ subtreeScanNodesScanned: subtreeScanned,
834
+ subtreeScanMaxNodes: maxFiberNodesToScanEval,
835
+ strategy,
836
+ };
837
+ // Find nearest meaningful component above the host fiber.
838
+ let nearest = null;
839
+ let cur = hostFiber;
840
+ while (cur) {
841
+ if (isMeaningfulComponentFiber(cur)) {
842
+ nearest = cur;
843
+ break;
844
+ }
845
+ cur = cur.return ?? null;
846
+ }
847
+ if (!nearest) {
848
+ notes.push('Fiber was found, but no meaningful function/class component was detected in the return chain.');
849
+ }
850
+ // Build stack (from nearest up) using return chain
851
+ const stack = [];
852
+ const seenFibers = new Set();
853
+ let stackCur = nearest;
854
+ while (stackCur && stack.length < maxStackDepthEval) {
855
+ if (seenFibers.has(stackCur)) {
856
+ notes.push('Detected a cycle in fiber.return chain; stopping stack traversal.');
857
+ break;
858
+ }
859
+ seenFibers.add(stackCur);
860
+ if (isMeaningfulComponentFiber(stackCur)) {
861
+ stack.push(frameFromFiber(stackCur));
862
+ }
863
+ stackCur = stackCur.return ?? null;
864
+ }
865
+ const collapsedStack = stack.length > 0
866
+ ? collapseConsecutiveDuplicates(stack)
867
+ : [];
868
+ const componentStackText = stackTextFromFrames(collapsedStack);
869
+ const wrappersDetected = detectWrappers(collapsedStack);
870
+ const wrapperFrames = findWrapperFrames(collapsedStack, wrappersDetected);
871
+ if (wrappersDetected.length >= 3) {
872
+ notes.push(`Wrapper-heavy stack detected (${wrappersDetected.join(', ')}). Interpreting nearestComponent may require skipping wrappers.`);
873
+ }
874
+ // Props preview (best-effort)
875
+ let nearestOut = null;
876
+ if (nearest) {
877
+ nearestOut = frameFromFiber(nearest);
878
+ if (includePropsPreviewEval) {
879
+ try {
880
+ const props = nearest.memoizedProps;
881
+ if (props !== undefined) {
882
+ nearestOut.propsPreview = safeStringify(props, maxPropsPreviewCharsEval);
883
+ }
884
+ else {
885
+ notes.push('memoizedProps not available on nearest component fiber.');
886
+ }
887
+ }
888
+ catch {
889
+ notes.push('Failed to read memoizedProps for nearest component (best-effort).');
890
+ }
891
+ }
892
+ }
893
+ notes.push('React Fiber inspection uses non-public internals; fields are best-effort.');
894
+ notes.push('Component names may come from displayName, wrappers, third-party libraries, or minified production builds.');
895
+ if (strategy === 'ancestor-fallback') {
896
+ notes.push('Host mapping fallback was used; consider selecting a deeper/more specific DOM element for a more accurate component stack.');
897
+ }
898
+ const detectionReason = strategy === 'direct-on-target'
899
+ ? 'React fiber found on the target element.'
900
+ : strategy === 'ancestor-subtree-scan'
901
+ ? 'React fiber found on an ancestor; exact host fiber located via subtree scan.'
902
+ : 'React fiber found on an ancestor; exact host fiber not found, stack may include unrelated frames.';
903
+ return {
904
+ target: {
905
+ selector: selectorEval ?? null,
906
+ point: {
907
+ x: typeof xEval === 'number' ? xEval : null,
908
+ y: typeof yEval === 'number' ? yEval : null,
909
+ },
910
+ found: true,
911
+ domHint: targetHint,
912
+ elementPath,
913
+ },
914
+ react: {
915
+ detected: true,
916
+ detectionReason,
917
+ fiberKey: fiberHit.fiberKey,
918
+ hostMapping,
919
+ nearestComponent: nearestOut,
920
+ componentStack: collapsedStack,
921
+ componentStackText,
922
+ wrappersDetected,
923
+ wrapperFrames,
924
+ notes,
925
+ },
926
+ };
927
+ }, {
928
+ selectorEval: selector ?? null,
929
+ xEval: typeof x === 'number' ? x : null,
930
+ yEval: typeof y === 'number' ? y : null,
931
+ maxStackDepthEval: maxStackDepth,
932
+ includePropsPreviewEval: includePropsPreview,
933
+ maxPropsPreviewCharsEval: maxPropsPreviewChars,
934
+ maxElementPathDepthEval: DEFAULT_MAX_ELEMENT_PATH_DEPTH,
935
+ maxFiberNodesToScanEval: DEFAULT_MAX_FIBER_SUBTREE_NODES_TO_SCAN,
936
+ });
937
+ return result;
938
+ }
939
+ }
940
+ exports.GetComponentForElement = GetComponentForElement;
941
+ //# sourceMappingURL=get-component-for-element.js.map