@qontinui/ui-bridge 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.
- package/dist/control/index.d.mts +134 -0
- package/dist/control/index.d.ts +134 -0
- package/dist/control/index.js +924 -0
- package/dist/control/index.js.map +1 -0
- package/dist/control/index.mjs +919 -0
- package/dist/control/index.mjs.map +1 -0
- package/dist/core/index.d.mts +52 -0
- package/dist/core/index.d.ts +52 -0
- package/dist/core/index.js +1424 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +1409 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/debug/index.d.mts +93 -0
- package/dist/debug/index.d.ts +93 -0
- package/dist/debug/index.js +673 -0
- package/dist/debug/index.js.map +1 -0
- package/dist/debug/index.mjs +664 -0
- package/dist/debug/index.mjs.map +1 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +4719 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4665 -0
- package/dist/index.mjs.map +1 -0
- package/dist/metrics-BCG7z7Aq.d.mts +147 -0
- package/dist/metrics-QCnK0EFw.d.ts +147 -0
- package/dist/react/index.d.mts +786 -0
- package/dist/react/index.d.ts +786 -0
- package/dist/react/index.js +4312 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +4290 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/registry-CT6BVVKr.d.mts +253 -0
- package/dist/registry-D4mQ01B3.d.ts +253 -0
- package/dist/render-log/index.d.mts +340 -0
- package/dist/render-log/index.d.ts +340 -0
- package/dist/render-log/index.js +702 -0
- package/dist/render-log/index.js.map +1 -0
- package/dist/render-log/index.mjs +695 -0
- package/dist/render-log/index.mjs.map +1 -0
- package/dist/types-BDkXy5si.d.ts +354 -0
- package/dist/types-BpvpStn3.d.mts +802 -0
- package/dist/types-BpvpStn3.d.ts +802 -0
- package/dist/types-DdJD9yw5.d.mts +354 -0
- package/dist/websocket-client-B2LC9CYc.d.mts +124 -0
- package/dist/websocket-client-DupH0X7B.d.ts +124 -0
- package/package.json +83 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/element-identifier.ts
|
|
4
|
+
function generateXPath(element) {
|
|
5
|
+
if (element.id) {
|
|
6
|
+
return `//*[@id="${element.id}"]`;
|
|
7
|
+
}
|
|
8
|
+
const parts = [];
|
|
9
|
+
let current = element;
|
|
10
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
11
|
+
let selector = current.nodeName.toLowerCase();
|
|
12
|
+
const uiId = current.getAttribute("data-ui-id");
|
|
13
|
+
if (uiId) {
|
|
14
|
+
selector += `[@data-ui-id="${uiId}"]`;
|
|
15
|
+
parts.unshift(selector);
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
const testId = current.getAttribute("data-testid");
|
|
19
|
+
if (testId) {
|
|
20
|
+
selector += `[@data-testid="${testId}"]`;
|
|
21
|
+
parts.unshift(selector);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
const id = current.id;
|
|
25
|
+
if (id) {
|
|
26
|
+
selector += `[@id="${id}"]`;
|
|
27
|
+
parts.unshift(selector);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
const parentEl = current.parentElement;
|
|
31
|
+
if (parentEl) {
|
|
32
|
+
const currentEl = current;
|
|
33
|
+
const siblings = Array.from(parentEl.children).filter(
|
|
34
|
+
(child) => child.nodeName === currentEl.nodeName
|
|
35
|
+
);
|
|
36
|
+
if (siblings.length > 1) {
|
|
37
|
+
const index = siblings.indexOf(currentEl) + 1;
|
|
38
|
+
selector += `[${index}]`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
parts.unshift(selector);
|
|
42
|
+
current = parentEl;
|
|
43
|
+
}
|
|
44
|
+
return "/" + parts.join("/");
|
|
45
|
+
}
|
|
46
|
+
function generateCSSSelector(element) {
|
|
47
|
+
const uiId = element.getAttribute("data-ui-id");
|
|
48
|
+
if (uiId) {
|
|
49
|
+
return `[data-ui-id="${uiId}"]`;
|
|
50
|
+
}
|
|
51
|
+
const testId = element.getAttribute("data-testid");
|
|
52
|
+
if (testId) {
|
|
53
|
+
return `[data-testid="${testId}"]`;
|
|
54
|
+
}
|
|
55
|
+
const awasId = element.getAttribute("data-awas-element");
|
|
56
|
+
if (awasId) {
|
|
57
|
+
return `[data-awas-element="${awasId}"]`;
|
|
58
|
+
}
|
|
59
|
+
if (element.id) {
|
|
60
|
+
return `#${CSS.escape(element.id)}`;
|
|
61
|
+
}
|
|
62
|
+
const path = [];
|
|
63
|
+
let current = element;
|
|
64
|
+
while (current && current.nodeType === Node.ELEMENT_NODE) {
|
|
65
|
+
let selector = current.nodeName.toLowerCase();
|
|
66
|
+
const parentUiId = current.getAttribute("data-ui-id");
|
|
67
|
+
if (parentUiId && current !== element) {
|
|
68
|
+
path.unshift(`[data-ui-id="${parentUiId}"]`);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
const parentTestId = current.getAttribute("data-testid");
|
|
72
|
+
if (parentTestId && current !== element) {
|
|
73
|
+
path.unshift(`[data-testid="${parentTestId}"]`);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
if (current.id) {
|
|
77
|
+
path.unshift(`#${CSS.escape(current.id)}`);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
const parentEl = current.parentElement;
|
|
81
|
+
if (parentEl) {
|
|
82
|
+
const currentEl = current;
|
|
83
|
+
const siblings = Array.from(parentEl.children);
|
|
84
|
+
const sameTagSiblings = siblings.filter(
|
|
85
|
+
(s) => s.nodeName === currentEl.nodeName
|
|
86
|
+
);
|
|
87
|
+
if (sameTagSiblings.length > 1) {
|
|
88
|
+
const index = siblings.indexOf(currentEl) + 1;
|
|
89
|
+
selector += `:nth-child(${index})`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
path.unshift(selector);
|
|
93
|
+
current = current.parentElement;
|
|
94
|
+
}
|
|
95
|
+
return path.join(" > ");
|
|
96
|
+
}
|
|
97
|
+
function getBestIdentifier(element) {
|
|
98
|
+
const uiId = element.getAttribute("data-ui-id");
|
|
99
|
+
if (uiId) return uiId;
|
|
100
|
+
const testId = element.getAttribute("data-testid");
|
|
101
|
+
if (testId) return testId;
|
|
102
|
+
const awasId = element.getAttribute("data-awas-element");
|
|
103
|
+
if (awasId) return awasId;
|
|
104
|
+
if (element.id) return element.id;
|
|
105
|
+
return generateCSSSelector(element);
|
|
106
|
+
}
|
|
107
|
+
function createElementIdentifier(element) {
|
|
108
|
+
return {
|
|
109
|
+
uiId: element.getAttribute("data-ui-id") || void 0,
|
|
110
|
+
testId: element.getAttribute("data-testid") || void 0,
|
|
111
|
+
awasId: element.getAttribute("data-awas-element") || void 0,
|
|
112
|
+
htmlId: element.id || void 0,
|
|
113
|
+
xpath: generateXPath(element),
|
|
114
|
+
selector: generateCSSSelector(element)
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/render-log/dom-capture.ts
|
|
119
|
+
var CAPTURE_ATTRIBUTES = [
|
|
120
|
+
"data-ui-id",
|
|
121
|
+
"data-testid",
|
|
122
|
+
"data-awas-element",
|
|
123
|
+
"id",
|
|
124
|
+
"name",
|
|
125
|
+
"type",
|
|
126
|
+
"href",
|
|
127
|
+
"src",
|
|
128
|
+
"alt",
|
|
129
|
+
"title",
|
|
130
|
+
"placeholder",
|
|
131
|
+
"value",
|
|
132
|
+
"aria-label",
|
|
133
|
+
"aria-labelledby",
|
|
134
|
+
"aria-describedby",
|
|
135
|
+
"aria-expanded",
|
|
136
|
+
"aria-selected",
|
|
137
|
+
"aria-checked",
|
|
138
|
+
"aria-disabled",
|
|
139
|
+
"aria-hidden",
|
|
140
|
+
"role",
|
|
141
|
+
"tabindex",
|
|
142
|
+
"disabled",
|
|
143
|
+
"readonly",
|
|
144
|
+
"required",
|
|
145
|
+
"checked"
|
|
146
|
+
];
|
|
147
|
+
var INTERACTIVE_SELECTORS = [
|
|
148
|
+
"a[href]",
|
|
149
|
+
"button",
|
|
150
|
+
"input",
|
|
151
|
+
"select",
|
|
152
|
+
"textarea",
|
|
153
|
+
"[onclick]",
|
|
154
|
+
'[role="button"]',
|
|
155
|
+
'[role="link"]',
|
|
156
|
+
'[role="checkbox"]',
|
|
157
|
+
'[role="radio"]',
|
|
158
|
+
'[role="menuitem"]',
|
|
159
|
+
'[role="tab"]',
|
|
160
|
+
'[role="switch"]',
|
|
161
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
162
|
+
'[contenteditable="true"]'
|
|
163
|
+
];
|
|
164
|
+
function isInteractive(element) {
|
|
165
|
+
return INTERACTIVE_SELECTORS.some((selector) => {
|
|
166
|
+
try {
|
|
167
|
+
return element.matches(selector);
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function getAccessibleName(element) {
|
|
174
|
+
const ariaLabel = element.getAttribute("aria-label");
|
|
175
|
+
if (ariaLabel) return ariaLabel;
|
|
176
|
+
const labelledBy = element.getAttribute("aria-labelledby");
|
|
177
|
+
if (labelledBy) {
|
|
178
|
+
const labels = labelledBy.split(" ").map((id) => document.getElementById(id)?.textContent?.trim()).filter(Boolean);
|
|
179
|
+
if (labels.length > 0) return labels.join(" ");
|
|
180
|
+
}
|
|
181
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement) {
|
|
182
|
+
const id = element.id;
|
|
183
|
+
if (id) {
|
|
184
|
+
const label = document.querySelector(`label[for="${id}"]`);
|
|
185
|
+
if (label) return label.textContent?.trim();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const title = element.getAttribute("title");
|
|
189
|
+
if (title) return title;
|
|
190
|
+
if (element instanceof HTMLImageElement) {
|
|
191
|
+
return element.alt || void 0;
|
|
192
|
+
}
|
|
193
|
+
if (element.matches('button, a, [role="button"], [role="link"]')) {
|
|
194
|
+
return element.textContent?.trim() || void 0;
|
|
195
|
+
}
|
|
196
|
+
return void 0;
|
|
197
|
+
}
|
|
198
|
+
function getElementState(element) {
|
|
199
|
+
const rect = element.getBoundingClientRect();
|
|
200
|
+
const style = window.getComputedStyle(element);
|
|
201
|
+
const state = {
|
|
202
|
+
visible: isVisible(element, rect, style),
|
|
203
|
+
enabled: !isDisabled(element),
|
|
204
|
+
focused: document.activeElement === element,
|
|
205
|
+
rect: {
|
|
206
|
+
x: rect.x,
|
|
207
|
+
y: rect.y,
|
|
208
|
+
width: rect.width,
|
|
209
|
+
height: rect.height,
|
|
210
|
+
top: rect.top,
|
|
211
|
+
right: rect.right,
|
|
212
|
+
bottom: rect.bottom,
|
|
213
|
+
left: rect.left
|
|
214
|
+
},
|
|
215
|
+
computedStyles: {
|
|
216
|
+
display: style.display,
|
|
217
|
+
visibility: style.visibility,
|
|
218
|
+
opacity: style.opacity,
|
|
219
|
+
pointerEvents: style.pointerEvents
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
if (element instanceof HTMLInputElement) {
|
|
223
|
+
state.value = element.value;
|
|
224
|
+
if (element.type === "checkbox" || element.type === "radio") {
|
|
225
|
+
state.checked = element.checked;
|
|
226
|
+
}
|
|
227
|
+
} else if (element instanceof HTMLTextAreaElement) {
|
|
228
|
+
state.value = element.value;
|
|
229
|
+
} else if (element instanceof HTMLSelectElement) {
|
|
230
|
+
state.value = element.value;
|
|
231
|
+
state.selectedOptions = Array.from(element.selectedOptions).map((opt) => opt.value);
|
|
232
|
+
}
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
function isVisible(element, rect, style) {
|
|
236
|
+
if (rect.width === 0 || rect.height === 0) return false;
|
|
237
|
+
if (style.display === "none") return false;
|
|
238
|
+
if (style.visibility === "hidden") return false;
|
|
239
|
+
if (parseFloat(style.opacity) === 0) return false;
|
|
240
|
+
if (element.getAttribute("aria-hidden") === "true") return false;
|
|
241
|
+
return rect.top < window.innerHeight && rect.bottom > 0 && rect.left < window.innerWidth && rect.right > 0;
|
|
242
|
+
}
|
|
243
|
+
function isDisabled(element) {
|
|
244
|
+
if ("disabled" in element && element.disabled) return true;
|
|
245
|
+
if (element.getAttribute("aria-disabled") === "true") return true;
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
function captureAttributes(element) {
|
|
249
|
+
const attrs = {};
|
|
250
|
+
for (const attr of CAPTURE_ATTRIBUTES) {
|
|
251
|
+
const value = element.getAttribute(attr);
|
|
252
|
+
if (value !== null) {
|
|
253
|
+
attrs[attr] = value;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return attrs;
|
|
257
|
+
}
|
|
258
|
+
function captureElement(element, depth, maxTextLength) {
|
|
259
|
+
const identifier = createElementIdentifier(element);
|
|
260
|
+
let textContent = element.textContent?.trim();
|
|
261
|
+
if (textContent && textContent.length > maxTextLength) {
|
|
262
|
+
textContent = textContent.substring(0, maxTextLength) + "...";
|
|
263
|
+
}
|
|
264
|
+
return {
|
|
265
|
+
identifier,
|
|
266
|
+
bestId: getBestIdentifier(element),
|
|
267
|
+
tagName: element.tagName.toLowerCase(),
|
|
268
|
+
role: element.getAttribute("role") || void 0,
|
|
269
|
+
accessibleName: getAccessibleName(element),
|
|
270
|
+
textContent,
|
|
271
|
+
state: getElementState(element),
|
|
272
|
+
attributes: captureAttributes(element),
|
|
273
|
+
childCount: element.children.length,
|
|
274
|
+
depth
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function captureDOMSnapshot(options = {}) {
|
|
278
|
+
const startTime = performance.now();
|
|
279
|
+
const {
|
|
280
|
+
root = document.body,
|
|
281
|
+
maxDepth = 50,
|
|
282
|
+
maxElements = 5e3,
|
|
283
|
+
interactiveOnly = false,
|
|
284
|
+
includeHidden = false,
|
|
285
|
+
includeSelectors,
|
|
286
|
+
excludeSelectors,
|
|
287
|
+
filter,
|
|
288
|
+
maxTextLength = 200
|
|
289
|
+
} = options;
|
|
290
|
+
const elements = [];
|
|
291
|
+
let totalNodeCount = 0;
|
|
292
|
+
function shouldCapture(element) {
|
|
293
|
+
if (filter && !filter(element)) return false;
|
|
294
|
+
if (excludeSelectors) {
|
|
295
|
+
for (const selector of excludeSelectors) {
|
|
296
|
+
try {
|
|
297
|
+
if (element.matches(selector)) return false;
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (includeSelectors && includeSelectors.length > 0) {
|
|
303
|
+
let matches = false;
|
|
304
|
+
for (const selector of includeSelectors) {
|
|
305
|
+
try {
|
|
306
|
+
if (element.matches(selector)) {
|
|
307
|
+
matches = true;
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!matches) return false;
|
|
314
|
+
}
|
|
315
|
+
if (interactiveOnly && !isInteractive(element)) return false;
|
|
316
|
+
if (!includeHidden) {
|
|
317
|
+
const rect = element.getBoundingClientRect();
|
|
318
|
+
const style = window.getComputedStyle(element);
|
|
319
|
+
if (!isVisible(element, rect, style)) return false;
|
|
320
|
+
}
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
function traverse(element, depth) {
|
|
324
|
+
if (depth > maxDepth || elements.length >= maxElements) return;
|
|
325
|
+
totalNodeCount++;
|
|
326
|
+
if (shouldCapture(element)) {
|
|
327
|
+
elements.push(captureElement(element, depth, maxTextLength));
|
|
328
|
+
}
|
|
329
|
+
for (const child of element.children) {
|
|
330
|
+
if (child instanceof HTMLElement) {
|
|
331
|
+
traverse(child, depth + 1);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
traverse(root, 0);
|
|
336
|
+
const endTime = performance.now();
|
|
337
|
+
return {
|
|
338
|
+
timestamp: Date.now(),
|
|
339
|
+
url: window.location.href,
|
|
340
|
+
title: document.title,
|
|
341
|
+
viewport: {
|
|
342
|
+
width: window.innerWidth,
|
|
343
|
+
height: window.innerHeight,
|
|
344
|
+
scrollX: window.scrollX,
|
|
345
|
+
scrollY: window.scrollY
|
|
346
|
+
},
|
|
347
|
+
elements,
|
|
348
|
+
totalNodeCount,
|
|
349
|
+
captureDurationMs: endTime - startTime
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function captureInteractiveElements(options = {}) {
|
|
353
|
+
return captureDOMSnapshot({ ...options, interactiveOnly: true });
|
|
354
|
+
}
|
|
355
|
+
var DOMChangeObserver = class {
|
|
356
|
+
constructor(options = {}) {
|
|
357
|
+
this.observer = null;
|
|
358
|
+
this.changes = [];
|
|
359
|
+
this.maxChanges = options.maxChanges ?? 1e3;
|
|
360
|
+
this.callback = options.callback;
|
|
361
|
+
}
|
|
362
|
+
start(root = document.body) {
|
|
363
|
+
if (this.observer) return;
|
|
364
|
+
this.observer = new MutationObserver((mutations) => {
|
|
365
|
+
for (const mutation of mutations) {
|
|
366
|
+
const change = this.processMutation(mutation);
|
|
367
|
+
if (change) {
|
|
368
|
+
this.addChange(change);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
this.observer.observe(root, {
|
|
373
|
+
childList: true,
|
|
374
|
+
attributes: true,
|
|
375
|
+
characterData: true,
|
|
376
|
+
subtree: true,
|
|
377
|
+
attributeOldValue: true
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
stop() {
|
|
381
|
+
this.observer?.disconnect();
|
|
382
|
+
this.observer = null;
|
|
383
|
+
}
|
|
384
|
+
processMutation(mutation) {
|
|
385
|
+
const target = mutation.target;
|
|
386
|
+
if (!(target instanceof HTMLElement)) return null;
|
|
387
|
+
const elementId = getBestIdentifier(target);
|
|
388
|
+
if (mutation.type === "attributes") {
|
|
389
|
+
return {
|
|
390
|
+
timestamp: Date.now(),
|
|
391
|
+
type: "attribute",
|
|
392
|
+
elementId,
|
|
393
|
+
tagName: target.tagName.toLowerCase(),
|
|
394
|
+
details: {
|
|
395
|
+
attributeName: mutation.attributeName || void 0,
|
|
396
|
+
oldValue: mutation.oldValue || void 0,
|
|
397
|
+
newValue: mutation.attributeName ? target.getAttribute(mutation.attributeName) || void 0 : void 0
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (mutation.type === "childList") {
|
|
402
|
+
if (mutation.addedNodes.length > 0) {
|
|
403
|
+
return {
|
|
404
|
+
timestamp: Date.now(),
|
|
405
|
+
type: "added",
|
|
406
|
+
elementId,
|
|
407
|
+
tagName: target.tagName.toLowerCase(),
|
|
408
|
+
details: {
|
|
409
|
+
addedNodes: mutation.addedNodes.length
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (mutation.removedNodes.length > 0) {
|
|
414
|
+
return {
|
|
415
|
+
timestamp: Date.now(),
|
|
416
|
+
type: "removed",
|
|
417
|
+
elementId,
|
|
418
|
+
tagName: target.tagName.toLowerCase(),
|
|
419
|
+
details: {
|
|
420
|
+
removedNodes: mutation.removedNodes.length
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
addChange(change) {
|
|
428
|
+
this.changes.push(change);
|
|
429
|
+
if (this.changes.length > this.maxChanges) {
|
|
430
|
+
this.changes.shift();
|
|
431
|
+
}
|
|
432
|
+
this.callback?.(change);
|
|
433
|
+
}
|
|
434
|
+
getChanges() {
|
|
435
|
+
return [...this.changes];
|
|
436
|
+
}
|
|
437
|
+
clearChanges() {
|
|
438
|
+
this.changes = [];
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
// src/render-log/snapshot.ts
|
|
443
|
+
var InMemoryRenderLogStorage = class {
|
|
444
|
+
constructor(maxEntries = 1e3) {
|
|
445
|
+
this.entries = [];
|
|
446
|
+
this.maxEntries = maxEntries;
|
|
447
|
+
}
|
|
448
|
+
async append(entry) {
|
|
449
|
+
this.entries.push(entry);
|
|
450
|
+
while (this.entries.length > this.maxEntries) {
|
|
451
|
+
this.entries.shift();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
async getEntries(options) {
|
|
455
|
+
let results = [...this.entries];
|
|
456
|
+
if (options?.type) {
|
|
457
|
+
results = results.filter((e) => e.type === options.type);
|
|
458
|
+
}
|
|
459
|
+
if (options?.since) {
|
|
460
|
+
results = results.filter((e) => e.timestamp >= options.since);
|
|
461
|
+
}
|
|
462
|
+
if (options?.until) {
|
|
463
|
+
results = results.filter((e) => e.timestamp <= options.until);
|
|
464
|
+
}
|
|
465
|
+
if (options?.limit) {
|
|
466
|
+
results = results.slice(-options.limit);
|
|
467
|
+
}
|
|
468
|
+
return results;
|
|
469
|
+
}
|
|
470
|
+
async clear() {
|
|
471
|
+
this.entries = [];
|
|
472
|
+
}
|
|
473
|
+
async count() {
|
|
474
|
+
return this.entries.length;
|
|
475
|
+
}
|
|
476
|
+
/** Get entries synchronously (for in-memory only) */
|
|
477
|
+
getEntriesSync() {
|
|
478
|
+
return [...this.entries];
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
function generateId() {
|
|
482
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
483
|
+
}
|
|
484
|
+
var RenderLogManager = class {
|
|
485
|
+
constructor(options = {}) {
|
|
486
|
+
this.changeObserver = null;
|
|
487
|
+
this.snapshotTimer = null;
|
|
488
|
+
this.pendingChanges = [];
|
|
489
|
+
this.started = false;
|
|
490
|
+
this.options = options;
|
|
491
|
+
this.storage = options.storage ?? new InMemoryRenderLogStorage(options.maxEntries);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Start capturing
|
|
495
|
+
*/
|
|
496
|
+
start() {
|
|
497
|
+
if (this.started) return;
|
|
498
|
+
this.started = true;
|
|
499
|
+
if (this.options.captureChanges !== false) {
|
|
500
|
+
this.changeObserver = new DOMChangeObserver({
|
|
501
|
+
callback: (change) => {
|
|
502
|
+
this.pendingChanges.push(change);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
this.changeObserver.start();
|
|
506
|
+
}
|
|
507
|
+
if (this.options.captureOnNavigation !== false) {
|
|
508
|
+
this.setupNavigationObserver();
|
|
509
|
+
}
|
|
510
|
+
if (this.options.snapshotInterval) {
|
|
511
|
+
this.snapshotTimer = setInterval(() => {
|
|
512
|
+
this.captureSnapshot();
|
|
513
|
+
}, this.options.snapshotInterval);
|
|
514
|
+
}
|
|
515
|
+
this.captureSnapshot();
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Stop capturing
|
|
519
|
+
*/
|
|
520
|
+
stop() {
|
|
521
|
+
if (!this.started) return;
|
|
522
|
+
this.started = false;
|
|
523
|
+
this.changeObserver?.stop();
|
|
524
|
+
this.changeObserver = null;
|
|
525
|
+
if (this.snapshotTimer) {
|
|
526
|
+
clearInterval(this.snapshotTimer);
|
|
527
|
+
this.snapshotTimer = null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Capture a DOM snapshot
|
|
532
|
+
*/
|
|
533
|
+
async captureSnapshot(metadata) {
|
|
534
|
+
if (this.pendingChanges.length > 0) {
|
|
535
|
+
await this.flushChanges();
|
|
536
|
+
}
|
|
537
|
+
const snapshot = captureDOMSnapshot(this.options.captureOptions);
|
|
538
|
+
const entry = {
|
|
539
|
+
id: generateId(),
|
|
540
|
+
type: "snapshot",
|
|
541
|
+
timestamp: snapshot.timestamp,
|
|
542
|
+
data: snapshot,
|
|
543
|
+
metadata
|
|
544
|
+
};
|
|
545
|
+
await this.addEntry(entry);
|
|
546
|
+
return entry;
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Flush pending DOM changes
|
|
550
|
+
*/
|
|
551
|
+
async flushChanges() {
|
|
552
|
+
if (this.pendingChanges.length === 0) return null;
|
|
553
|
+
const changes = [...this.pendingChanges];
|
|
554
|
+
this.pendingChanges = [];
|
|
555
|
+
const entry = {
|
|
556
|
+
id: generateId(),
|
|
557
|
+
type: "change",
|
|
558
|
+
timestamp: Date.now(),
|
|
559
|
+
data: changes
|
|
560
|
+
};
|
|
561
|
+
await this.addEntry(entry);
|
|
562
|
+
return entry;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Log an interaction
|
|
566
|
+
*/
|
|
567
|
+
async logInteraction(eventType, details) {
|
|
568
|
+
const entry = {
|
|
569
|
+
id: generateId(),
|
|
570
|
+
type: "interaction",
|
|
571
|
+
timestamp: Date.now(),
|
|
572
|
+
data: {
|
|
573
|
+
eventType,
|
|
574
|
+
...details
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
await this.addEntry(entry);
|
|
578
|
+
return entry;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Log an error
|
|
582
|
+
*/
|
|
583
|
+
async logError(message, details) {
|
|
584
|
+
const entry = {
|
|
585
|
+
id: generateId(),
|
|
586
|
+
type: "error",
|
|
587
|
+
timestamp: Date.now(),
|
|
588
|
+
data: {
|
|
589
|
+
message,
|
|
590
|
+
...details
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
await this.addEntry(entry);
|
|
594
|
+
return entry;
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Log a navigation
|
|
598
|
+
*/
|
|
599
|
+
async logNavigation(from, to, navigationType) {
|
|
600
|
+
const entry = {
|
|
601
|
+
id: generateId(),
|
|
602
|
+
type: "navigation",
|
|
603
|
+
timestamp: Date.now(),
|
|
604
|
+
data: {
|
|
605
|
+
from,
|
|
606
|
+
to,
|
|
607
|
+
navigationType
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
await this.addEntry(entry);
|
|
611
|
+
return entry;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Add a custom entry
|
|
615
|
+
*/
|
|
616
|
+
async logCustom(data, metadata) {
|
|
617
|
+
const entry = {
|
|
618
|
+
id: generateId(),
|
|
619
|
+
type: "custom",
|
|
620
|
+
timestamp: Date.now(),
|
|
621
|
+
data,
|
|
622
|
+
metadata
|
|
623
|
+
};
|
|
624
|
+
await this.addEntry(entry);
|
|
625
|
+
return entry;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Get log entries
|
|
629
|
+
*/
|
|
630
|
+
async getEntries(options) {
|
|
631
|
+
return this.storage.getEntries(options);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Clear the log
|
|
635
|
+
*/
|
|
636
|
+
async clear() {
|
|
637
|
+
this.pendingChanges = [];
|
|
638
|
+
await this.storage.clear();
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Get entry count
|
|
642
|
+
*/
|
|
643
|
+
async count() {
|
|
644
|
+
return this.storage.count();
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Get the latest snapshot
|
|
648
|
+
*/
|
|
649
|
+
async getLatestSnapshot() {
|
|
650
|
+
const snapshots = await this.storage.getEntries({ type: "snapshot", limit: 1 });
|
|
651
|
+
return snapshots[0] || null;
|
|
652
|
+
}
|
|
653
|
+
async addEntry(entry) {
|
|
654
|
+
await this.storage.append(entry);
|
|
655
|
+
this.options.onEntry?.(entry);
|
|
656
|
+
}
|
|
657
|
+
setupNavigationObserver() {
|
|
658
|
+
let lastUrl = window.location.href;
|
|
659
|
+
const originalPushState = history.pushState;
|
|
660
|
+
const originalReplaceState = history.replaceState;
|
|
661
|
+
history.pushState = (...args) => {
|
|
662
|
+
const result = originalPushState.apply(history, args);
|
|
663
|
+
const newUrl = window.location.href;
|
|
664
|
+
if (newUrl !== lastUrl) {
|
|
665
|
+
this.logNavigation(lastUrl, newUrl, "push");
|
|
666
|
+
this.captureSnapshot({ trigger: "navigation" });
|
|
667
|
+
lastUrl = newUrl;
|
|
668
|
+
}
|
|
669
|
+
return result;
|
|
670
|
+
};
|
|
671
|
+
history.replaceState = (...args) => {
|
|
672
|
+
const result = originalReplaceState.apply(history, args);
|
|
673
|
+
const newUrl = window.location.href;
|
|
674
|
+
if (newUrl !== lastUrl) {
|
|
675
|
+
this.logNavigation(lastUrl, newUrl, "replace");
|
|
676
|
+
this.captureSnapshot({ trigger: "navigation" });
|
|
677
|
+
lastUrl = newUrl;
|
|
678
|
+
}
|
|
679
|
+
return result;
|
|
680
|
+
};
|
|
681
|
+
window.addEventListener("popstate", () => {
|
|
682
|
+
const newUrl = window.location.href;
|
|
683
|
+
if (newUrl !== lastUrl) {
|
|
684
|
+
this.logNavigation(lastUrl, newUrl, "pop");
|
|
685
|
+
this.captureSnapshot({ trigger: "navigation" });
|
|
686
|
+
lastUrl = newUrl;
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
function createRenderLogManager(options) {
|
|
692
|
+
return new RenderLogManager(options);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
exports.DOMChangeObserver = DOMChangeObserver;
|
|
696
|
+
exports.InMemoryRenderLogStorage = InMemoryRenderLogStorage;
|
|
697
|
+
exports.RenderLogManager = RenderLogManager;
|
|
698
|
+
exports.captureDOMSnapshot = captureDOMSnapshot;
|
|
699
|
+
exports.captureInteractiveElements = captureInteractiveElements;
|
|
700
|
+
exports.createRenderLogManager = createRenderLogManager;
|
|
701
|
+
//# sourceMappingURL=index.js.map
|
|
702
|
+
//# sourceMappingURL=index.js.map
|