browsertrack 0.2.1 → 0.2.2

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 (52) hide show
  1. package/AGENTS.md +9 -6
  2. package/README.md +2 -0
  3. package/dist/{chunk-INXDWPJW.js → chunk-4HRLW6YF.js} +161 -106
  4. package/dist/chunk-4HRLW6YF.js.map +1 -0
  5. package/dist/{chunk-3HOXPTM2.js → chunk-AYSVE6NG.js} +808 -53
  6. package/dist/chunk-AYSVE6NG.js.map +1 -0
  7. package/dist/{chunk-6VA7GBAO.js → chunk-QRZ57ME3.js} +70 -2
  8. package/dist/chunk-QRZ57ME3.js.map +1 -0
  9. package/dist/{chunk-464D4U2U.js → chunk-TWEYRBDU.js} +279 -43
  10. package/dist/chunk-TWEYRBDU.js.map +1 -0
  11. package/dist/cli/index.js +1038 -545
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/client/index.cjs +184 -104
  14. package/dist/client/index.js +2 -2
  15. package/dist/client.iife.js +9 -9
  16. package/dist/core/index.d.ts +24 -1
  17. package/dist/core/index.js +9 -1
  18. package/dist/daemon/index.d.ts +2 -2
  19. package/dist/daemon/index.js +6 -8
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.js +14 -7
  22. package/dist/mcp/index.d.ts +1 -1
  23. package/dist/mcp/index.js +7 -4
  24. package/dist/{server-DiVmTrIR.d.ts → server-DjV7RWQM.d.ts} +9 -1
  25. package/docs/cli.md +4 -1
  26. package/docs/getting-started.md +60 -6
  27. package/docs/mcp-reference.md +42 -0
  28. package/package.json +1 -1
  29. package/packages/cli/src/index.ts +247 -151
  30. package/packages/client/src/interceptors/navigation.ts +38 -26
  31. package/packages/client/src/interceptors/network.ts +22 -17
  32. package/packages/client/src/notes/inspector.ts +81 -48
  33. package/packages/client/src/source/resolver.ts +9 -3
  34. package/packages/client/src/transport/websocket.ts +23 -18
  35. package/packages/core/src/index.ts +1 -0
  36. package/packages/core/src/safety.ts +86 -0
  37. package/packages/daemon/src/server/daemon.ts +7 -1
  38. package/packages/daemon/src/server/http.ts +125 -5
  39. package/packages/daemon/src/server/ws.ts +33 -29
  40. package/packages/daemon/src/storage/db.ts +57 -35
  41. package/packages/mcp/src/handlers.ts +114 -45
  42. package/packages/mcp/src/server.ts +202 -2
  43. package/test/core/safety.test.ts +106 -0
  44. package/test/daemon/storage.test.ts +36 -0
  45. package/test/e2e/daemon-mcp-e2e.test.ts +10 -0
  46. package/test/mcp/auto-start.test.ts +87 -0
  47. package/dist/chunk-3HOXPTM2.js.map +0 -1
  48. package/dist/chunk-464D4U2U.js.map +0 -1
  49. package/dist/chunk-6VA7GBAO.js.map +0 -1
  50. package/dist/chunk-7OCOQGDN.js +0 -635
  51. package/dist/chunk-7OCOQGDN.js.map +0 -1
  52. package/dist/chunk-INXDWPJW.js.map +0 -1
@@ -169,6 +169,32 @@ function truncate(str, maxLength = 200) {
169
169
  return str.slice(0, maxLength) + "...";
170
170
  }
171
171
 
