@newtalaria/browser 0.1.10 → 0.1.12

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.
@@ -0,0 +1,4 @@
1
+ /** Bumped with package.json version for event metadata. */
2
+ export declare const SDK_NAME = "@newtalaria/browser";
3
+ export declare const SDK_VERSION = "0.1.12";
4
+ //# sourceMappingURL=sdk_meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk_meta.d.ts","sourceRoot":"","sources":["../src/sdk_meta.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,eAAO,MAAM,QAAQ,wBAAwB,CAAC;AAC9C,eAAO,MAAM,WAAW,WAAW,CAAC"}
@@ -17,6 +17,21 @@
17
17
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
18
18
  }
19
19
 
20
+ // src/utils/browser_extension_noise.ts
21
+ var EXTENSION_URL = /(?:chrome|moz|safari|safari-web|ms-browser)-extension:\/\//i;
22
+ var WEBKIT_MASKED = /webkit-masked-url:\/\/hidden\//i;
23
+ function isBrowserExtensionNoise(opts) {
24
+ const filename = opts.filename ?? "";
25
+ if (EXTENSION_URL.test(filename) || WEBKIT_MASKED.test(filename)) {
26
+ return true;
27
+ }
28
+ const stack = opts.stack ?? "";
29
+ if (EXTENSION_URL.test(stack) || WEBKIT_MASKED.test(stack)) {
30
+ return true;
31
+ }
32
+ return false;
33
+ }
34
+
20
35
  // src/utils/opaque_script_error.ts
21
36
  function isOpaqueCrossOriginScriptError(event) {
22
37
  const msg = (event.message ?? "").trim();
@@ -25,6 +40,137 @@
25
40
  return true;
26
41
  }
27
42
 
43
+ // src/utils/browser_context.ts
44
+ function parseBrowserContext(ua = typeof navigator !== "undefined" ? navigator.userAgent : "", language = typeof navigator !== "undefined" ? navigator.language : "") {
45
+ const userAgent = ua || "";
46
+ const lang = language || "";
47
+ let name = "unknown";
48
+ let version = "";
49
+ const rules = [
50
+ { name: "Edge", re: /Edg(?:e|A|iOS)?\/([\d.]+)/ },
51
+ { name: "Opera", re: /OPR\/([\d.]+)/ },
52
+ { name: "Samsung Internet", re: /SamsungBrowser\/([\d.]+)/ },
53
+ { name: "Firefox", re: /Firefox\/([\d.]+)/ },
54
+ { name: "Chrome", re: /(?:Chrome|CriOS)\/([\d.]+)/ },
55
+ { name: "Safari", re: /Version\/([\d.]+).*Safari/ }
56
+ ];
57
+ for (const rule2 of rules) {
58
+ const m = userAgent.match(rule2.re);
59
+ if (m) {
60
+ name = rule2.name;
61
+ version = m[1] ?? "";
62
+ break;
63
+ }
64
+ }
65
+ if (name === "unknown" && /iPhone|iPad|iPod/.test(userAgent) && /AppleWebKit/.test(userAgent)) {
66
+ name = "Safari";
67
+ const m = userAgent.match(/OS ([\d_]+)/);
68
+ if (m?.[1]) version = m[1].replace(/_/g, ".");
69
+ }
70
+ let os = "unknown";
71
+ let osVersion = "";
72
+ const win = userAgent.match(/Windows NT ([\d.]+)/);
73
+ if (win) {
74
+ os = "Windows";
75
+ osVersion = win[1] ?? "";
76
+ } else {
77
+ const android = userAgent.match(/Android ([\d.]+)/);
78
+ if (android) {
79
+ os = "Android";
80
+ osVersion = android[1] ?? "";
81
+ } else {
82
+ const ios = userAgent.match(/(?:iPhone|iPad|iPod).*OS ([\d_]+)/);
83
+ if (ios) {
84
+ os = "iOS";
85
+ osVersion = (ios[1] ?? "").replace(/_/g, ".");
86
+ } else {
87
+ const mac = userAgent.match(/Mac OS X ([\d_]+)/);
88
+ if (mac) {
89
+ os = "macOS";
90
+ osVersion = (mac[1] ?? "").replace(/_/g, ".");
91
+ } else if (/CrOS/.test(userAgent)) {
92
+ os = "Chrome OS";
93
+ } else if (/Linux/.test(userAgent)) {
94
+ os = "Linux";
95
+ }
96
+ }
97
+ }
98
+ }
99
+ let device = "desktop";
100
+ if (/iPad|Tablet|Android(?!.*Mobile)/i.test(userAgent)) {
101
+ device = "tablet";
102
+ } else if (/Mobi|iPhone|iPod|Android.*Mobile/i.test(userAgent)) {
103
+ device = "mobile";
104
+ } else if (!userAgent) {
105
+ device = "unknown";
106
+ }
107
+ return {
108
+ name,
109
+ version,
110
+ os,
111
+ osVersion,
112
+ device,
113
+ language: lang,
114
+ userAgent
115
+ };
116
+ }
117
+ function browserContextTags(ctx) {
118
+ return {
119
+ "browser.name": ctx.name,
120
+ "browser.version": ctx.version || "unknown",
121
+ "os.name": ctx.os,
122
+ "os.version": ctx.osVersion || "unknown",
123
+ device: ctx.device
124
+ };
125
+ }
126
+ async function collectBrowserContext() {
127
+ const base = parseBrowserContext();
128
+ const nav = typeof navigator !== "undefined" ? navigator : void 0;
129
+ const uad = nav?.userAgentData;
130
+ if (!uad) return base;
131
+ if (uad.mobile === true) base.device = "mobile";
132
+ if (uad.platform) {
133
+ const p = uad.platform;
134
+ if (/Windows/i.test(p)) base.os = "Windows";
135
+ else if (/macOS|Mac OS/i.test(p)) base.os = "macOS";
136
+ else if (/Android/i.test(p)) base.os = "Android";
137
+ else if (/iOS|iPhone/i.test(p)) base.os = "iOS";
138
+ else if (/Linux/i.test(p)) base.os = "Linux";
139
+ else if (/Chrome OS|Chromium OS/i.test(p)) base.os = "Chrome OS";
140
+ else base.os = p;
141
+ }
142
+ const brands = uad.brands ?? [];
143
+ const interesting = brands.find(
144
+ (b) => !/Not.?A.?Brand/i.test(b.brand) && !/Chromium/i.test(b.brand)
145
+ );
146
+ if (interesting) {
147
+ base.name = interesting.brand.replace(/^Google /, "");
148
+ base.version = interesting.version;
149
+ }
150
+ try {
151
+ if (typeof uad.getHighEntropyValues === "function") {
152
+ const hi = await uad.getHighEntropyValues([
153
+ "platformVersion",
154
+ "fullVersionList"
155
+ ]);
156
+ if (hi.platformVersion) base.osVersion = hi.platformVersion;
157
+ const full = hi.fullVersionList?.find(
158
+ (b) => !/Not.?A.?Brand/i.test(b.brand) && !/Chromium/i.test(b.brand)
159
+ );
160
+ if (full) {
161
+ base.name = full.brand.replace(/^Google /, "");
162
+ base.version = full.version;
163
+ }
164
+ }
165
+ } catch {
166
+ }
167
+ return base;
168
+ }
169
+
170
+ // src/sdk_meta.ts
171
+ var SDK_NAME = "@newtalaria/browser";
172
+ var SDK_VERSION = "0.1.12";
173
+
28
174
  // src/transport/serverpod.ts
