@watchforge/browser 0.1.20 → 0.1.22

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@watchforge/browser",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "main": "./src/index.js",
5
5
  "types": "./src/index.d.ts",
6
6
  "description": "WatchForge JavaScript SDK for browser JavaScript, Next.js, React, Node.js, and Express.js",
package/src/client.js CHANGED
@@ -97,6 +97,71 @@ function captureGlobalException(error, context = {}) {
97
97
  void captureException(normalizeException(error, context.message), context);
98
98
  }
99
99
 
100
+ function normalizeStackFramePath(path) {
101
+ if (!path) return "";
102
+ try {
103
+ if (isBrowser) {
104
+ const url = new URL(String(path), window.location.href);
105
+ if (url.origin === window.location.origin) {
106
+ return url.pathname.replace(/^\/+/, "");
107
+ }
108
+ }
109
+ } catch {
110
+ // keep original path
111
+ }
112
+
113
+ return String(path)
114
+ .replace(/^webpack-internal:\/\/\/?/, "")
115
+ .replace(/^webpack:\/\/(?:\([^)]*\)\/)?/, "")
116
+ .replace(/^webpack:\/\/[^/]+\//, "")
117
+ .replace(/^\([^)]*\)\//, "")
118
+ .replace(/^file:\/\//, "")
119
+ .split("?")[0]
120
+ .split("#")[0]
121
+ .replace(/\\/g, "/")
122
+ .replace(/^\/+/, "")
123
+ .replace(/^\.\//, "");
124
+ }
125
+
126
+ function isOriginalSourcePath(path) {
127
+ const normalized = normalizeStackFramePath(path);
128
+ return /(?:^|\/)src\/.+\.(?:tsx?|jsx?)$/.test(normalized);
129
+ }
130
+
131
+ function applyBrowserLocationOverride(stacktrace, location) {
132
+ if (!stacktrace?.frames?.length || !location?.filename || !location?.lineno) {
133
+ return stacktrace;
134
+ }
135
+ if (!isOriginalSourcePath(location.filename)) return stacktrace;
136
+
137
+ const normalizedLocation = normalizeStackFramePath(location.filename);
138
+ const matchingFrames = stacktrace.frames.filter((frame) => {
139
+ const framePath = normalizeStackFramePath(
140
+ frame.abs_path || frame.module || frame.raw_abs_path || frame.filename
141
+ );
142
+ return (
143
+ frame.in_app &&
144
+ (framePath === normalizedLocation ||
145
+ framePath.endsWith(`/${normalizedLocation}`) ||
146
+ normalizedLocation.endsWith(`/${framePath}`))
147
+ );
148
+ });
149
+
150
+ const target = matchingFrames[matchingFrames.length - 1] || stacktrace.frames.find((f) => f.in_app);
151
+ if (!target) return stacktrace;
152
+
153
+ target.abs_path = normalizedLocation;
154
+ target.raw_abs_path = location.filename;
155
+ target.module = normalizedLocation;
156
+ target.filename = normalizedLocation.split("/").pop() || target.filename;
157
+ target.lineno = Number(location.lineno);
158
+ if (location.colno != null) target.colno = Number(location.colno);
159
+ target.function = target.function || "<anonymous>";
160
+ target.browser_location_override = true;
161
+
162
+ return stacktrace;
163
+ }
164
+
100
165
  function getElementDescriptor(element) {
101
166
  if (!element || typeof element !== "object") return null;
102
167
  const tag = element.tagName ? String(element.tagName).toLowerCase() : "unknown";
@@ -236,6 +301,11 @@ function setupBrowserInstrumentation() {
236
301
  mechanism: "window.onerror",
237
302
  },
238
303
  extra: { url, line, col },
304
+ stackFrame: {
305
+ filename: url,
306
+ lineno: line,
307
+ colno: col,
308
+ },
239
309
  message: String(msg),
240
310
  });
241
311
  };
@@ -591,6 +661,9 @@ export function register({
591
661
  blockClass = "rr-block",
592
662
  ignoreClass = "rr-ignore",
593
663
  maskTextClass = "rr-mask",
664
+ inlineImages = true,
665
+ inlineStylesheet = true,
666
+ collectFonts = true,
594
667
  captureConsoleErrors = true,
595
668
  captureResourceErrors = true,
596
669
  captureFailedRequests = true,
@@ -610,6 +683,9 @@ export function register({
610
683
  blockClass,
611
684
  ignoreClass,
612
685
  maskTextClass,
686
+ inlineImages,
687
+ inlineStylesheet,
688
+ collectFonts,
613
689
  captureConsoleErrors,
614
690
  captureResourceErrors,
615
691
  captureFailedRequests,
@@ -667,6 +743,9 @@ export function register({
667
743
  blockClass,
668
744
  ignoreClass,
669
745
  maskTextClass,
746
+ inlineImages,
747
+ inlineStylesheet,
748
+ collectFonts,
670
749
  debug,
671
750
  });
672
751
  }
@@ -742,6 +821,9 @@ export async function captureException(error, context = {}) {
742
821
  if (!DSN) return;
743
822
 
744
823
  let stacktrace = buildStacktraceFromError(error);
824
+ if (stacktrace && context.stackFrame) {
825
+ stacktrace = applyBrowserLocationOverride(stacktrace, context.stackFrame);
826
+ }
745
827
  if (stacktrace) {
746
828
  stacktrace = await enrichStacktraceAsync(
747
829
  stacktrace,
package/src/index.d.ts CHANGED
@@ -9,6 +9,9 @@ export interface WatchForgeRegisterOptions {
9
9
  blockClass?: string;
10
10
  ignoreClass?: string;
11
11
  maskTextClass?: string;
12
+ inlineImages?: boolean;
13
+ inlineStylesheet?: boolean;
14
+ collectFonts?: boolean;
12
15
  captureConsoleErrors?: boolean;
13
16
  captureResourceErrors?: boolean;
14
17
  captureFailedRequests?: boolean;
@@ -23,6 +26,11 @@ export interface WatchForgeCaptureContext {
23
26
  tags?: Record<string, unknown>;
24
27
  extra?: Record<string, unknown>;
25
28
  contexts?: Record<string, unknown>;
29
+ stackFrame?: {
30
+ filename?: string;
31
+ lineno?: number;
32
+ colno?: number;
33
+ };
26
34
  }
27
35
 
28
36
  export function register(options: WatchForgeRegisterOptions): void;
package/src/replay.js CHANGED
@@ -13,6 +13,9 @@ let options = {
13
13
  blockClass: "rr-block",
14
14
  ignoreClass: "rr-ignore",
15
15
  maskTextClass: "rr-mask",
16
+ inlineImages: true,
17
+ inlineStylesheet: true,
18
+ collectFonts: true,
16
19
  };
17
20
  let stopRecording = null;
18
21
  let events = [];
@@ -112,6 +115,9 @@ export async function initReplay(config = {}) {
112
115
  tel: true,
113
116
  text: Boolean(options.maskAllInputs),
114
117
  },
118
+ inlineImages: options.inlineImages !== false,
119
+ inlineStylesheet: options.inlineStylesheet !== false,
120
+ collectFonts: options.collectFonts !== false,
115
121
  });
116
122
  } catch (error) {
117
123
  if (options.debug) {
@@ -172,7 +178,7 @@ export function flushReplayForEvent(dsn, eventId) {
172
178
  events,
173
179
  sdk: {
174
180
  name: "@watchforge/browser",
175
- version: "0.1.12",
181
+ version: "0.1.22",
176
182
  },
177
183
  };
178
184