172
+ // packages/core/src/safety.ts
173
+ function safeJsonStringify(val, fallback = "{}") {
174
+ if (val === void 0) return fallback;
175
+ try {
176
+ const seen = /* @__PURE__ */ new WeakSet();
177
+ return JSON.stringify(val, (key, value) => {
178
+ if (typeof value === "object" && value !== null) {
179
+ if (seen.has(value)) {
180
+ return "[Circular]";
181
+ }
182
+ seen.add(value);
183
+ }
184
+ if (typeof value === "bigint") {
185
+ return value.toString();
186
+ }
187
+ return value;
188
+ });
189
+ } catch {
190
+ try {
191
+ return JSON.stringify(String(val));
192
+ } catch {
193
+ return fallback;
194
+ }
195
+ }
196
+ }
197
+
172
198
  // packages/client/src/source/resolver.ts
173
199
  function resolveComponentSource(el) {
174
200
  if (!el || typeof el !== "object") return void 0;
@@ -218,7 +244,9 @@ function resolveReactComponent(el) {
218
244
  }
219
245
  }
220
246
  let curr = hostFiber;
221
- while (curr) {
247
+ let depth = 0;
248
+ while (curr && depth < 50) {
249
+ depth++;
222
250
  const type = curr.type;
223
251
  const name = getReactComponentName(type);
224
252
  if (name && !name.startsWith("html:") && name !== "Fragment") {
@@ -273,7 +301,9 @@ function resolveVueComponent(el) {
273
301
  const sourceFile = type.__file;
274
302
  const hierarchy = [];
275
303
  let curr = vueParent;
276
- while (curr) {
304
+ let depth = 0;
305
+ while (curr && depth < 50) {
306
+ depth++;
277
307
  const cType = curr.type || {};
278
308
  const cName = cType.name || cType.__name || cType.displayName;
279
309
  if (cName && !hierarchy.includes(cName)) {
@@ -310,7 +340,9 @@ function resolveVueComponent(el) {
310
340
  function resolveSvelteComponent(el) {
311
341
  try {
312
342
  let curr = el;
313
- while (curr) {
343
+ let depth = 0;
344
+ while (curr && depth < 50) {
345
+ depth++;
314
346
  const meta = curr.__svelte_meta;
315
347
  if (meta && meta.loc) {
316
348
  return {
@@ -849,12 +881,16 @@ function setupConsoleInterceptors(onConsole) {
849
881
  function setupNavigationInterceptors(onNavigation) {
850
882
  if (typeof window === "undefined" || typeof history === "undefined") return () => {
851
883
  };
852
- let currentUrl = redactUrl(window.location.href);
853
- onNavigation({
854
- to: currentUrl,
855
- type: "initial",
856
- timestamp: Date.now()
857
- });
884
+ let currentUrl = "";
885
+ try {
886
+ currentUrl = redactUrl(window.location.href);
887
+ onNavigation({
888
+ to: currentUrl,
889
+ type: "initial",
890
+ timestamp: Date.now()
891
+ });
892
+ } catch {
893
+ }
858
894
  const originalPushState = history.pushState;
859
895
  const originalReplaceState = history.replaceState;
860
896
  history.pushState = function(data, unused, url) {
@@ -890,26 +926,32 @@ function setupNavigationInterceptors(onNavigation) {
890
926
  return result;
891
927
  };
892
928
  const onPopState = () => {
893
- const from = currentUrl;
894
- const to = redactUrl(window.location.href);
895
- currentUrl = to;
896
- onNavigation({
897
- from,
898
- to,
899
- type: "popstate",
900
- timestamp: Date.now()
901
- });
929
+ try {
930
+ const from = currentUrl;
931
+ const to = redactUrl(window.location.href);
932
+ currentUrl = to;
933
+ onNavigation({
934
+ from,
935
+ to,
936
+ type: "popstate",
937
+ timestamp: Date.now()
938
+ });
939
+ } catch {
940
+ }
902
941
  };
903
942
  const onHashChange = () => {
904
- const from = currentUrl;
905
- const to = redactUrl(window.location.href);
906
- currentUrl = to;
907
- onNavigation({
908
- from,
909
- to,
910
- type: "hashchange",
911
- timestamp: Date.now()
912
- });
943
+ try {
944
+ const from = currentUrl;
945
+ const to = redactUrl(window.location.href);
946
+ currentUrl = to;
947
+ onNavigation({
948
+ from,
949
+ to,
950
+ type: "hashchange",
951
+ timestamp: Date.now()
952
+ });
953
+ } catch {
954
+ }
913
955
  };
914
956
  window.addEventListener("popstate", onPopState);
915
957
  window.addEventListener("hashchange", onHashChange);
@@ -946,31 +988,38 @@ function setupNetworkInterceptors(onNetwork) {
946
988
  urlStr = "unknown_url";
947
989
  }
948
990
  const safeUrl = redactUrl(urlStr);
991
+ let response;
949
992
  try {
950
- const response = await originalFetch.apply(window, [input, init2]);
951
- const durationMs = Date.now() - startTime;
952
- onNetwork({
953
- url: safeUrl,
954
- method,
955
- status: response.status,
956
- statusText: response.statusText,
957
- durationMs,
958
- timestamp: startTime
959
- });
960
- return response;
993
+ response = await originalFetch.apply(window, [input, init2]);
961
994
  } catch (err) {
962
- const durationMs = Date.now() - startTime;
995
+ const durationMs2 = Date.now() - startTime;
963
996
  const isAbort = err?.name === "AbortError";
997
+ try {
998
+ onNetwork({
999
+ url: safeUrl,
1000
+ method,
1001
+ durationMs: durationMs2,
1002
+ error: err?.message || "Network request failed",
1003
+ aborted: isAbort,
1004
+ timestamp: startTime
1005
+ });
1006
+ } catch {
1007
+ }
1008
+ throw err;
1009
+ }
1010
+ const durationMs = Date.now() - startTime;
1011
+ try {
964
1012
  onNetwork({
965
1013
  url: safeUrl,
966
1014
  method,
1015
+ status: response.status,
1016
+ statusText: response.statusText,
967
1017
  durationMs,
968
- error: err?.message || "Network request failed",
969
- aborted: isAbort,
970
1018
  timestamp: startTime
971
1019
  });
972
- throw err;
1020
+ } catch {
973
1021
  }
1022
+ return response;
974
1023
  };
975
1024
  cleanups.push(() => {
976
1025
  window.fetch = originalFetch;
@@ -1358,15 +1407,18 @@ var WebSocketTransport = class {
1358
1407
  }
1359
1408
  }
1360
1409
  send(payload) {
1361
- const raw = JSON.stringify(payload);
1362
- if (this.isConnected()) {
1363
- try {
1364
- this.ws.send(raw);
1365
- } catch {
1410
+ try {
1411
+ const raw = safeJsonStringify(payload);
1412
+ if (this.isConnected()) {
1413
+ try {
1414
+ this.ws.send(raw);
1415
+ } catch {
1416
+ this.enqueue(raw);
1417
+ }
1418
+ } else {
1366
1419
  this.enqueue(raw);
1367
1420
  }
1368
- } else {
1369
- this.enqueue(raw);
1421
+ } catch {
1370
1422
  }
1371
1423
  }
1372
1424
  enqueue(raw) {
@@ -1391,17 +1443,17 @@ var WebSocketTransport = class {
1391
1443
  }
1392
1444
  sendHello() {
1393
1445
  if (typeof window === "undefined") return;
1394
- const hello = {
1395
- type: "hello",
1396
- origin: window.location.origin,
1397
- url: window.location.href,
1398
- title: document.title,
1399
- userAgent: navigator.userAgent,
1400
- timestamp: Date.now(),
1401
- projectId: this.projectId
1402
- };
1403
1446
  try {
1404
- this.ws.send(JSON.stringify(hello));
1447
+ const hello = {
1448
+ type: "hello",
1449
+ origin: window.location.origin,
1450
+ url: window.location.href,
1451
+ title: document.title,
1452
+ userAgent: navigator.userAgent,
1453
+ timestamp: Date.now(),
1454
+ projectId: this.projectId
1455
+ };
1456
+ this.ws.send(safeJsonStringify(hello));
1405
1457
  } catch {
1406
1458
  }
1407
1459
  }
@@ -2389,9 +2441,15 @@ var NoteInspector = class {
2389
2441
  this.markersContainer.innerHTML = "";
2390
2442
  if (!this.showMarkers || this.isHidden) return;
2391
2443
  const currentPath = window.location.pathname;
2392
- const activeNotes = this.savedNotes.filter(
2393
- (n) => n.status === "OPEN" && (n.route === currentPath || !n.route || n.route === "/" || window.location.href.includes(n.route))
2394
- );
2444
+ const activeNotes = this.savedNotes.filter((n) => {
2445
+ if (n.status !== "OPEN") return false;
2446
+ if (!n.route) return true;
2447
+ if (n.route === currentPath) return true;
2448
+ if (n.route.includes("#") || n.route.includes("?")) {
2449
+ return window.location.href.includes(n.route);
2450
+ }
2451
+ return false;
2452
+ });
2395
2453
  const pageNotes = [];
2396
2454
  activeNotes.forEach((note, index) => {
2397
2455
  if (note.type === "page") {
@@ -2616,65 +2674,83 @@ var NoteInspector = class {
2616
2674
  }
2617
2675
  setupListeners() {
2618
2676
  const onMouseMove = (e) => {
2619
- if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
2620
- const path = e.composedPath ? e.composedPath() : [];
2621
- if (this.container && path.includes(this.container)) {
2622
- this.hideHighlight();
2623
- return;
2624
- }
2625
- if (e.altKey || this.activeMode === "element") {
2626
- const target = document.elementFromPoint(e.clientX, e.clientY);
2627
- if (target && target !== this.container && !this.container?.contains(target)) {
2628
- this.hoveredElement = target;
2629
- this.updateHighlight(target);
2677
+ try {
2678
+ if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
2679
+ const path = e.composedPath ? e.composedPath() : [];
2680
+ if (this.container && path.includes(this.container)) {
2681
+ this.hideHighlight();
2630
2682
  return;
2631
2683
  }
2632
- }
2633
- if (!e.altKey && this.activeMode !== "element") {
2634
- this.hideHighlight();
2684
+ if (e.altKey || this.activeMode === "element") {
2685
+ const target = document.elementFromPoint(e.clientX, e.clientY);
2686
+ if (target && target !== this.container && !this.container?.contains(target)) {
2687
+ this.hoveredElement = target;
2688
+ this.updateHighlight(target);
2689
+ return;
2690
+ }
2691
+ }
2692
+ if (!e.altKey && this.activeMode !== "element") {
2693
+ this.hideHighlight();
2694
+ }
2695
+ } catch {
2635
2696
  }
2636
2697
  };
2637
2698
  const onClick = (e) => {
2638
- if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
2639
- const path = e.composedPath ? e.composedPath() : [];
2640
- if (this.container && path.includes(this.container)) {
2641
- return;
2642
- }
2643
- if (this.activeMode === "region") return;
2644
- if (e.altKey || this.activeMode === "element") {
2645
- e.preventDefault();
2646
- e.stopPropagation();
2647
- const target = this.hoveredElement || document.elementFromPoint(e.clientX, e.clientY);
2648
- if (target && target !== this.container && !this.container?.contains(target)) {
2649
- this.selectedElement = target;
2650
- this.openNoteEditor(target, "element");
2651
- if (this.activeMode === "element" && !this.activeScenario) {
2652
- this.setMode("idle");
2699
+ try {
2700
+ if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
2701
+ const path = e.composedPath ? e.composedPath() : [];
2702
+ if (this.container && path.includes(this.container)) {
2703
+ return;
2704
+ }
2705
+ if (this.activeMode === "region") return;
2706
+ if (e.altKey || this.activeMode === "element") {
2707
+ e.preventDefault();
2708
+ e.stopPropagation();
2709
+ const target = this.hoveredElement || document.elementFromPoint(e.clientX, e.clientY);
2710
+ if (target && target !== this.container && !this.container?.contains(target)) {
2711
+ this.selectedElement = target;
2712
+ this.openNoteEditor(target, "element");
2713
+ if (this.activeMode === "element" && !this.activeScenario) {
2714
+ this.setMode("idle");
2715
+ }
2653
2716
  }
2654
2717
  }
2718
+ } catch {
2655
2719
  }
2656
2720
  };
2657
2721
  const onKeyDown = (e) => {
2658
- if (e.key === "Escape") {
2659
- if (this.cardOverlay && this.cardOverlay.parentElement && this.shadowRoot) {
2660
- this.shadowRoot.removeChild(this.cardOverlay);
2661
- this.cardOverlay = null;
2662
- } else if (this.activeMode === "region" || this.activeMode === "element") {
2663
- if (!this.activeScenario) {
2664
- this.setMode("idle");
2722
+ try {
2723
+ if (e.key === "Escape") {
2724
+ if (this.cardOverlay && this.cardOverlay.parentElement && this.shadowRoot) {
2725
+ this.shadowRoot.removeChild(this.cardOverlay);
2726
+ this.cardOverlay = null;
2727
+ } else if (this.activeMode === "region" || this.activeMode === "element") {
2728
+ if (!this.activeScenario) {
2729
+ this.setMode("idle");
2730
+ }
2665
2731
  }
2666
2732
  }
2733
+ } catch {
2667
2734
  }
2668
2735
  };
2669
2736
  const onKeyUp = (e) => {
2670
- if (e.key === "Alt" && !this.modalOverlay && !this.cardOverlay && this.activeMode !== "element") {
2671
- this.hideHighlight();
2737
+ try {
2738
+ if (e.key === "Alt" && !this.modalOverlay && !this.cardOverlay && this.activeMode !== "element") {
2739
+ this.hideHighlight();
2740
+ }
2741
+ } catch {
2672
2742
  }
2673
2743
  };
2674
2744
  const onScrollOrResize = () => {
2675
- requestAnimationFrame(() => {
2676
- this.updateMarkerPositions();
2677
- });
2745
+ try {
2746
+ requestAnimationFrame(() => {
2747
+ try {
2748
+ this.updateMarkerPositions();
2749
+ } catch {
2750
+ }
2751
+ });
2752
+ } catch {
2753
+ }
2678
2754
  };
2679
2755
  window.addEventListener("mousemove", onMouseMove, { capture: true, passive: true });
2680
2756
  window.addEventListener("click", onClick, { capture: true });
@@ -3136,7 +3212,11 @@ var NoteInspector = class {
3136
3212
  region.width,
3137
3213
  region.height
3138
3214
  );
3139
- resolve(canvas.toDataURL("image/png"));
3215
+ try {
3216
+ resolve(canvas.toDataURL("image/png"));
3217
+ } catch {
3218
+ resolve(dataUrl);
3219
+ }
3140
3220
  };
3141
3221
  img.onerror = () => resolve(dataUrl);
3142
3222
  img.src = dataUrl;
@@ -8,8 +8,8 @@ import {
8
8
  init,
9
9
  resolveComponentSource,
10
10
  shouldHideUIFromUrl
11
- } from "../chunk-INXDWPJW.js";
12
- import "../chunk-6VA7GBAO.js";
11
+ } from "../chunk-4HRLW6YF.js";
12
+ import "../chunk-QRZ57ME3.js";
13
13
  export {
14
14
  BreadcrumbBuffer,
15
15
  BrowserScriptScreenshotDriver,