@newtalaria/browser 0.1.18 → 0.1.19

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/dist/index.js CHANGED
@@ -270,6 +270,259 @@ async function collectBrowserContext() {
270
270
  return base;
271
271
  }
272
272
 
273
+ // src/utils/tags.ts
274
+ var MAX_TAGS_PER_EVENT = 20;
275
+ var MAX_TAG_KEY_LENGTH = 64;
276
+ var MAX_TAG_VALUE_LENGTH = 128;
277
+ var MAX_TAG_TOTAL_UTF8_BYTES = 2048;
278
+ var KEY_PATTERN = /^[a-z0-9_.-]+$/;
279
+ var HIGH_CARDINALITY_TAG_KEYS = /* @__PURE__ */ new Set([
280
+ "user_id",
281
+ "userid",
282
+ "request_id",
283
+ "requestid",
284
+ "business_id",
285
+ "order_id",
286
+ "email",
287
+ "url",
288
+ "uuid",
289
+ "session_id",
290
+ "trace_id",
291
+ "span_id"
292
+ ]);
293
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
294
+ var EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
295
+ var LONG_NUMERIC_RE = /^\d{8,}$/;
296
+ function utf8Bytes(s) {
297
+ if (typeof TextEncoder !== "undefined") {
298
+ return new TextEncoder().encode(s).length;
299
+ }
300
+ return unescape(encodeURIComponent(s)).length;
301
+ }
302
+ function normalizeKey(raw) {
303
+ const key = raw.trim().toLowerCase();
304
+ if (!key || key.length > MAX_TAG_KEY_LENGTH) return null;
305
+ if (!KEY_PATTERN.test(key)) return null;
306
+ return key;
307
+ }
308
+ function normalizeValue(raw) {
309
+ if (raw == null) return null;
310
+ const value = String(raw).trim();
311
+ if (!value) return null;
312
+ return value.length <= MAX_TAG_VALUE_LENGTH ? value : value.slice(0, MAX_TAG_VALUE_LENGTH);
313
+ }
314
+ function normalizeTags(tags) {
315
+ if (!tags) return {};
316
+ const result2 = {};
317
+ let totalBytes = 0;
318
+ for (const [rawKey, rawValue] of Object.entries(tags)) {
319
+ if (Object.keys(result2).length >= MAX_TAGS_PER_EVENT) break;
320
+ const key = normalizeKey(rawKey);
321
+ if (!key) continue;
322
+ const value = normalizeValue(rawValue);
323
+ if (value == null) continue;
324
+ if (result2[key] != null) {
325
+ totalBytes -= utf8Bytes(key) + utf8Bytes(result2[key]);
326
+ }
327
+ const entryBytes = utf8Bytes(key) + utf8Bytes(value);
328
+ if (totalBytes + entryBytes > MAX_TAG_TOTAL_UTF8_BYTES) continue;
329
+ result2[key] = value;
330
+ totalBytes += entryBytes;
331
+ }
332
+ return result2;
333
+ }
334
+ function mergeTags(...parts) {
335
+ const merged = {};
336
+ for (const part of parts) {
337
+ if (!part) continue;
338
+ Object.assign(merged, part);
339
+ }
340
+ return normalizeTags(merged);
341
+ }
342
+ function looksHighCardinalityValue(value) {
343
+ return UUID_RE.test(value) || EMAIL_RE.test(value) || LONG_NUMERIC_RE.test(value);
344
+ }
345
+ function warnSuspiciousTags(tags, environment) {
346
+ if (!environment || environment === "production") return;
347
+ if (typeof console === "undefined" || typeof console.warn !== "function") {
348
+ return;
349
+ }
350
+ for (const [key, value] of Object.entries(tags)) {
351
+ if (HIGH_CARDINALITY_TAG_KEYS.has(key)) {
352
+ console.warn(
353
+ `@newtalaria/browser: Tag "${key}" appears to have high cardinality. Consider moving it to context/extra.`
354
+ );
355
+ continue;
356
+ }
357
+ if (looksHighCardinalityValue(value)) {
358
+ console.warn(
359
+ `@newtalaria/browser: Tag "${key}" value looks high-cardinality. Consider moving it to context/extra.`
360
+ );
361
+ }
362
+ }
363
+ }
364
+ var RESERVED_TAG_KEYS = [
365
+ "service",
366
+ "platform",
367
+ "feature",
368
+ "operation",
369
+ "component",
370
+ "runtime",
371
+ "runtime_version"
372
+ ];
373
+
374
+ // src/utils/stacktrace.ts
375
+ var V8_FRAME = /^\s*at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?\s*$/;
376
+ var V8_FRAME_BARE = /^\s*at\s+(.+)\s*$/;
377
+ var EXTENSION_URL2 = /(?:chrome|moz|safari|safari-web|ms-browser)-extension:\/\//i;
378
+ function parseStackTrace(stack, inAppOptions) {
379
+ if (!stack || !stack.trim()) return void 0;
380
+ const parsed = [];
381
+ for (const line of stack.split("\n")) {
382
+ const frame = parseStackLine(line, inAppOptions);
383
+ if (frame) parsed.push(frame);
384
+ }
385
+ if (parsed.length === 0) return void 0;
386
+ parsed.reverse();
387
+ return { frames: parsed };
388
+ }
389
+ function parseStackLine(line, inAppOptions) {
390
+ if (!/^\s*at\s+/.test(line)) return null;
391
+ const v8 = V8_FRAME.exec(line);
392
+ if (v8) {
393
+ const functionName = cleanFunction(v8[1]);
394
+ const absPath = stripQuery(v8[2] ?? "");
395
+ const lineno = Number.parseInt(v8[3] ?? "", 10);
396
+ const colno = Number.parseInt(v8[4] ?? "", 10);
397
+ return {
398
+ functionName,
399
+ absPath: absPath || void 0,
400
+ filename: basename(absPath),
401
+ lineno: Number.isFinite(lineno) ? lineno : void 0,
402
+ colno: Number.isFinite(colno) ? colno : void 0,
403
+ inApp: isInAppFrame(absPath, inAppOptions),
404
+ platform: "javascript"
405
+ };
406
+ }
407
+ const bare = V8_FRAME_BARE.exec(line);
408
+ if (bare) {
409
+ const functionName = cleanFunction(bare[1]);
410
+ if (!functionName) return null;
411
+ return {
412
+ functionName,
413
+ inApp: false,
414
+ platform: "javascript"
415
+ };
416
+ }
417
+ return null;
418
+ }
419
+ function applySourceLocation(stacktrace, source, inAppOptions) {
420
+ if (!source) return stacktrace;
421
+ const filename = source.filename?.trim() || void 0;
422
+ const lineno = typeof source.lineno === "number" && source.lineno > 0 ? source.lineno : void 0;
423
+ const colno = typeof source.colno === "number" && source.colno >= 0 ? source.colno : void 0;
424
+ if (!filename && lineno == null && colno == null) return stacktrace;
425
+ if (!stacktrace || stacktrace.frames.length === 0) {
426
+ if (!filename && lineno == null) return stacktrace;
427
+ const absPath = filename;
428
+ return {
429
+ frames: [
430
+ {
431
+ filename: absPath ? basename(absPath) : void 0,
432
+ absPath,
433
+ lineno,
434
+ colno,
435
+ inApp: absPath ? isInAppFrame(absPath, inAppOptions) : true,
436
+ platform: "javascript"
437
+ }
438
+ ]
439
+ };
440
+ }
441
+ const frames = stacktrace.frames.slice();
442
+ const top = { ...frames[frames.length - 1] };
443
+ if (filename) {
444
+ top.absPath = filename;
445
+ top.filename = basename(filename);
446
+ top.inApp = isInAppFrame(filename, inAppOptions);
447
+ }
448
+ if (lineno != null) top.lineno = lineno;
449
+ if (colno != null) top.colno = colno;
450
+ frames[frames.length - 1] = top;
451
+ return { ...stacktrace, frames };
452
+ }
453
+ function isInAppFrame(path, options) {
454
+ const lower = path.toLowerCase();
455
+ if (!lower) return true;
456
+ if (lower.includes("node_modules")) return false;
457
+ if (EXTENSION_URL2.test(path) || lower.includes("webkit-masked-url://")) {
458
+ return false;
459
+ }
460
+ if (lower.includes("@newtalaria/browser")) return false;
461
+ if (matchesUrlList(path, options?.denyUrls)) return false;
462
+ if (matchesUrlList(path, options?.allowUrls)) return true;
463
+ const frameOrigin = tryParseOrigin(path);
464
+ if (frameOrigin != null) {
465
+ const pageOrigin = normalizeOrigin(options?.pageOrigin);
466
+ if (pageOrigin && frameOrigin === pageOrigin) return true;
467
+ const extra = options?.inAppOrigins ?? [];
468
+ for (const origin of extra) {
469
+ if (normalizeOrigin(origin) === frameOrigin) return true;
470
+ }
471
+ return false;
472
+ }
473
+ return true;
474
+ }
475
+ function matchesUrlList(path, patterns) {
476
+ if (!patterns || patterns.length === 0) return false;
477
+ for (const pattern of patterns) {
478
+ if (typeof pattern === "string") {
479
+ if (pattern && path.includes(pattern)) return true;
480
+ } else if (pattern.test(path)) {
481
+ return true;
482
+ }
483
+ }
484
+ return false;
485
+ }
486
+ function tryParseOrigin(path) {
487
+ if (!/^https?:\/\//i.test(path)) return null;
488
+ try {
489
+ return new URL(path).origin;
490
+ } catch {
491
+ return null;
492
+ }
493
+ }
494
+ function normalizeOrigin(origin) {
495
+ if (!origin || !origin.trim()) return null;
496
+ const trimmed = origin.trim();
497
+ try {
498
+ if (/^https?:\/\//i.test(trimmed)) {
499
+ return new URL(trimmed).origin;
500
+ }
501
+ return trimmed;
502
+ } catch {
503
+ return trimmed;
504
+ }
505
+ }
506
+ function resolvePageOrigin(originOrUrl) {
507
+ return normalizeOrigin(originOrUrl) ?? void 0;
508
+ }
509
+ function cleanFunction(raw) {
510
+ if (raw == null) return void 0;
511
+ const value = raw.trim();
512
+ return value.length === 0 ? void 0 : value;
513
+ }
514
+ function stripQuery(path) {
515
+ const q = path.indexOf("?");
516
+ return q >= 0 ? path.slice(0, q) : path;
517
+ }
518
+ function basename(path) {
519
+ if (!path) return void 0;
520
+ const normalized = stripQuery(path);
521
+ const parts = normalized.split(/[/\\]/);
522
+ const last = parts[parts.length - 1];
523
+ return last || normalized;
524
+ }
525
+
273
526
  // src/sdk_meta.ts
274
527
  var SDK_NAME = "@newtalaria/browser";
275
528
  var SDK_VERSION = "0.1.18";
@@ -330,6 +583,9 @@ async function ingestEvent(transport, params) {
330
583
  if (params.eventType) input2.eventType = params.eventType;
331
584
  if (params.title) input2.title = params.title;
332
585
  if (params.stackTrace) input2.stackTrace = params.stackTrace;
586
+ if (params.exception) input2.exception = serializeException(params.exception);
587
+ if (params.debugMeta) input2.debugMeta = serializeDebugMeta(params.debugMeta);
588
+ if (params.platform) input2.platform = params.platform;
333
589
  if (params.release) input2.release = params.release;
334
590
  if (params.userId) input2.userId = params.userId;
335
591
  if (params.sessionId) input2.sessionId = params.sessionId;
@@ -346,6 +602,89 @@ async function ingestEvent(transport, params) {
346
602
  { keepalive: params.keepalive }
347
603
  );
348
604
  }
605
+ function serializeException(data) {
606
+ return {
607
+ __className__: "ExceptionDataDto",
608
+ values: data.values.map(serializeExceptionValue)
609
+ };
610
+ }
611
+ function serializeExceptionValue(value) {
612
+ const out = {
613
+ __className__: "ExceptionValueDto"
614
+ };
615
+ if (value.type != null) out.type = value.type;
616
+ if (value.value != null) out.value = value.value;
617
+ if (value.module != null) out.module = value.module;
618
+ if (value.threadId != null) out.threadId = value.threadId;
619
+ if (value.code != null) out.code = value.code;
620
+ if (value.mechanism) out.mechanism = serializeMechanism(value.mechanism);
621
+ if (value.stacktrace) out.stacktrace = serializeStackTrace(value.stacktrace);
622
+ return out;
623
+ }
624
+ function serializeMechanism(mechanism) {
625
+ const out = {
626
+ __className__: "ExceptionMechanismDto",
627
+ type: mechanism.type
628
+ };
629
+ if (mechanism.handled != null) out.handled = mechanism.handled;
630
+ if (mechanism.synthetic != null) out.synthetic = mechanism.synthetic;
631
+ return out;
632
+ }
633
+ function serializeStackTrace(stack) {
634
+ const out = {
635
+ __className__: "StackTraceDto",
636
+ frames: stack.frames.map(serializeStackFrame)
637
+ };
638
+ if (stack.registers) out.registers = stack.registers;
639
+ return out;
640
+ }
641
+ function serializeStackFrame(frame) {
642
+ const out = {
643
+ __className__: "StackFrameDto"
644
+ };
645
+ if (frame.filename != null) out.filename = frame.filename;
646
+ if (frame.absPath != null) out.absPath = frame.absPath;
647
+ if (frame.functionName != null) out.functionName = frame.functionName;
648
+ if (frame.rawFunction != null) out.rawFunction = frame.rawFunction;
649
+ if (frame.module != null) out.module = frame.module;
650
+ if (frame.package != null) out.package = frame.package;
651
+ if (frame.platform != null) out.platform = frame.platform;
652
+ if (frame.lineno != null) out.lineno = frame.lineno;
653
+ if (frame.colno != null) out.colno = frame.colno;
654
+ if (frame.inApp != null) out.inApp = frame.inApp;
655
+ if (frame.instructionAddr != null) out.instructionAddr = frame.instructionAddr;
656
+ if (frame.symbolAddr != null) out.symbolAddr = frame.symbolAddr;
657
+ if (frame.imageAddr != null) out.imageAddr = frame.imageAddr;
658
+ if (frame.addrMode != null) out.addrMode = frame.addrMode;
659
+ if (frame.contextLine != null) out.contextLine = frame.contextLine;
660
+ if (frame.preContext != null) out.preContext = frame.preContext;
661
+ if (frame.postContext != null) out.postContext = frame.postContext;
662
+ if (frame.vars != null) out.vars = frame.vars;
663
+ if (frame.stackStart != null) out.stackStart = frame.stackStart;
664
+ return out;
665
+ }
666
+ function serializeDebugMeta(meta) {
667
+ const out = {
668
+ __className__: "DebugMetaDto"
669
+ };
670
+ if (meta.images) {
671
+ out.images = meta.images.map((image) => {
672
+ const img = {
673
+ __className__: "DebugImageDto"
674
+ };
675
+ if (image.type != null) img.type = image.type;
676
+ if (image.imageAddr != null) img.imageAddr = image.imageAddr;
677
+ if (image.imageSize != null) img.imageSize = image.imageSize;
678
+ if (image.debugId != null) img.debugId = image.debugId;
679
+ if (image.debugFile != null) img.debugFile = image.debugFile;
680
+ if (image.codeId != null) img.codeId = image.codeId;
681
+ if (image.codeFile != null) img.codeFile = image.codeFile;
682
+ if (image.arch != null) img.arch = image.arch;
683
+ return img;
684
+ });
685
+ }
686
+ return out;
687
+ }
349
688
 
350
689
  // src/utils/gzip.ts
351
690
  async function gzipBytes(data) {
@@ -13767,7 +14106,7 @@ function normalizeNetworkOrigin(raw) {
13767
14106
  }
13768
14107
  }
13769
14108
  }
