@qontinui/ui-bridge 0.3.0 → 0.3.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 (127) hide show
  1. package/dist/ai/index.d.mts +312 -155
  2. package/dist/ai/index.d.ts +312 -155
  3. package/dist/ai/index.js +2363 -67
  4. package/dist/ai/index.js.map +1 -1
  5. package/dist/ai/index.mjs +2328 -68
  6. package/dist/ai/index.mjs.map +1 -1
  7. package/dist/annotations/index.d.mts +218 -0
  8. package/dist/annotations/index.d.ts +218 -0
  9. package/dist/annotations/index.js +246 -0
  10. package/dist/annotations/index.js.map +1 -0
  11. package/dist/annotations/index.mjs +241 -0
  12. package/dist/annotations/index.mjs.map +1 -0
  13. package/dist/assertions-BSR3afVr.d.ts +161 -0
  14. package/dist/assertions-CTw1hfOx.d.mts +161 -0
  15. package/dist/babel-plugin/index.js +23 -34
  16. package/dist/babel-plugin/index.js.map +1 -1
  17. package/dist/babel-plugin/index.mjs +23 -34
  18. package/dist/babel-plugin/index.mjs.map +1 -1
  19. package/dist/browser-capture-Bms60T6f.d.mts +47 -0
  20. package/dist/browser-capture-CsTU29mb.d.ts +47 -0
  21. package/dist/control/index.d.mts +26 -7
  22. package/dist/control/index.d.ts +26 -7
  23. package/dist/control/index.js +276 -48
  24. package/dist/control/index.js.map +1 -1
  25. package/dist/control/index.mjs +276 -48
  26. package/dist/control/index.mjs.map +1 -1
  27. package/dist/core/index.d.mts +2 -2
  28. package/dist/core/index.d.ts +2 -2
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/core/index.mjs.map +1 -1
  31. package/dist/debug/index.d.mts +5 -3
  32. package/dist/debug/index.d.ts +5 -3
  33. package/dist/debug/index.js +925 -1
  34. package/dist/debug/index.js.map +1 -1
  35. package/dist/debug/index.mjs +924 -2
  36. package/dist/debug/index.mjs.map +1 -1
  37. package/dist/index.d.mts +12 -7
  38. package/dist/index.d.ts +12 -7
  39. package/dist/index.js +4720 -173
  40. package/dist/index.js.map +1 -1
  41. package/dist/index.mjs +4656 -174
  42. package/dist/index.mjs.map +1 -1
  43. package/dist/{metrics-DTA2bwG7.d.mts → metrics-DuA2qIIz.d.mts} +2 -2
  44. package/dist/{metrics-BfiT_rhZ.d.ts → metrics-KFAAKNEB.d.ts} +2 -2
  45. package/dist/native/control/index.js +2 -7
  46. package/dist/native/control/index.js.map +1 -1
  47. package/dist/native/control/index.mjs +2 -7
  48. package/dist/native/control/index.mjs.map +1 -1
  49. package/dist/native/core/index.js.map +1 -1
  50. package/dist/native/core/index.mjs.map +1 -1
  51. package/dist/native/debug/index.js +23 -66
  52. package/dist/native/debug/index.js.map +1 -1
  53. package/dist/native/debug/index.mjs +23 -66
  54. package/dist/native/debug/index.mjs.map +1 -1
  55. package/dist/native/index.js +89 -131
  56. package/dist/native/index.js.map +1 -1
  57. package/dist/native/index.mjs +89 -131
  58. package/dist/native/index.mjs.map +1 -1
  59. package/dist/native/react/index.js +28 -52
  60. package/dist/native/react/index.js.map +1 -1
  61. package/dist/native/react/index.mjs +28 -52
  62. package/dist/native/react/index.mjs.map +1 -1
  63. package/dist/native/server/index.js +38 -13
  64. package/dist/native/server/index.js.map +1 -1
  65. package/dist/native/server/index.mjs +38 -13
  66. package/dist/native/server/index.mjs.map +1 -1
  67. package/dist/react/index.d.mts +107 -8
  68. package/dist/react/index.d.ts +107 -8
  69. package/dist/react/index.js +2194 -84
  70. package/dist/react/index.js.map +1 -1
  71. package/dist/react/index.mjs +2194 -85
  72. package/dist/react/index.mjs.map +1 -1
  73. package/dist/{registry-BKLEm-yk.d.ts → registry-C6dDtn1v.d.ts} +27 -2
  74. package/dist/{registry-BmZgyCz8.d.mts → registry-POtcxnal.d.mts} +27 -2
  75. package/dist/render-log/index.d.mts +1 -1
  76. package/dist/render-log/index.d.ts +1 -1
  77. package/dist/server/express.d.mts +5 -4
  78. package/dist/server/express.d.ts +5 -4
  79. package/dist/server/express.js +104 -2
  80. package/dist/server/express.js.map +1 -1
  81. package/dist/server/express.mjs +104 -2
  82. package/dist/server/express.mjs.map +1 -1
  83. package/dist/server/handlers.d.mts +36 -5
  84. package/dist/server/handlers.d.ts +36 -5
  85. package/dist/server/handlers.js +3129 -224
  86. package/dist/server/handlers.js.map +1 -1
  87. package/dist/server/handlers.mjs +3129 -224
  88. package/dist/server/handlers.mjs.map +1 -1
  89. package/dist/server/index.d.mts +7 -5
  90. package/dist/server/index.d.ts +7 -5
  91. package/dist/server/index.js +3215 -183
  92. package/dist/server/index.js.map +1 -1
  93. package/dist/server/index.mjs +3215 -183
  94. package/dist/server/index.mjs.map +1 -1
  95. package/dist/server/nextjs.d.mts +6 -4
  96. package/dist/server/nextjs.d.ts +6 -4
  97. package/dist/server/nextjs.js +106 -3
  98. package/dist/server/nextjs.js.map +1 -1
  99. package/dist/server/nextjs.mjs +106 -3
  100. package/dist/server/nextjs.mjs.map +1 -1
  101. package/dist/server/standalone.d.mts +6 -5
  102. package/dist/server/standalone.d.ts +6 -5
  103. package/dist/server/standalone.js +131 -5
  104. package/dist/server/standalone.js.map +1 -1
  105. package/dist/server/standalone.mjs +131 -5
  106. package/dist/server/standalone.mjs.map +1 -1
  107. package/dist/specs/index.d.mts +365 -0
  108. package/dist/specs/index.d.ts +365 -0
  109. package/dist/specs/index.js +2809 -0
  110. package/dist/specs/index.js.map +1 -0
  111. package/dist/specs/index.mjs +2786 -0
  112. package/dist/specs/index.mjs.map +1 -0
  113. package/dist/{standalone-BURj8J3G.d.ts → standalone-B6GLIEmR.d.ts} +6 -2
  114. package/dist/{standalone-Dwmel29d.d.mts → standalone-CjdYqj3P.d.mts} +6 -2
  115. package/dist/{types-CHnlwiTK.d.ts → types-B2EfvEaq.d.ts} +83 -3
  116. package/dist/{types-B7J7noLK.d.mts → types-C7gVYRnF.d.ts} +72 -2
  117. package/dist/{types-BkNRILUa.d.ts → types-CJGrBEhC.d.mts} +72 -2
  118. package/dist/types-CebMQj76.d.ts +1275 -0
  119. package/dist/types-D_ypYl3T.d.mts +1275 -0
  120. package/dist/types-UBtp7R0u.d.mts +132 -0
  121. package/dist/types-UBtp7R0u.d.ts +132 -0
  122. package/dist/{types-CEQLnFMv.d.mts → types-gO696T_t.d.mts} +83 -3
  123. package/dist/{types-jKVgTI6_.d.mts → types-suaYwWWg.d.mts} +173 -2
  124. package/dist/{types-jKVgTI6_.d.ts → types-suaYwWWg.d.ts} +173 -2
  125. package/package.json +18 -2
  126. package/dist/types-B5Q0GVo0.d.mts +0 -646
  127. package/dist/types-DfPqwU-i.d.ts +0 -646
