@watchforge/browser 0.1.21 → 0.1.23

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.21",
3
+ "version": "0.1.23",
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
@@ -627,7 +627,7 @@ function setupBrowserInstrumentation() {
627
627
  }
628
628
  }
629
629
 
630
- import { initTracing } from "./tracing.js";
630
+ import { getCurrentTransaction, initTracing, setupBrowserTracing } from "./tracing.js";
631
631
 
632
632
  async function buildEventContexts(context = {}) {
633
633
  const runtime = getRuntimeContext();
@@ -661,12 +661,16 @@ export function register({
661
661
  blockClass = "rr-block",
662
662
  ignoreClass = "rr-ignore",
663
663
  maskTextClass = "rr-mask",
664
+ inlineImages = true,
665
+ inlineStylesheet = true,
666
+ collectFonts = true,
664
667
  captureConsoleErrors = true,
665
668
  captureResourceErrors = true,
666
669
  captureFailedRequests = true,
667
670
  captureNodeWarnings = false,
668
671
  captureNodeMultipleResolves = false,
669
672
  projectRoot = null,
673
+ tracesSampleRate = 1,
670
674
  }) {
671
675
  const nextBrowserRegisterKey = isBrowser
672
676
  ? JSON.stringify({
@@ -680,6 +684,9 @@ export function register({
680
684
  blockClass,
681
685
  ignoreClass,
682
686
  maskTextClass,
687
+ inlineImages,
688
+ inlineStylesheet,
689
+ collectFonts,
683
690
  captureConsoleErrors,
684
691
  captureResourceErrors,
685
692
  captureFailedRequests,
@@ -705,7 +712,7 @@ export function register({
705
712
  );
706
713
 
707
714
  // Initialize tracing
708
- initTracing(dsn, app_env, debug);
715
+ initTracing(dsn, app_env, debug, tracesSampleRate);
709
716
 
710
717
  if (DEBUG) {
711
718
  // Parse DSN to show API URL in debug
@@ -730,6 +737,7 @@ export function register({
730
737
  if (isBrowser) {
731
738
  browserRegisterKey = nextBrowserRegisterKey;
732
739
  setupBrowserInstrumentation();
740
+ setupBrowserTracing(getPageContext);
733
741
  replayInitPromise = initReplay({
734
742
  replaysSessionSampleRate,
735
743
  replaysOnErrorSampleRate,
@@ -737,6 +745,9 @@ export function register({
737
745
  blockClass,
738
746
  ignoreClass,
739
747
  maskTextClass,
748
+ inlineImages,
749
+ inlineStylesheet,
750
+ collectFonts,
740
751
  debug,
741
752
  });
742
753
  }
@@ -822,6 +833,7 @@ export async function captureException(error, context = {}) {
822
833
  );
823
834
  }
824
835
 
836
+ const currentTransaction = getCurrentTransaction();
825
837
  const event = {
826
838
  event_id: generateEventId(),
827
839
  level: "error",
@@ -833,6 +845,11 @@ export async function captureException(error, context = {}) {
833
845
  sdk: getSdkMetadata(),
834
846
  };
835
847
 
848
+ if (currentTransaction?.trace_id) {
849
+ event.trace_id = currentTransaction.trace_id;
850
+ event.transaction = currentTransaction.transaction;
851
+ }
852
+
836
853
  if (isBrowser && replayInitPromise) {
837
854
  try {
838
855
  await replayInitPromise;
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;
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
 
package/src/tracing.js CHANGED
@@ -3,14 +3,31 @@
3
3
  let DSN = null;
4
4
  let APP_ENV = "production";
5
5
  let DEBUG = false;
6
+ let TRACES_SAMPLE_RATE = 1;
6
7
 
7
8
  // Active transaction stack (for nested transactions)
8
9
  const transactionStack = [];
9
-
10
- export function initTracing(dsn, environment = "production", debug = false) {
10
+ let activePageTransaction = null;
11
+ let browserTracingInstalled = false;
12
+ let navigationFinishTimer = null;
13
+
14
+ export function initTracing(
15
+ dsn,
16
+ environment = "production",
17
+ debug = false,
18
+ tracesSampleRate = 1
19
+ ) {
11
20
  DSN = dsn;
12
21
  APP_ENV = environment;
13
22
  DEBUG = debug;
23
+ TRACES_SAMPLE_RATE =
24
+ typeof tracesSampleRate === "number" && tracesSampleRate >= 0 && tracesSampleRate <= 1
25
+ ? tracesSampleRate
26
+ : 1;
27
+ }
28
+
29
+ function shouldSampleTrace() {
30
+ return TRACES_SAMPLE_RATE >= 1 || Math.random() < TRACES_SAMPLE_RATE;
14
31
  }
15
32
 
16
33
  class Span {
@@ -250,4 +267,73 @@ export function finishTransaction(status = "ok") {
250
267
  return null;
251
268
  }
252
269
 
270
+ function endPageTransaction(status = "ok") {
271
+ if (navigationFinishTimer) {
272
+ clearTimeout(navigationFinishTimer);
273
+ navigationFinishTimer = null;
274
+ }
275
+ if (!activePageTransaction) return;
276
+ activePageTransaction.finish(status);
277
+ if (transactionStack[transactionStack.length - 1] === activePageTransaction) {
278
+ transactionStack.pop();
279
+ }
280
+ activePageTransaction = null;
281
+ }
282
+
283
+ function beginPageTransaction(op, getPageContext) {
284
+ if (!shouldSampleTrace()) return null;
285
+
286
+ const page = typeof getPageContext === "function" ? getPageContext() : null;
287
+ const path = page?.pathname || (typeof window !== "undefined" ? window.location.pathname : "/");
288
+ const name = page?.title || (typeof document !== "undefined" ? document.title : path) || path;
289
+ const txn = startTransaction(path, name, op);
290
+ txn.setRequest({
291
+ url: page?.url || (typeof window !== "undefined" ? window.location.href : path),
292
+ method: "GET",
293
+ });
294
+ activePageTransaction = txn;
295
+ return txn;
296
+ }
297
+
298
+ function schedulePageTransactionFinish(delayMs = 5000) {
299
+ if (navigationFinishTimer) clearTimeout(navigationFinishTimer);
300
+ navigationFinishTimer = setTimeout(() => {
301
+ endPageTransaction();
302
+ navigationFinishTimer = null;
303
+ }, delayMs);
304
+ }
305
+
306
+ export function setupBrowserTracing(getPageContext) {
307
+ if (typeof window === "undefined" || browserTracingInstalled) return;
308
+ browserTracingInstalled = true;
309
+
310
+ beginPageTransaction("pageload", getPageContext);
311
+
312
+ if (document.readyState === "complete") {
313
+ endPageTransaction();
314
+ } else {
315
+ window.addEventListener("load", () => endPageTransaction(), { once: true });
316
+ }
317
+
318
+ const onNavigation = () => {
319
+ endPageTransaction();
320
+ if (!beginPageTransaction("navigation", getPageContext)) return;
321
+ schedulePageTransactionFinish();
322
+ };
323
+
324
+ const originalPushState = history.pushState;
325
+ history.pushState = function (...args) {
326
+ originalPushState.apply(history, args);
327
+ onNavigation();
328
+ };
329
+
330
+ const originalReplaceState = history.replaceState;
331
+ history.replaceState = function (...args) {
332
+ originalReplaceState.apply(history, args);
333
+ onNavigation();
334
+ };
335
+
336
+ window.addEventListener("hashchange", onNavigation);
337
+ }
338
+
253
339
  export { Transaction, Span };