29
175
  var ServerpodTransport = class {
30
176
  constructor(options) {
@@ -13538,6 +13684,8 @@
13538
13684
  this.linkableReplayId = null;
13539
13685
  /** Last error-clip paint/upload failure (cleared on successful segment 0). */
13540
13686
  this.lastReplayCaptureFailure = null;
13687
+ /** Cached once per init for event tags/extra. */
13688
+ this.browserContext = null;
13541
13689
  }
13542
13690
  init(options) {
13543
13691
  if (this.options) {
@@ -13560,6 +13708,10 @@
13560
13708
  this.errorClipDeadlineMs = null;
13561
13709
  this.linkableReplayId = null;
13562
13710
  this.lastReplayCaptureFailure = null;
13711
+ this.browserContext = parseBrowserContext();
13712
+ void collectBrowserContext().then((ctx) => {
13713
+ if (this.options) this.browserContext = ctx;
13714
+ });
13563
13715
  this.buffer = new SegmentBuffer();
13564
13716
  this.uploadChain = Promise.resolve();
13565
13717
  this.sessionSampled = Math.random() < this.options.replaysSessionSampleRate;
@@ -13629,6 +13781,14 @@
13629
13781
  }
13630
13782
  async captureException(error, context) {
13631
13783
  const err = error instanceof Error ? error : new Error(typeof error === "string" ? error : "Unknown error");
13784
+ const filename = typeof context?.extra?.filename === "string" ? context.extra.filename : void 0;
13785
+ if (isBrowserExtensionNoise({
13786
+ message: err.message,
13787
+ stack: err.stack,
13788
+ filename
13789
+ })) {
13790
+ return;
13791
+ }
13632
13792
  await this.capture({
13633
13793
  message: err.message || err.name || "Error",
13634
13794
  level: "error",
@@ -13722,6 +13882,7 @@
13722
13882
  this.closed = false;
13723
13883
  this.linkableReplayId = null;
13724
13884
  this.lastReplayCaptureFailure = null;
13885
+ this.browserContext = null;
13725
13886
  }
13726
13887
  onRrwebEvent(event) {
13727
13888
  if (this.closed || !this.options) return;
@@ -13805,13 +13966,28 @@
13805
13966
  }
13806
13967
  const tags = applyReplayCaptureTags(
13807
13968
  {
13969
+ ...this.browserContext ? browserContextTags(this.browserContext) : {},
13808
13970
  ...this.options.tags ?? {},
13809
13971
  ...args.context?.tags ?? {}
13810
13972
  },
13811
13973
  errorClipOutcome
13812
13974
  );
13813
13975
  const extra = mergeReplayCaptureExtra(
13814
- args.context?.extra,
13976
+ {
13977
+ ...this.browserContext ? {
13978
+ browser: {
13979
+ name: this.browserContext.name,
13980
+ version: this.browserContext.version,
13981
+ os: this.browserContext.os,
13982
+ osVersion: this.browserContext.osVersion,
13983
+ device: this.browserContext.device,
13984
+ language: this.browserContext.language,
13985
+ userAgent: this.browserContext.userAgent
13986
+ }
13987
+ } : {},
13988
+ sdk: { name: SDK_NAME, version: SDK_VERSION },
13989
+ ...args.context?.extra
13990
+ },
13815
13991
  errorClipOutcome
13816
13992
  );
13817
13993
  try {
@@ -14035,6 +14211,13 @@
14035
14211
  const onError = (event) => {
14036
14212
  if (isOpaqueCrossOriginScriptError(event)) return;
14037
14213
  const error = event.error instanceof Error ? event.error : new Error(event.message || "window.onerror");
14214
+ if (isBrowserExtensionNoise({
14215
+ message: event.message || error.message,
14216
+ stack: error.stack,
14217
+ filename: event.filename
14218
+ })) {
14219
+ return;
14220
+ }
14038
14221
  void this.captureException(error, {
14039
14222
  extra: {
14040
14223
  filename: event.filename,