@@ -119,6 +119,241 @@ function createElementIdentifier(element) {
119
119
  selector: generateCSSSelector(element)
120
120
  };
121
121
  }
122
+
123
+ // src/annotations/types.ts
124
+ var ANNOTATION_CONFIG_VERSION = "1.0.0";
125
+
126
+ // src/annotations/store.ts
127
+ var AnnotationStore = class {
128
+ constructor() {
129
+ this.store = /* @__PURE__ */ new Map();
130
+ this.listeners = /* @__PURE__ */ new Set();
131
+ }
132
+ /**
133
+ * Get an annotation by element ID.
134
+ */
135
+ get(elementId) {
136
+ return this.store.get(elementId);
137
+ }
138
+ /**
139
+ * Get all annotations as a record.
140
+ */
141
+ getAll() {
142
+ const result = {};
143
+ for (const [id, annotation] of this.store) {
144
+ result[id] = annotation;
145
+ }
146
+ return result;
147
+ }
148
+ /**
149
+ * Set an annotation for an element. Auto-sets `updatedAt`.
150
+ */
151
+ set(elementId, annotation) {
152
+ const updated = {
153
+ ...annotation,
154
+ updatedAt: Date.now()
155
+ };
156
+ this.store.set(elementId, updated);
157
+ this.emit({
158
+ type: "annotation:set",
159
+ elementId,
160
+ annotation: updated,
161
+ timestamp: Date.now()
162
+ });
163
+ }
164
+ /**
165
+ * Delete an annotation by element ID.
166
+ *
167
+ * @returns true if the annotation existed and was deleted
168
+ */
169
+ delete(elementId) {
170
+ const existed = this.store.delete(elementId);
171
+ if (existed) {
172
+ this.emit({
173
+ type: "annotation:deleted",
174
+ elementId,
175
+ timestamp: Date.now()
176
+ });
177
+ }
178
+ return existed;
179
+ }
180
+ /**
181
+ * Check if an annotation exists for an element.
182
+ */
183
+ has(elementId) {
184
+ return this.store.has(elementId);
185
+ }
186
+ /**
187
+ * Get the number of stored annotations.
188
+ */
189
+ get count() {
190
+ return this.store.size;
191
+ }
192
+ /**
193
+ * Clear all annotations.
194
+ */
195
+ clear() {
196
+ this.store.clear();
197
+ this.emit({
198
+ type: "annotation:cleared",
199
+ timestamp: Date.now()
200
+ });
201
+ }
202
+ /**
203
+ * Import annotations from a config object.
204
+ *
205
+ * Merges with existing annotations (new values overwrite per element ID).
206
+ *
207
+ * @returns Number of annotations imported
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * const config: AnnotationConfig = {
212
+ * version: '1.0.0',
213
+ * annotations: {
214
+ * 'btn-1': { description: 'Submit button', tags: ['form'] },
215
+ * 'input-1': { description: 'Name field' },
216
+ * },
217
+ * };
218
+ * const count = store.importConfig(config); // 2
219
+ * ```
220
+ */
221
+ importConfig(config) {
222
+ let count = 0;
223
+ for (const [id, annotation] of Object.entries(config.annotations)) {
224
+ this.store.set(id, {
225
+ ...annotation,
226
+ updatedAt: annotation.updatedAt ?? Date.now()
227
+ });
228
+ count++;
229
+ }
230
+ this.emit({
231
+ type: "annotation:imported",
232
+ count,
233
+ timestamp: Date.now()
234
+ });
235
+ return count;
236
+ }
237
+ /**
238
+ * Export all annotations as a config object.
239
+ *
240
+ * The returned object can be serialized to JSON and saved to a file,
241
+ * then later re-imported with {@link importConfig}.
242
+ *
243
+ * @param metadata - Optional metadata to include (appName, description, etc.)
244
+ * @returns AnnotationConfig with all current annotations
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * const config = store.exportConfig({ appName: 'MyApp' });
249
+ * // config.version === '1.0.0'
250
+ * // config.annotations === { 'btn-1': { ... }, 'input-1': { ... } }
251
+ * // config.metadata === { appName: 'MyApp', exportedAt: 1706900000000 }
252
+ *
253
+ * // Save to file
254
+ * fs.writeFileSync('annotations.json', JSON.stringify(config, null, 2));
255
+ * ```
256
+ */
257
+ exportConfig(metadata) {
258
+ return {
259
+ version: ANNOTATION_CONFIG_VERSION,
260
+ annotations: this.getAll(),
261
+ metadata: {
262
+ ...metadata,
263
+ exportedAt: Date.now()
264
+ }
265
+ };
266
+ }
267
+ /**
268
+ * Compute annotation coverage against a set of known element IDs.
269
+ *
270
+ * Compares the store's annotations against the provided list of element IDs
271
+ * to determine what percentage of elements have been annotated.
272
+ *
273
+ * @param allElementIds - Array of all known element IDs in the UI
274
+ * @returns Coverage statistics including percentages and lists of annotated/unannotated IDs
275
+ *
276
+ * @example
277
+ * ```ts
278
+ * store.set('btn-1', { description: 'Submit' });
279
+ * store.set('input-1', { description: 'Name' });
280
+ *
281
+ * const coverage = store.getCoverage(['btn-1', 'input-1', 'input-2', 'link-1']);
282
+ * // coverage.totalElements === 4
283
+ * // coverage.annotatedElements === 2
284
+ * // coverage.coveragePercent === 50
285
+ * // coverage.annotatedIds === ['btn-1', 'input-1']
286
+ * // coverage.unannotatedIds === ['input-2', 'link-1']
287
+ * ```
288
+ */
289
+ getCoverage(allElementIds) {
290
+ const annotatedIds = [];
291
+ const unannotatedIds = [];
292
+ for (const id of allElementIds) {
293
+ if (this.store.has(id)) {
294
+ annotatedIds.push(id);
295
+ } else {
296
+ unannotatedIds.push(id);
297
+ }
298
+ }
299
+ const total = allElementIds.length;
300
+ return {
301
+ totalElements: total,
302
+ annotatedElements: annotatedIds.length,
303
+ coveragePercent: total > 0 ? annotatedIds.length / total * 100 : 0,
304
+ annotatedIds,
305
+ unannotatedIds,
306
+ timestamp: Date.now()
307
+ };
308
+ }
309
+ /**
310
+ * Subscribe to annotation events.
311
+ *
312
+ * The listener is called whenever annotations are set, deleted, imported,
313
+ * or cleared. Returns an unsubscribe function to stop listening.
314
+ *
315
+ * @param listener - Callback function receiving {@link AnnotationEvent} objects
316
+ * @returns Unsubscribe function - call it to remove the listener
317
+ *
318
+ * @example
319
+ * ```ts
320
+ * const unsubscribe = store.on((event) => {
321
+ * if (event.type === 'annotation:set') {
322
+ * console.log(`Element ${event.elementId} annotated:`, event.annotation);
323
+ * }
324
+ * });
325
+ *
326
+ * store.set('btn-1', { description: 'Submit' });
327
+ * // Logs: "Element btn-1 annotated: { description: 'Submit', updatedAt: ... }"
328
+ *
329
+ * unsubscribe(); // Stop listening
330
+ * ```
331
+ */
332
+ on(listener) {
333
+ this.listeners.add(listener);
334
+ return () => {
335
+ this.listeners.delete(listener);
336
+ };
337
+ }
338
+ /**
339
+ * Emit an event to all listeners.
340
+ */
341
+ emit(event) {
342
+ for (const listener of this.listeners) {
343
+ try {
344
+ listener(event);
345
+ } catch {
346
+ }
347
+ }
348
+ }
349
+ };
350
+ var globalStore = null;
351
+ function getGlobalAnnotationStore() {
352
+ if (!globalStore) {
353
+ globalStore = new AnnotationStore();
354
+ }
355
+ return globalStore;
356
+ }
122
357
  var overlayStyles = {
123
358
  position: "fixed",
124
359
  pointerEvents: "none",
@@ -334,7 +569,7 @@ function InfoPanel({ element, onClose, registeredElement }) {
334
569
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: identifier.xpath })
335
570
  ] })