13770
- function resolvePageOrigin(pageOrigin) {
14109
+ function resolvePageOrigin2(pageOrigin) {
13771
14110
  if (pageOrigin) {
13772
14111
  try {
13773
14112
  return new URL(pageOrigin).origin;
@@ -13792,12 +14131,12 @@ function isAllowedNetworkOrigin(requestOrigin, opts) {
13792
14131
  if (!requestOrigin) return false;
13793
14132
  const allow = opts.networkErrorOrigins.map(normalizeNetworkOrigin);
13794
14133
  if (allow.includes("*")) return true;
13795
- const page = resolvePageOrigin(opts.pageOrigin);
14134
+ const page = resolvePageOrigin2(opts.pageOrigin);
13796
14135
  if (page && requestOrigin === page) return true;
13797
14136
  return allow.includes(requestOrigin);
13798
14137
  }
13799
14138
  function classifyNetworkParty(requestOrigin, pageOrigin) {
13800
- const page = resolvePageOrigin(pageOrigin);
14139
+ const page = resolvePageOrigin2(pageOrigin);
13801
14140
  if (page && requestOrigin && requestOrigin === page) return "first_party";
13802
14141
  return "third_party";
13803
14142
  }
@@ -14014,6 +14353,16 @@ function installNetworkHook(options = {}) {
14014
14353
  }
14015
14354
 
14016
14355
  // src/client.ts
14356
+ var PLATFORM_JAVASCRIPT = "javascript";
14357
+ var EXTRA_LOCATION_KEYS = /* @__PURE__ */ new Set([
14358
+ "exception_class",
14359
+ "file",
14360
+ "line",
14361
+ "code",
14362
+ "filename",
14363
+ "lineno",
14364
+ "colno"
14365
+ ]);
14017
14366
  function networkExceptionClass(failure) {
14018
14367
  if (failure.failureKind === "http") return "HttpError";
14019
14368
  if (failure.failureKind === "timeout") return "TimeoutError";
@@ -14056,14 +14405,59 @@ function networkFailureExtra(failure) {
14056
14405
  aborted: failure.aborted ?? false,
14057
14406
  ok: failure.ok ?? false
14058
14407
  },
14059
- // Top-level for server fingerprint compatibility.
14408
+ // Top-level for server fingerprint compatibility (status only —
14409
+ // exception type lives on first-class `exception.values[0].type`).
14060
14410
  status_code: statusCode,
14061
- exception_class: networkExceptionClass(failure),
14062
14411
  durationMs: failure.durationMs,
14063
14412
  aborted: failure.aborted ?? false,
14064
14413
  ok: failure.ok ?? false
14065
14414
  };
14066
14415
  }
14416
+ function networkExceptionData(failure, message) {
14417
+ return {
14418
+ values: [
14419
+ {
14420
+ type: networkExceptionClass(failure),
14421
+ value: message,
14422
+ mechanism: {
14423
+ type: "http",
14424
+ handled: true,
14425
+ synthetic: true
14426
+ }
14427
+ }
14428
+ ]
14429
+ };
14430
+ }
14431
+ function scrubLegacyExceptionExtra(extra) {
14432
+ if (!extra) return void 0;
14433
+ const out = {};
14434
+ for (const [key, value] of Object.entries(extra)) {
14435
+ if (EXTRA_LOCATION_KEYS.has(key)) continue;
14436
+ out[key] = value;
14437
+ }
14438
+ return Object.keys(out).length ? out : void 0;
14439
+ }
14440
+ function buildExceptionFromError(err, context, inAppOptions) {
14441
+ const mechanism = context?.mechanism ?? {
14442
+ type: "generic",
14443
+ handled: true
14444
+ };
14445
+ const stacktrace = applySourceLocation(
14446
+ parseStackTrace(err.stack, inAppOptions),
14447
+ context?.source,
14448
+ inAppOptions
14449
+ );
14450
+ return {
14451
+ values: [
14452
+ {
14453
+ type: err.name || "Error",
14454
+ value: err.message || err.name || "Error",
14455
+ mechanism,
14456
+ ...stacktrace ? { stacktrace } : {}
14457
+ }
14458
+ ]
14459
+ };
14460
+ }
14067
14461
  function mergeNetworkFailureContext(context, failure) {
14068
14462
  return {
14069
14463
  ...context,
@@ -14100,14 +14494,17 @@ function resolveOptions(options) {
14100
14494
  inlineStylesheet: options.inlineStylesheet ?? false,
14101
14495
  blockSelector: options.blockSelector ?? "",
14102
14496
  userId: options.userId,
14103
- tags: options.tags,
14497
+ tags: mergeTags(options.tags),
14104
14498
  disableDefaultIntegrations: options.disableDefaultIntegrations ?? false,
14105
14499
  captureFailedRequests: options.captureFailedRequests ?? true,
14106
14500
  captureNetworkErrors: options.captureNetworkErrors ?? true,
14107
14501
  networkErrorOrigins: options.networkErrorOrigins ?? [],
14108
14502
  includeNetworkUrlQuery: options.captureRequestQueryParameters ?? options.includeNetworkUrlQuery ?? false,
14109
14503
  failedRequestStatusCodes: options.failedRequestStatusCodes ?? [[500, 599]],
14110
- failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? []
14504
+ failedRequestIgnoreUrls: options.failedRequestIgnoreUrls ?? [],
14505
+ inAppAllowUrls: options.inAppAllowUrls ?? [],
14506
+ inAppDenyUrls: options.inAppDenyUrls ?? [],
14507
+ inAppOrigins: options.inAppOrigins ?? []
14111
14508
  };
14112
14509
  }
14113
14510
  var RECENT_NETWORK_FAILURE_MS = 5e3;
@@ -14254,17 +14651,21 @@ var TalariaClient = class {
14254
14651
  failureKind: "http",
14255
14652
  aborted: false
14256
14653
  };
14257
- void this.captureMessage(
14258
- `HTTP ${status}: ${method} ${url}`,
14259
- status >= 500 ? "error" : "warning",
14260
- {
14654
+ const message = `HTTP ${status}: ${method} ${url}`;
14655
+ void this.capture({
14656
+ message,
14657
+ level: status >= 500 ? "error" : "warning",
14658
+ title: networkExceptionClass(enriched),
14659
+ exception: networkExceptionData(enriched, message),
14660
+ platform: PLATFORM_JAVASCRIPT,
14661
+ context: {
14261
14662
  tags: {
14262
14663
  ...networkFailureTags(enriched),
14263
14664
  "http.status_code": String(status)
14264
14665
  },
14265
14666
  extra: networkFailureExtra(enriched)
14266
14667
  }
14267
- );
14668
+ });
14268
14669
  },
14269
14670
  onNetworkError: (meta) => {
14270
14671
  this.rememberNetworkFailure(meta, { promoted: true });
@@ -14272,14 +14673,18 @@ var TalariaClient = class {
14272
14673
  const url = meta.url || "(unknown url)";
14273
14674
  const errLabel = meta.errorName && meta.errorMessage ? `${meta.errorName}: ${meta.errorMessage}` : meta.errorMessage || meta.errorName || "Failed to fetch";
14274
14675
  const prefix = meta.failureKind === "timeout" ? "Timeout error" : "Network error";
14275
- void this.captureMessage(
14276
- `${prefix}: ${method} ${url} \u2014 ${errLabel}`,
14277
- "error",
14278
- {
14676
+ const message = `${prefix}: ${method} ${url} \u2014 ${errLabel}`;
14677
+ void this.capture({
14678
+ message,
14679
+ level: "error",
14680
+ title: networkExceptionClass(meta),
14681
+ exception: networkExceptionData(meta, message),
14682
+ platform: PLATFORM_JAVASCRIPT,
14683
+ context: {
14279
14684
  tags: networkFailureTags(meta),
14280
14685
  extra: networkFailureExtra(meta)
14281
14686
  }
14282
- );
14687
+ });
14283
14688
  }
14284
14689
  }),
14285
14690
  installVisibilityResumeHook(() => this.onForegroundResume())
@@ -14316,7 +14721,7 @@ var TalariaClient = class {
14316
14721
  if (this.capturing || this.ingestDisabled) return;
14317
14722
  const err = error instanceof Error ? error : new Error(typeof error === "string" ? error : "Unknown error");
14318
14723
  if (isAbortError(err)) return;
14319
- const filename = typeof context?.extra?.filename === "string" ? context.extra.filename : void 0;
14724
+ const filename = context?.source?.filename;
14320
14725
  if (isBrowserExtensionNoise({
14321
14726
  message: err.message,
14322
14727
  stack: err.stack,
@@ -14338,6 +14743,12 @@ var TalariaClient = class {
14338
14743
  level: "error",
14339
14744
  title: err.name || "Error",
14340
14745
  stackTrace: err.stack,
14746
+ exception: buildExceptionFromError(
14747
+ err,
14748
+ mergedContext,
14749
+ this.inAppFrameOptions()
14750
+ ),
14751
+ platform: PLATFORM_JAVASCRIPT,
14341
14752
  context: mergedContext
14342
14753
  });
14343
14754
  } finally {
@@ -14353,6 +14764,13 @@ var TalariaClient = class {
14353
14764
  context
14354
14765
  });
14355
14766
  }
14767
+ /**
14768
+ * Returns a scoped capture facade that merges [tags] into every event.
14769
+ * Nested `withTags` calls merge further (later keys win).
14770
+ */
14771
+ withTags(tags) {
14772
+ return createScopedTalaria(this, mergeTags(tags));
14773
+ }
14356
14774
  async flush(opts) {
14357
14775
  if (!this.options || !this.transport || this.closed) return;
14358
14776
  const keepalive = opts?.keepalive ?? false;
@@ -14517,13 +14935,17 @@ var TalariaClient = class {
14517
14935
  }
14518
14936
  const queuedMs = Math.max(0, Date.now() - occurredAt.getTime());
14519
14937
  const tags = applyReplayCaptureTags(
14520
- {
14521
- ...this.browserContext ? browserContextTags(this.browserContext) : {},
14522
- ...this.options.tags ?? {},
14523
- ...args.context?.tags ?? {}
14524
- },
14938
+ mergeTags(
14939
+ this.browserContext ? browserContextTags(this.browserContext) : {},
14940
+ this.options.tags,
14941
+ args.context?.tags
14942
+ ),
14525
14943
  errorClipOutcome
14526
14944
  );
14945
+ warnSuspiciousTags(tags, this.options.environment);
14946
+ const scrubbedContextExtra = scrubLegacyExceptionExtra(
14947
+ args.context?.extra
14948
+ );
14527
14949
  const extra = mergeReplayCaptureExtra(
14528
14950
  {
14529
14951
  ...this.browserContext ? {
@@ -14545,7 +14967,7 @@ var TalariaClient = class {
14545
14967
  // Time spent in capture() before ingest (mostly replay flush).
14546
14968
  ...queuedMs >= 50 ? { queuedMs } : {}
14547
14969
  },
14548
- ...args.context?.extra
14970
+ ...scrubbedContextExtra ?? {}
14549
14971
  },
14550
14972
  errorClipOutcome
14551
14973
  );
@@ -14557,6 +14979,8 @@ var TalariaClient = class {
14557
14979
  eventType: levelToEventType(args.level),
14558
14980
  title: args.title ?? args.context?.title,
14559
14981
  stackTrace: args.stackTrace,
14982
+ exception: args.exception,
14983
+ platform: args.platform,
14560
14984
  release: this.options.release,
14561
14985
  userId: args.context?.userId ?? this.options.userId,
14562
14986
  sessionId: this.sessionId ?? void 0,
@@ -14806,6 +15230,23 @@ var TalariaClient = class {
14806
15230
  this.resetToBufferMode();
14807
15231
  }
14808
15232
  }
15233
+ inAppFrameOptions() {
15234
+ const opts = this.options;
15235
+ let pageOrigin;
15236
+ try {
15237
+ pageOrigin = resolvePageOrigin(
15238
+ typeof window !== "undefined" ? window.location?.origin : void 0
15239
+ );
15240
+ } catch {
15241
+ pageOrigin = void 0;
15242
+ }
15243
+ return {
15244
+ pageOrigin,
15245
+ allowUrls: opts?.inAppAllowUrls,
15246
+ denyUrls: opts?.inAppDenyUrls,
15247
+ inAppOrigins: opts?.inAppOrigins
15248
+ };
15249
+ }
14809
15250
  pruneRecentNetworkFailures(now = Date.now()) {
14810
15251
  this.recentNetworkFailures = this.recentNetworkFailures.filter(
14811
15252
  (f) => now - f.at <= RECENT_NETWORK_FAILURE_MS
@@ -14880,7 +15321,8 @@ var TalariaClient = class {
14880
15321
  return;
14881
15322
  }
14882
15323
  void this.captureException(error, {
14883
- extra: {
15324
+ mechanism: { type: "onerror", handled: false },
15325
+ source: {
14884
15326
  filename: event.filename,
14885
15327
  lineno: event.lineno,
14886
15328
  colno: event.colno
@@ -14902,7 +15344,9 @@ var TalariaClient = class {
14902
15344
  })) {
14903
15345
  return;
14904
15346
  }
14905
- void this.captureException(reason);
15347
+ void this.captureException(reason, {
15348
+ mechanism: { type: "unhandledrejection", handled: false }
15349
+ });
14906
15350
  };
14907
15351
  window.addEventListener("error", onError);
14908
15352
  window.addEventListener("unhandledrejection", onRejection);
@@ -15164,6 +15608,23 @@ var TalariaClient = class {
15164
15608
  }
15165
15609
  }
15166
15610
  };
15611
+ function createScopedTalaria(client2, scopeTags) {
15612
+ const mergeContext = (context) => ({
15613
+ ...context,
15614
+ tags: mergeTags(scopeTags, context?.tags)
15615
+ });
15616
+ return {
15617
+ captureException(error, context) {
15618
+ return client2.captureException(error, mergeContext(context));
15619
+ },
15620
+ captureMessage(message, level, context) {
15621
+ return client2.captureMessage(message, level, mergeContext(context));
15622
+ },
15623
+ withTags(tags) {
15624
+ return createScopedTalaria(client2, mergeTags(scopeTags, tags));
15625
+ }
15626
+ };
15627
+ }
15167
15628
 
15168
15629
  // src/index.ts
15169
15630
  var client = new TalariaClient();
@@ -15177,6 +15638,9 @@ var Talaria = {
15177
15638
  captureMessage(message, level, context) {
15178
15639
  return client.captureMessage(message, level, context);
15179
15640
  },
15641
+ withTags(tags) {
15642
+ return client.withTags(tags);
15643
+ },
15180
15644
  getReplayId() {
15181
15645
  return client.getReplayId();
15182
15646
  },
@@ -15194,14 +15658,22 @@ export {
15194
15658
  MAX_SEGMENTS_ERROR_CLIP,
15195
15659
  REPLAY_CAPTURE_REASON_TAG,
15196
15660
  REPLAY_CAPTURE_TAG,
15661
+ RESERVED_TAG_KEYS,
15197
15662
  TARGET_COMPRESSED_SEGMENT_BYTES,
15198
15663
  Talaria,
15199
15664
  TalariaClient,
15665
+ applySourceLocation,
15200
15666
  computeErrorClipDeadlineMs,
15201
15667
  index_default as default,
15202
15668
  fitCompressedPrefix,
15203
15669
  isErrorClipBudgetExhausted,
15204
- planOversizedRetry
15670
+ isInAppFrame,
15671
+ mergeTags,
15672
+ normalizeTags,
15673
+ parseStackLine,
15674
+ parseStackTrace,
15675
+ planOversizedRetry,
15676
+ resolvePageOrigin
15205
15677
  };
15206
15678
  /*! Bundled license information:
15207
15679