336
571
  ] }),
337
- registeredElement && registeredElement.actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...sectionStyles, borderBottom: "none" }, children: [
572
+ registeredElement && registeredElement.actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: sectionStyles, children: [
338
573
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Actions" }),
339
574
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px" }, children: registeredElement.actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
340
575
  "span",
@@ -349,6 +584,43 @@ function InfoPanel({ element, onClose, registeredElement }) {
349
584
  },
350
585
  action
351
586
  )) })
587
+ ] }),
588
+ /* @__PURE__ */ jsxRuntime.jsx(AnnotationSection, { elementId: bestId })
589
+ ] });
590
+ }
591
+ function AnnotationSection({ elementId }) {
592
+ const annotation = getGlobalAnnotationStore().get(elementId);
593
+ if (!annotation) return null;
594
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...sectionStyles, borderBottom: "none" }, children: [
595
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "8px", fontWeight: "bold", color: "#f3f4f6" }, children: "Annotation" }),
596
+ annotation.description && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "4px" }, children: [
597
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Description:" }),
598
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: annotation.description })
599
+ ] }),
600
+ annotation.purpose && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "4px" }, children: [
601
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Purpose:" }),
602
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: valueStyles, children: annotation.purpose })
603
+ ] }),
604
+ annotation.notes && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "4px" }, children: [
605
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Notes:" }),
606
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#fbbf24" }, children: annotation.notes })
607
+ ] }),
608
+ annotation.tags && annotation.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
609
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelKeyStyles, children: "Tags:" }),
610
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-flex", flexWrap: "wrap", gap: "4px" }, children: annotation.tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx(
611
+ "span",
612
+ {
613
+ style: {
614
+ padding: "1px 6px",
615
+ backgroundColor: "#1e3a5f",
616
+ borderRadius: "4px",
617
+ color: "#93c5fd",
618
+ fontSize: "11px"
619
+ },
620
+ children: tag
621
+ },
622
+ tag
623
+ )) })
352
624
  ] })
353
625
  ] });
354
626
  }
@@ -445,6 +717,656 @@ function Inspector({ getRegisteredElement, initialActive }) {
445
717
  ] });
446
718
  }
447
719
 
720
+ // src/debug/browser-capture-types.ts
721
+ var DEFAULT_CAPTURE_CONFIG = {
722
+ console: true,
723
+ network: true,
724
+ navigation: true,
725
+ longTasks: true,
726
+ longAnimationFrames: true,
727
+ resourceErrors: true,
728
+ wsDisconnections: true,
729
+ hmr: true,
730
+ webVitals: false,
731
+ memory: false,
732
+ memoryIntervalMs: 3e4,
733
+ maxEntries: 200
734
+ };
735
+
736
+ // src/debug/captures/console.ts
737
+ function argsToMessage(args) {
738
+ return args.map((a) => {
739
+ if (a instanceof Error) return a.message;
740
+ if (typeof a === "string") return a;
741
+ try {
742
+ return JSON.stringify(a);
743
+ } catch {
744
+ return String(a);
745
+ }
746
+ }).join(" ");
747
+ }
748
+ function extractStack(args) {
749
+ const err = args.find((a) => a instanceof Error);
750
+ return err?.stack;
751
+ }
752
+ function makeEvent(level, message, stack) {
753
+ return {
754
+ type: "console",
755
+ timestamp: Date.now(),
756
+ url: typeof window !== "undefined" ? window.location.href : "",
757
+ level,
758
+ message,
759
+ stack
760
+ };
761
+ }
762
+ function installConsoleCapture(emit) {
763
+ const originalError = console.error;
764
+ const originalWarn = console.warn;
765
+ console.error = (...args) => {
766
+ emit(makeEvent("error", argsToMessage(args), extractStack(args)));
767
+ originalError.apply(console, args);
768
+ };
769
+ console.warn = (...args) => {
770
+ emit(makeEvent("warn", argsToMessage(args), extractStack(args)));
771
+ originalWarn.apply(console, args);
772
+ };
773
+ const rejectionHandler = (event) => {
774
+ const reason = event.reason;
775
+ const message = reason instanceof Error ? reason.message : String(reason ?? "Unhandled rejection");
776
+ const stack = reason instanceof Error ? reason.stack : void 0;
777
+ emit(makeEvent("unhandledrejection", message, stack));
778
+ };
779
+ if (typeof window !== "undefined") {
780
+ window.addEventListener("unhandledrejection", rejectionHandler);
781
+ }
782
+ return () => {
783
+ console.error = originalError;
784
+ console.warn = originalWarn;
785
+ if (typeof window !== "undefined") {
786
+ window.removeEventListener("unhandledrejection", rejectionHandler);
787
+ }
788
+ };
789
+ }
790
+
791
+ // src/debug/captures/network.ts
792
+ var DEFAULT_IGNORE = ["/api/dev-debug/", "/api/ui-bridge/", "localhost:9876"];
793
+ function installNetworkCapture(emit, options) {
794
+ if (typeof window === "undefined" || typeof window.fetch !== "function") {
795
+ return () => {
796
+ };
797
+ }
798
+ const originalFetch = window.fetch;
799
+ const ignorePatterns = options?.ignorePatterns ?? DEFAULT_IGNORE;
800
+ function shouldIgnore(url) {
801
+ return ignorePatterns.some((p) => url.includes(p));
802
+ }
803
+ function getMethod(input, init) {
804
+ if (init?.method) return init.method.toUpperCase();
805
+ if (input instanceof Request) return input.method.toUpperCase();
806
+ return "GET";
807
+ }
808
+ function getUrl(input) {
809
+ if (typeof input === "string") return input;
810
+ if (input instanceof URL) return input.href;
811
+ if (input instanceof Request) return input.url;
812
+ return String(input);
813
+ }
814
+ window.fetch = async function patchedFetch(input, init) {
815
+ const requestUrl = getUrl(input);
816
+ if (shouldIgnore(requestUrl)) {
817
+ return originalFetch.call(window, input, init);
818
+ }
819
+ const method = getMethod(input, init);
820
+ const start = performance.now();
821
+ try {
822
+ const response = await originalFetch.call(window, input, init);
823
+ const durationMs = Math.round(performance.now() - start);
824
+ if (response.status >= 400) {
825
+ const event = {
826
+ type: "network",
827
+ timestamp: Date.now(),
828
+ url: typeof window !== "undefined" ? window.location.href : "",
829
+ method,
830
+ requestUrl,
831
+ status: response.status,
832
+ statusText: response.statusText,
833
+ durationMs,
834
+ kind: "http-error"
835
+ };
836
+ emit(event);
837
+ }
838
+ return response;
839
+ } catch (err) {
840
+ const durationMs = Math.round(performance.now() - start);
841
+ const errorMessage = err instanceof Error ? err.message : String(err);
842
+ let kind = "network-error";
843
+ if (err instanceof DOMException && err.name === "AbortError") {
844
+ kind = "abort";
845
+ } else if (errorMessage.includes("CORS") || errorMessage.includes("cross-origin")) {
846
+ kind = "cors";
847
+ } else if (errorMessage.includes("timeout") || errorMessage.includes("timed out")) {
848
+ kind = "timeout";
849
+ }
850
+ const event = {
851
+ type: "network",
852
+ timestamp: Date.now(),
853
+ url: typeof window !== "undefined" ? window.location.href : "",
854
+ method,
855
+ requestUrl,
856
+ durationMs,
857
+ kind,
858
+ errorMessage
859
+ };
860
+ emit(event);
861
+ throw err;
862
+ }
863
+ };
864
+ return () => {
865
+ window.fetch = originalFetch;
866
+ };
867
+ }
868
+
869
+ // src/debug/captures/navigation.ts
870
+ function installNavigationCapture(emit) {
871
+ if (typeof window === "undefined" || typeof history === "undefined") {
872
+ return () => {
873
+ };
874
+ }
875
+ let lastUrl = window.location.href;
876
+ function emitNav(to, trigger) {
877
+ const from = lastUrl;
878
+ lastUrl = to;
879
+ if (from === to) return;
880
+ emit({
881
+ type: "navigation",
882
+ timestamp: Date.now(),
883
+ url: to,
884
+ from,
885
+ to,
886
+ trigger
887
+ });
888
+ }
889
+ const originalPushState = history.pushState;
890
+ const originalReplaceState = history.replaceState;
891
+ history.pushState = function(...args) {
892
+ originalPushState.apply(this, args);
893
+ emitNav(window.location.href, "pushState");
894
+ };
895
+ history.replaceState = function(...args) {
896
+ originalReplaceState.apply(this, args);
897
+ emitNav(window.location.href, "replaceState");
898
+ };
899
+ const popstateHandler = () => {
900
+ emitNav(window.location.href, "popstate");
901
+ };
902
+ window.addEventListener("popstate", popstateHandler);
903
+ return () => {
904
+ history.pushState = originalPushState;
905
+ history.replaceState = originalReplaceState;
906
+ window.removeEventListener("popstate", popstateHandler);
907
+ };
908
+ }
909
+
910
+ // src/debug/captures/long-tasks.ts
911
+ function installLongTaskCapture(emit) {
912
+ if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") {
913
+ return () => {
914
+ };
915
+ }
916
+ try {
917
+ const observer = new PerformanceObserver((list) => {
918
+ for (const entry of list.getEntries()) {
919
+ emit({
920
+ type: "long-task",
921
+ timestamp: Date.now(),
922
+ url: window.location.href,
923
+ durationMs: Math.round(entry.duration)
924
+ });
925
+ }
926
+ });
927
+ observer.observe({ type: "longtask", buffered: true });
928
+ return () => {
929
+ observer.disconnect();
930
+ };
931
+ } catch {
932
+ return () => {
933
+ };
934
+ }
935
+ }
936
+
937
+ // src/debug/captures/resource-errors.ts
938
+ var TRACKED_TAGS = /* @__PURE__ */ new Set(["IMG", "SCRIPT", "LINK"]);
939
+ function installResourceErrorCapture(emit) {
940
+ if (typeof window === "undefined") {
941
+ return () => {
942
+ };
943
+ }
944
+ const handler = (event) => {
945
+ const target = event.target;
946
+ if (!target || !target.tagName) return;
947
+ if (!TRACKED_TAGS.has(target.tagName)) return;
948
+ const resourceUrl = target.src || target.src || target.href || "";
949
+ if (!resourceUrl) return;
950
+ emit({
951
+ type: "resource-error",
952
+ timestamp: Date.now(),
953
+ url: window.location.href,
954
+ resourceUrl,
955
+ tagName: target.tagName
956
+ });
957
+ };
958
+ window.addEventListener("error", handler, true);
959
+ return () => {
960
+ window.removeEventListener("error", handler, true);
961
+ };
962
+ }
963
+
964
+ // src/debug/captures/web-vitals.ts
965
+ function installWebVitalsCapture(emit) {
966
+ if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") {
967
+ return () => {
968
+ };
969
+ }
970
+ const observers = [];
971
+ try {
972
+ const lcpObserver = new PerformanceObserver((list) => {
973
+ const entries = list.getEntries();
974
+ const last = entries[entries.length - 1];
975
+ if (last) {
976
+ emit({
977
+ type: "web-vital",
978
+ timestamp: Date.now(),
979
+ url: window.location.href,
980
+ metric: "LCP",
981
+ value: Math.round(last.startTime)
982
+ });
983
+ }
984
+ });
985
+ lcpObserver.observe({ type: "largest-contentful-paint", buffered: true });
986
+ observers.push(lcpObserver);
987
+ } catch {
988
+ }
989
+ try {
990
+ let clsValue = 0;
991
+ const clsObserver = new PerformanceObserver((list) => {
992
+ for (const entry of list.getEntries()) {
993
+ if (!entry.hadRecentInput) {
994
+ clsValue += entry.value ?? 0;
995
+ }
996
+ }
997
+ emit({
998
+ type: "web-vital",
999
+ timestamp: Date.now(),
1000
+ url: window.location.href,
1001
+ metric: "CLS",
1002
+ value: Math.round(clsValue * 1e3) / 1e3
1003
+ });
1004
+ });
1005
+ clsObserver.observe({ type: "layout-shift", buffered: true });
1006
+ observers.push(clsObserver);
1007
+ } catch {
1008
+ }
1009
+ return () => {
1010
+ for (const obs of observers) {
1011
+ obs.disconnect();
1012
+ }
1013
+ };
1014
+ }
1015
+
1016
+ // src/debug/captures/memory.ts
1017
+ function installMemoryCapture(emit, intervalMs = 3e4) {
1018
+ if (typeof window === "undefined") {
1019
+ return () => {
1020
+ };
1021
+ }
1022
+ const perf = performance;
1023
+ if (!perf.memory) {
1024
+ return () => {
1025
+ };
1026
+ }
1027
+ const tick = () => {
1028
+ const mem = perf.memory;
1029
+ if (!mem) return;
1030
+ emit({
1031
+ type: "memory",
1032
+ timestamp: Date.now(),
1033
+ url: window.location.href,
1034
+ usedJSHeapSize: mem.usedJSHeapSize,
1035
+ totalJSHeapSize: mem.totalJSHeapSize,
1036
+ jsHeapSizeLimit: mem.jsHeapSizeLimit
1037
+ });
1038
+ };
1039
+ tick();
1040
+ const id = setInterval(tick, intervalMs);
1041
+ return () => {
1042
+ clearInterval(id);
1043
+ };
1044
+ }
1045
+
1046
+ // src/debug/captures/hmr.ts
1047
+ var HMR_PATH_PATTERNS = ["/_next/webpack-hmr", "/__turbopack_hmr", "/_next/turbopack-hmr"];
1048
+ function isHmrUrl(url) {
1049
+ return HMR_PATH_PATTERNS.some((p) => url.includes(p));
1050
+ }
1051
+ function makeEvent2(level, message, moduleName, loc) {
1052
+ return {
1053
+ type: "hmr",
1054
+ level,
1055
+ message,
1056
+ moduleName,
1057
+ loc,
1058
+ timestamp: Date.now(),
1059
+ url: typeof window !== "undefined" ? window.location.href : ""
1060
+ };
1061
+ }
1062
+ function processHmrMessage(data, emit) {
1063
+ try {
1064
+ const msg = JSON.parse(data);
1065
+ if (Array.isArray(msg.errors)) {
1066
+ for (const err of msg.errors) {
1067
+ emit(
1068
+ makeEvent2(
1069
+ "error",
1070
+ typeof err === "string" ? err : err.message ?? String(err),
1071
+ err.moduleName ?? err.moduleIdentifier,
1072
+ err.loc ? String(err.loc) : void 0
1073
+ )
1074
+ );
1075
+ }
1076
+ }
1077
+ if (Array.isArray(msg.warnings)) {
1078
+ for (const warn of msg.warnings) {
1079
+ emit(
1080
+ makeEvent2(
1081
+ "warning",
1082
+ typeof warn === "string" ? warn : warn.message ?? String(warn),
1083
+ warn.moduleName ?? warn.moduleIdentifier,
1084
+ warn.loc ? String(warn.loc) : void 0
1085
+ )
1086
+ );
1087
+ }
1088
+ }
1089
+ if (msg.action === "serverError" && msg.errorJSON) {
1090
+ try {
1091
+ const err = JSON.parse(msg.errorJSON);
1092
+ emit(makeEvent2("error", err.message ?? String(err)));
1093
+ } catch {
1094
+ emit(makeEvent2("error", msg.errorJSON));
1095
+ }
1096
+ }
1097
+ if ((msg.action === "turbopack-message" || msg.type === "turbopack-message") && msg.data?.diagnostics) {
1098
+ for (const diag of msg.data.diagnostics) {
1099
+ emit(
1100
+ makeEvent2(
1101
+ diag.category === "warning" ? "warning" : "error",
1102
+ diag.message ?? String(diag),
1103
+ diag.filePath,
1104
+ diag.line != null ? `${diag.line}:${diag.column ?? 0}` : void 0
1105
+ )
1106
+ );
1107
+ }
1108
+ }
1109
+ } catch {
1110
+ }
1111
+ }
1112
+ function installWebSocketCapture(emit, cleanups) {
1113
+ if (!window.WebSocket) return;
1114
+ const OriginalWebSocket = window.WebSocket;
1115
+ const trackedSockets = [];
1116
+ const PatchedWebSocket = function(url, protocols) {
1117
+ const ws = new OriginalWebSocket(url, protocols);
1118
+ const urlStr = typeof url === "string" ? url : url.toString();
1119
+ if (isHmrUrl(urlStr)) {
1120
+ ws.addEventListener("message", (event) => {
1121
+ if (typeof event.data === "string") {
1122
+ processHmrMessage(event.data, emit);
1123
+ }
1124
+ });
1125
+ trackedSockets.push(ws);
1126
+ }
1127
+ return ws;
1128
+ };
1129
+ PatchedWebSocket.prototype = OriginalWebSocket.prototype;
1130
+ Object.defineProperty(PatchedWebSocket, "CONNECTING", { value: OriginalWebSocket.CONNECTING });
1131
+ Object.defineProperty(PatchedWebSocket, "OPEN", { value: OriginalWebSocket.OPEN });
1132
+ Object.defineProperty(PatchedWebSocket, "CLOSING", { value: OriginalWebSocket.CLOSING });
1133
+ Object.defineProperty(PatchedWebSocket, "CLOSED", { value: OriginalWebSocket.CLOSED });
1134
+ window.WebSocket = PatchedWebSocket;
1135
+ cleanups.push(() => {
1136
+ window.WebSocket = OriginalWebSocket;
1137
+ for (const ws of trackedSockets) {
1138
+ ws.close();
1139
+ }
1140
+ trackedSockets.length = 0;
1141
+ });
1142
+ }
1143
+ function installEventSourceCapture(emit, cleanups) {
1144
+ if (!window.EventSource) return;
1145
+ const OriginalEventSource = window.EventSource;
1146
+ const trackedSources = [];
1147
+ const messageHandler = (event) => {
1148
+ if (typeof event.data === "string") {
1149
+ processHmrMessage(event.data, emit);
1150
+ }
1151
+ };
1152
+ const PatchedEventSource = function(url, init) {
1153
+ const es = new OriginalEventSource(url, init);
1154
+ const urlStr = typeof url === "string" ? url : url.toString();
1155
+ if (isHmrUrl(urlStr)) {
1156
+ es.addEventListener("message", messageHandler);
1157
+ trackedSources.push(es);
1158
+ }
1159
+ return es;
1160
+ };
1161
+ PatchedEventSource.prototype = OriginalEventSource.prototype;
1162
+ Object.defineProperty(PatchedEventSource, "CONNECTING", {
1163
+ value: OriginalEventSource.CONNECTING
1164
+ });
1165
+ Object.defineProperty(PatchedEventSource, "OPEN", { value: OriginalEventSource.OPEN });
1166
+ Object.defineProperty(PatchedEventSource, "CLOSED", { value: OriginalEventSource.CLOSED });
1167
+ window.EventSource = PatchedEventSource;
1168
+ cleanups.push(() => {
1169
+ window.EventSource = OriginalEventSource;
1170
+ for (const es of trackedSources) {
1171
+ es.close();
1172
+ }
1173
+ trackedSources.length = 0;
1174
+ });
1175
+ }
1176
+ function installHmrCapture(emit) {
1177
+ if (typeof window === "undefined") return () => {
1178
+ };
1179
+ const cleanups = [];
1180
+ installWebSocketCapture(emit, cleanups);
1181
+ installEventSourceCapture(emit, cleanups);
1182
+ return () => {
1183
+ for (const cleanup of cleanups) {
1184
+ cleanup();
1185
+ }
1186
+ };
1187
+ }
1188
+
1189
+ // src/debug/captures/long-animation-frames.ts
1190
+ function installLoafCapture(emit) {
1191
+ if (typeof window === "undefined" || typeof PerformanceObserver === "undefined") {
1192
+ return () => {
1193
+ };
1194
+ }
1195
+ try {
1196
+ const observer = new PerformanceObserver((list) => {
1197
+ for (const entry of list.getEntries()) {
1198
+ const scripts = (entry.scripts ?? []).map((s) => {
1199
+ const script = s;
1200
+ return {
1201
+ invoker: script.invoker ?? "",
1202
+ sourceURL: script.sourceURL ?? "",
1203
+ sourceFunctionName: script.sourceFunctionName ?? "",
1204
+ sourceCharPosition: script.sourceCharPosition ?? 0,
1205
+ duration: Math.round(script.duration ?? 0)
1206
+ };
1207
+ });
1208
+ emit({
1209
+ type: "long-animation-frame",
1210
+ timestamp: Date.now(),
1211
+ url: window.location.href,
1212
+ durationMs: Math.round(entry.duration),
1213
+ blockingDurationMs: Math.round(
1214
+ entry.blockingDuration ?? 0
1215
+ ),
1216
+ scripts
1217
+ });
1218
+ }
1219
+ });
1220
+ observer.observe({ type: "long-animation-frame", buffered: true });
1221
+ return () => {
1222
+ observer.disconnect();
1223
+ };
1224
+ } catch {
1225
+ return () => {
1226
+ };
1227
+ }
1228
+ }
1229
+
1230
+ // src/debug/browser-capture.ts
1231
+ var BrowserEventCapture = class {
1232
+ constructor(config) {
1233
+ this.buffer = [];
1234
+ this.installed = false;
1235
+ this.cleanups = [];
1236
+ this.onEvent = null;
1237
+ this.config = config ?? {};
1238
+ this.maxEntries = config?.maxEntries ?? DEFAULT_CAPTURE_CONFIG.maxEntries;
1239
+ }
1240
+ setOnEvent(cb) {
1241
+ this.onEvent = cb;
1242
+ }
1243
+ /**
1244
+ * Install all enabled capture sub-modules.
1245
+ * Safe to call multiple times (no-ops if already installed).
1246
+ */
1247
+ install() {
1248
+ if (this.installed) return;
1249
+ const cfg = { ...DEFAULT_CAPTURE_CONFIG, ...this.config };
1250
+ const emit = (event) => {
1251
+ this.buffer.push(event);
1252
+ this.trim();
1253
+ this.onEvent?.(event);
1254
+ };
1255
+ if (cfg.console) {
1256
+ this.cleanups.push(installConsoleCapture(emit));
1257
+ }
1258
+ if (cfg.network) {
1259
+ this.cleanups.push(installNetworkCapture(emit, cfg.networkOptions));
1260
+ }
1261
+ if (cfg.navigation) {
1262
+ this.cleanups.push(installNavigationCapture(emit));
1263
+ }
1264
+ if (cfg.longTasks) {
1265
+ this.cleanups.push(installLongTaskCapture(emit));
1266
+ }
1267
+ if (cfg.longAnimationFrames) {
1268
+ this.cleanups.push(installLoafCapture(emit));
1269
+ }
1270
+ if (cfg.resourceErrors) {
1271
+ this.cleanups.push(installResourceErrorCapture(emit));
1272
+ }
1273
+ if (cfg.webVitals) {
1274
+ this.cleanups.push(installWebVitalsCapture(emit));
1275
+ }
1276
+ if (cfg.memory) {
1277
+ this.cleanups.push(installMemoryCapture(emit, cfg.memoryIntervalMs));
1278
+ }
1279
+ if (cfg.hmr) {
1280
+ this.cleanups.push(installHmrCapture(emit));
1281
+ }
1282
+ this.installed = true;
1283
+ }
1284
+ /**
1285
+ * Uninstall all capture sub-modules.
1286
+ */
1287
+ uninstall() {
1288
+ if (!this.installed) return;
1289
+ for (const cleanup of this.cleanups) {
1290
+ cleanup();
1291
+ }
1292
+ this.cleanups = [];
1293
+ this.installed = false;
1294
+ }
1295
+ // -------------------------------------------------------------------------
1296
+ // Manual event reporting (for events that can't be auto-captured)
1297
+ // -------------------------------------------------------------------------
1298
+ reportReactError(error, errorInfo) {
1299
+ const event = {
1300
+ type: "react-error",
1301
+ timestamp: Date.now(),
1302
+ url: typeof window !== "undefined" ? window.location.href : "",
1303
+ message: error.message,
1304
+ stack: error.stack,
1305
+ componentStack: errorInfo.componentStack
1306
+ };
1307
+ this.buffer.push(event);
1308
+ this.trim();
1309
+ this.onEvent?.(event);
1310
+ }
1311
+ reportWsStateChange(prev, next, reconnectAttempt) {
1312
+ if (next === "disconnected" || next === "error") {
1313
+ const event = {
1314
+ type: "ws-disconnection",
1315
+ timestamp: Date.now(),
1316
+ url: typeof window !== "undefined" ? window.location.href : "",
1317
+ previousState: prev,
1318
+ newState: next,
1319
+ reconnectAttempt
1320
+ };
1321
+ this.buffer.push(event);
1322
+ this.trim();
1323
+ this.onEvent?.(event);
1324
+ }
1325
+ }
1326
+ // -------------------------------------------------------------------------
1327
+ // Query methods
1328
+ // -------------------------------------------------------------------------
1329
+ getSince(ts) {
1330
+ return this.buffer.filter((e) => e.timestamp >= ts);
1331
+ }
1332
+ getRecent(n = 50) {
1333
+ return this.buffer.slice(-n);
1334
+ }
1335
+ getByType(type) {
1336
+ return this.buffer.filter((e) => e.type === type);
1337
+ }
1338
+ /**
1339
+ * Get console errors since a timestamp (backward-compat for ActionExecutor).
1340
+ */
1341
+ getConsoleSince(ts) {
1342
+ return this.buffer.filter((e) => (e.type === "console" || e.type === "hmr") && e.timestamp >= ts).map((e) => ({
1343
+ timestamp: e.timestamp,
1344
+ level: e.type === "hmr" ? e.level === "warning" ? "warn" : e.level : e.level,
1345
+ message: e.message,
1346
+ stack: e.stack
1347
+ }));
1348
+ }
1349
+ /**
1350
+ * Get recent console errors (backward-compat for ActionExecutor).
1351
+ */
1352
+ getConsoleRecent(n = 50) {
1353
+ return this.buffer.filter((e) => e.type === "console" || e.type === "hmr").slice(-n).map((e) => ({
1354
+ timestamp: e.timestamp,
1355
+ level: e.type === "hmr" ? e.level === "warning" ? "warn" : e.level : e.level,
1356
+ message: e.message,
1357
+ stack: e.stack
1358
+ }));
1359
+ }
1360
+ clear() {
1361
+ this.buffer = [];
1362
+ }
1363
+ trim() {
1364
+ if (this.buffer.length > this.maxEntries) {
1365
+ this.buffer = this.buffer.slice(-this.maxEntries);
1366
+ }
1367
+ }
1368
+ };
1369
+
448
1370
  // src/debug/metrics.ts
449
1371
  function generateId() {
450
1372
  return `${Date.now()}-${Math.random().toString(36).substr(2, 8)}`;
@@ -661,6 +1583,8 @@ function formatPercentage(value) {
661
1583
  return `${(value * 100).toFixed(1)}%`;
662
1584
  }
663
1585
 
1586
+ exports.BrowserEventCapture = BrowserEventCapture;
1587
+ exports.DEFAULT_CAPTURE_CONFIG = DEFAULT_CAPTURE_CONFIG;
664
1588
  exports.InfoPanel = InfoPanel;
665
1589
  exports.Inspector = Inspector;
666
1590
  exports.InspectorOverlay = InspectorOverlay;