@vigilon/browser 0.2.14 → 0.2.15

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/README.md CHANGED
@@ -58,6 +58,18 @@ setTag("plan", "pro");
58
58
 
59
59
  `captureException` accepts any thrown value. Non-`Error` values are recorded with their string or JSON form as the message, and an error's `cause` chain is captured up to five levels.
60
60
 
61
+ ## Pages
62
+
63
+ Vigilon groups an error's affected pages by route template, such as `/users/:id`, so a page with IDs in its URL still counts as one page. Without a template it derives one from the URL path, replacing segments that look like IDs. If your router knows the template, set it on every navigation:
64
+
65
+ ```ts
66
+ import { setRoute } from "@vigilon/browser";
67
+
68
+ setRoute("/users/:id"); // the template, never the URL; pass null to clear it
69
+ ```
70
+
71
+ `@vigilon/react` does this for TanStack Router and React Router.
72
+
61
73
  ## Configuration
62
74
 
63
75
  | Option | Default | |
package/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  close: () => close,
26
26
  flush: () => flush,
27
27
  init: () => init,
28
+ setRoute: () => setRoute,
28
29
  setTag: () => setTag,
29
30
  setUser: () => setUser
30
31
  });
@@ -556,7 +557,7 @@ function sessionId() {
556
557
 
557
558
  // src/browser/client.ts
558
559
  var SDK_NAME = "@vigilon/browser";
559
- var SDK_VERSION = "0.2.14";
560
+ var SDK_VERSION = "0.2.15";
560
561
  var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
561
562
  var EVENTS_PATH = "/v1/browser/events";
562
563
  var DUPLICATE_WINDOW_MS = 6e4;
@@ -566,6 +567,7 @@ var BrowserClient = class {
566
567
  this.tags = {};
567
568
  this.debugIds = new DebugIdIndex(globalThis);
568
569
  this.userId = null;
570
+ this.route = null;
569
571
  this.removeListeners = [];
570
572
  var _a, _b, _c, _d;
571
573
  const endpoint = ((_a = options.endpoint) != null ? _a : DEFAULT_ENDPOINT).replace(/\/+$/, "");
@@ -627,6 +629,9 @@ var BrowserClient = class {
627
629
  setTag(key, value) {
628
630
  this.tags[key] = String(value);
629
631
  }
632
+ setRoute(route) {
633
+ this.route = route === null || route === "" ? null : String(route);
634
+ }
630
635
  flush(timeoutMs) {
631
636
  return this.transport.flush(timeoutMs);
632
637
  }
@@ -639,7 +644,7 @@ var BrowserClient = class {
639
644
  return flushed;
640
645
  }
641
646
  captureUnsafe(error, context, sourceUrl) {
642
- var _a, _b, _c, _d;
647
+ var _a, _b, _c, _d, _e;
643
648
  const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
644
649
  if (sampleRate < 1 && Math.random() >= sampleRate) {
645
650
  return null;
@@ -657,6 +662,7 @@ var BrowserClient = class {
657
662
  ...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
658
663
  ]);
659
664
  const session = sessionId();
665
+ const route = (_e = context.route) != null ? _e : this.route;
660
666
  let event = {
661
667
  type: "exception",
662
668
  eventId: uuid(),
@@ -665,7 +671,7 @@ var BrowserClient = class {
665
671
  ...debugIds ? { debugIds } : {},
666
672
  page: {
667
673
  url: window.location.href,
668
- ...context.route ? { route: context.route } : {}
674
+ ...route ? { route } : {}
669
675
  },
670
676
  session: session === null ? null : { id: session },
671
677
  user: this.userId === null ? null : { id: this.userId },
@@ -776,6 +782,9 @@ function captureException(error, context) {
776
782
  function setUser(user) {
777
783
  client == null ? void 0 : client.setUser(user);
778
784
  }
785
+ function setRoute(route) {
786
+ client == null ? void 0 : client.setRoute(route);
787
+ }
779
788
  function setTag(key, value) {
780
789
  client == null ? void 0 : client.setTag(key, value);
781
790
  }
@@ -797,6 +806,7 @@ async function close(timeoutMs) {
797
806
  close,
798
807
  flush,
799
808
  init,
809
+ setRoute,
800
810
  setTag,
801
811
  setUser
802
812
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["init"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["init"]}
package/dist/index.d.mts CHANGED
@@ -67,7 +67,7 @@ interface VigilonBrowserOptions {
67
67
  interface CaptureContext {
68
68
  mechanism?: ExceptionMechanism;
69
69
  handled?: boolean;
70
- /** Overrides the route template for this event. */
70
+ /** Overrides the route template set with setRoute, for this event only. */
71
71
  route?: string;
72
72
  componentStack?: string;
73
73
  }
@@ -95,6 +95,13 @@ declare function captureException(error: unknown, context?: CaptureContext): str
95
95
  declare function setUser(user: {
96
96
  id: string;
97
97
  } | null): void;
98
+ /**
99
+ * Sets the route template of the current page, such as `/users/:id`, for
100
+ * later events; pass null to clear it. Vigilon groups pages by it, so it must
101
+ * be a template, never a URL with IDs in it. Without one, Vigilon derives the
102
+ * page from the URL. Router integrations call this on every navigation.
103
+ */
104
+ declare function setRoute(route: string | null): void;
98
105
  /** Attaches a tag to later events. */
99
106
  declare function setTag(key: string, value: string): void;
100
107
  /** Sends queued events now. Resolves true when everything was sent. */
@@ -102,4 +109,4 @@ declare function flush(timeoutMs?: number): Promise<boolean>;
102
109
  /** Flushes, then stops capturing errors. */
103
110
  declare function close(timeoutMs?: number): Promise<boolean>;
104
111
 
105
- export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setTag, setUser };
112
+ export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setRoute, setTag, setUser };
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ interface VigilonBrowserOptions {
67
67
  interface CaptureContext {
68
68
  mechanism?: ExceptionMechanism;
69
69
  handled?: boolean;
70
- /** Overrides the route template for this event. */
70
+ /** Overrides the route template set with setRoute, for this event only. */
71
71
  route?: string;
72
72
  componentStack?: string;
73
73
  }
@@ -95,6 +95,13 @@ declare function captureException(error: unknown, context?: CaptureContext): str
95
95
  declare function setUser(user: {
96
96
  id: string;
97
97
  } | null): void;
98
+ /**
99
+ * Sets the route template of the current page, such as `/users/:id`, for
100
+ * later events; pass null to clear it. Vigilon groups pages by it, so it must
101
+ * be a template, never a URL with IDs in it. Without one, Vigilon derives the
102
+ * page from the URL. Router integrations call this on every navigation.
103
+ */
104
+ declare function setRoute(route: string | null): void;
98
105
  /** Attaches a tag to later events. */
99
106
  declare function setTag(key: string, value: string): void;
100
107
  /** Sends queued events now. Resolves true when everything was sent. */
@@ -102,4 +109,4 @@ declare function flush(timeoutMs?: number): Promise<boolean>;
102
109
  /** Flushes, then stops capturing errors. */
103
110
  declare function close(timeoutMs?: number): Promise<boolean>;
104
111
 
105
- export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setTag, setUser };
112
+ export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setRoute, setTag, setUser };
package/dist/index.mjs CHANGED
@@ -524,7 +524,7 @@ function sessionId() {
524
524
 
525
525
  // src/browser/client.ts
526
526
  var SDK_NAME = "@vigilon/browser";
527
- var SDK_VERSION = "0.2.14";
527
+ var SDK_VERSION = "0.2.15";
528
528
  var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
529
529
  var EVENTS_PATH = "/v1/browser/events";
530
530
  var DUPLICATE_WINDOW_MS = 6e4;
@@ -534,6 +534,7 @@ var BrowserClient = class {
534
534
  this.tags = {};
535
535
  this.debugIds = new DebugIdIndex(globalThis);
536
536
  this.userId = null;
537
+ this.route = null;
537
538
  this.removeListeners = [];
538
539
  var _a, _b, _c, _d;
539
540
  const endpoint = ((_a = options.endpoint) != null ? _a : DEFAULT_ENDPOINT).replace(/\/+$/, "");
@@ -595,6 +596,9 @@ var BrowserClient = class {
595
596
  setTag(key, value) {
596
597
  this.tags[key] = String(value);
597
598
  }
599
+ setRoute(route) {
600
+ this.route = route === null || route === "" ? null : String(route);
601
+ }
598
602
  flush(timeoutMs) {
599
603
  return this.transport.flush(timeoutMs);
600
604
  }
@@ -607,7 +611,7 @@ var BrowserClient = class {
607
611
  return flushed;
608
612
  }
609
613
  captureUnsafe(error, context, sourceUrl) {
610
- var _a, _b, _c, _d;
614
+ var _a, _b, _c, _d, _e;
611
615
  const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
612
616
  if (sampleRate < 1 && Math.random() >= sampleRate) {
613
617
  return null;
@@ -625,6 +629,7 @@ var BrowserClient = class {
625
629
  ...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
626
630
  ]);
627
631
  const session = sessionId();
632
+ const route = (_e = context.route) != null ? _e : this.route;
628
633
  let event = {
629
634
  type: "exception",
630
635
  eventId: uuid(),
@@ -633,7 +638,7 @@ var BrowserClient = class {
633
638
  ...debugIds ? { debugIds } : {},
634
639
  page: {
635
640
  url: window.location.href,
636
- ...context.route ? { route: context.route } : {}
641
+ ...route ? { route } : {}
637
642
  },
638
643
  session: session === null ? null : { id: session },
639
644
  user: this.userId === null ? null : { id: this.userId },
@@ -744,6 +749,9 @@ function captureException(error, context) {
744
749
  function setUser(user) {
745
750
  client == null ? void 0 : client.setUser(user);
746
751
  }
752
+ function setRoute(route) {
753
+ client == null ? void 0 : client.setRoute(route);
754
+ }
747
755
  function setTag(key, value) {
748
756
  client == null ? void 0 : client.setTag(key, value);
749
757
  }
@@ -764,6 +772,7 @@ export {
764
772
  close,
765
773
  flush,
766
774
  init,
775
+ setRoute,
767
776
  setTag,
768
777
  setUser
769
778
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts","../src/index.ts"],"sourcesContent":["/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n","/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n"],"mappings":"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","names":["init"]}
1
+ {"version":3,"sources":["../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts","../src/index.ts"],"sourcesContent":["/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n","/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n"],"mappings":"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","names":["init"]}
@@ -1,10 +1,10 @@
1
- "use strict";var Vigilon=(()=>{var k=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Q=Object.prototype.hasOwnProperty;var Z=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},ee=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of J(e))!Q.call(t,i)&&i!==n&&k(t,i,{get:()=>e[i],enumerable:!(r=W(e,i))||r.enumerable});return t};var te=t=>ee(k({},"__esModule",{value:!0}),t);var Me={};Z(Me,{SDK_VERSION:()=>_,captureException:()=>Pe,close:()=>Be,flush:()=>Oe,init:()=>Le,setTag:()=>Ue,setUser:()=>Ae});var o={eventsPerRequest:20,serviceNameLength:100,serviceVersionLength:200,exceptionTypeLength:256,exceptionMessageLength:8192,exceptionStackLength:65536,exceptionStackLines:250,componentStackLength:16384,causeDepth:5,debugIdEntries:100,debugIdKeyLength:2048,pageUrlLength:2048,pageRouteLength:200,identifierLength:128,tagEntries:20,tagKeyLength:64,tagValueLength:256},A=/^[A-Za-z0-9._:/-]+$/,U="vigilon.browser.event@1";function d(t,e){if(t.length<=e)return t;let n=t.charCodeAt(e-1),r=n>=55296&&n<=56319?e-1:e;return t.slice(0,r)}function O(t){let e=t.split(`
1
+ "use strict";var Vigilon=(()=>{var k=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var te=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},ne=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Z(e))!ee.call(t,i)&&i!==n&&k(t,i,{get:()=>e[i],enumerable:!(r=Q(e,i))||r.enumerable});return t};var re=t=>ne(k({},"__esModule",{value:!0}),t);var Ve={};te(Ve,{SDK_VERSION:()=>R,captureException:()=>Ue,close:()=>Ne,flush:()=>De,init:()=>Ae,setRoute:()=>Be,setTag:()=>Me,setUser:()=>Oe});var o={eventsPerRequest:20,serviceNameLength:100,serviceVersionLength:200,exceptionTypeLength:256,exceptionMessageLength:8192,exceptionStackLength:65536,exceptionStackLines:250,componentStackLength:16384,causeDepth:5,debugIdEntries:100,debugIdKeyLength:2048,pageUrlLength:2048,pageRouteLength:200,identifierLength:128,tagEntries:20,tagKeyLength:64,tagValueLength:256},O=/^[A-Za-z0-9._:/-]+$/,B="vigilon.browser.event@1";function p(t,e){if(t.length<=e)return t;let n=t.charCodeAt(e-1),r=n>=55296&&n<=56319?e-1:e;return t.slice(0,r)}function M(t){let e=t.split(`
2
2
  `),n=e.length>o.exceptionStackLines?e.slice(0,o.exceptionStackLines).join(`
3
- `):t;return d(n,o.exceptionStackLength)}var ne=/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i,re=/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi,ie=/(:\d+:\d+)?([)\]}>"',;.]*)$/;function g(t){let e=t.replace(ne,"$1"),n=e.indexOf("?"),r=e.indexOf("#"),i=n===-1?r:r===-1?n:Math.min(n,r);return i===-1?e:e.slice(0,i)}function f(t){return t.replace(re,e=>{var r,i;let n=(i=(r=ie.exec(e))==null?void 0:r[0])!=null?i:"";return g(e.slice(0,e.length-n.length))+n})}var se=["_vigilonDebugIds","_sentryDebugIds","_debugIds"],oe=/([a-z][a-z0-9+.-]*:\/\/\S+?):\d+:\d+/i,m=class{constructor(e){this.scope=e;this.indexedEntries=-1;this.byUrl={}}forStacks(e){let n=f(e.filter(Boolean).join(`
4
- `));if(n.length===0)return;let r;for(let[i,s]of Object.entries(this.registered()))n.includes(`${i}:`)&&(r!=null||(r={}),r[i]=s);return r}registered(){let e=se.map(i=>this.scope[i]).filter(ue),n=e.reduce((i,s)=>i+Object.keys(s).length,0);if(n===this.indexedEntries)return this.byUrl;let r={};for(let i of e)for(let[s,a]of Object.entries(i)){let l=ae(s);l!==null&&typeof a=="string"&&!(l in r)&&(r[l]=a)}return this.indexedEntries=n,this.byUrl=r,r}};function ae(t){for(let e of t.split(`
5
- `)){let n=oe.exec(e);if(n)return g(n[1])}return null}function ue(t){return typeof t=="object"&&t!==null}function M(t,e,n){let i={...N(t),mechanism:e,handled:n},s=le(t);return s.length>0&&(i.cause=s),i}function D(t){let e={...t,...B(t)};return t.cause&&(e.cause=t.cause.slice(0,o.causeDepth).map(n=>B(n))),e}function B(t){return{type:d(String(t.type),o.exceptionTypeLength),message:d(String(t.message),o.exceptionMessageLength),stack:typeof t.stack=="string"&&t.stack.length>0?O(f(t.stack)):null}}function N(t){return I(t)?{type:typeof t.name=="string"&&t.name?t.name:"Error",message:typeof t.message=="string"?t.message:"",stack:typeof t.stack=="string"?t.stack:null}:{type:"Error",message:ce(t),stack:null}}function le(t){let e=[],n=new Set([t]),r=I(t)?t.cause:void 0;for(;r!=null&&e.length<o.causeDepth&&!n.has(r);)n.add(r),e.push(N(r)),r=I(r)?r.cause:void 0;return e}function I(t){return t instanceof Error||typeof t=="object"&&t!==null&&typeof t.message=="string"&&typeof t.stack=="string"}function ce(t){if(typeof t=="string")return t;try{let e=JSON.stringify(t);return e===void 0?String(t):e}catch{return String(t)}}var V=[/^Script error\.?$/,/^(TypeError: )?Failed to fetch$/,/^(TypeError: )?NetworkError when attempting to fetch resource\.?$/,/^(TypeError: )?Load failed$/,/^AbortError\b/,/The (operation|user) aborted a request/,/The operation was aborted/,/^ResizeObserver loop/],C=[/^(chrome|moz|safari|safari-web|ms-browser)-extension:\/\//,/^webkit-masked-url:\/\//],pe=/\b[a-z][a-z0-9+.-]*:\/\/[^\s)]+/i;function E(t,e){return e.some(n=>typeof n=="string"?t.includes(n):n.test(t))}function $(t,e){let{type:n,message:r}=t.exception;return E(r,e)||E(`${n}: ${r}`,e)||n==="AbortError"&&E("AbortError",e)}function q(t,e,n){var i,s;let r=e||((s=pe.exec((i=t.exception.stack)!=null?i:""))==null?void 0:s[0]);return r!==void 0&&E(r,n)}var v=class{constructor(e,n){this.maxEventsPerPage=e;this.duplicateWindowMs=n;this.lastSeen=new Map;this.accepted=0}admit(e,n){if(this.accepted>=this.maxEventsPerPage)return!1;let{type:r,message:i,stack:s}=e.exception,a=`${r}
3
+ `):t;return p(n,o.exceptionStackLength)}var ie=/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i,se=/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi,oe=/(:\d+:\d+)?([)\]}>"',;.]*)$/;function g(t){let e=t.replace(ie,"$1"),n=e.indexOf("?"),r=e.indexOf("#"),i=n===-1?r:r===-1?n:Math.min(n,r);return i===-1?e:e.slice(0,i)}function f(t){return t.replace(se,e=>{var r,i;let n=(i=(r=oe.exec(e))==null?void 0:r[0])!=null?i:"";return g(e.slice(0,e.length-n.length))+n})}var ae=["_vigilonDebugIds","_sentryDebugIds","_debugIds"],ue=/([a-z][a-z0-9+.-]*:\/\/\S+?):\d+:\d+/i,m=class{constructor(e){this.scope=e;this.indexedEntries=-1;this.byUrl={}}forStacks(e){let n=f(e.filter(Boolean).join(`
4
+ `));if(n.length===0)return;let r;for(let[i,s]of Object.entries(this.registered()))n.includes(`${i}:`)&&(r!=null||(r={}),r[i]=s);return r}registered(){let e=ae.map(i=>this.scope[i]).filter(ce),n=e.reduce((i,s)=>i+Object.keys(s).length,0);if(n===this.indexedEntries)return this.byUrl;let r={};for(let i of e)for(let[s,a]of Object.entries(i)){let c=le(s);c!==null&&typeof a=="string"&&!(c in r)&&(r[c]=a)}return this.indexedEntries=n,this.byUrl=r,r}};function le(t){for(let e of t.split(`
5
+ `)){let n=ue.exec(e);if(n)return g(n[1])}return null}function ce(t){return typeof t=="object"&&t!==null}function N(t,e,n){let i={...C(t),mechanism:e,handled:n},s=pe(t);return s.length>0&&(i.cause=s),i}function V(t){let e={...t,...D(t)};return t.cause&&(e.cause=t.cause.slice(0,o.causeDepth).map(n=>D(n))),e}function D(t){return{type:p(String(t.type),o.exceptionTypeLength),message:p(String(t.message),o.exceptionMessageLength),stack:typeof t.stack=="string"&&t.stack.length>0?M(f(t.stack)):null}}function C(t){return I(t)?{type:typeof t.name=="string"&&t.name?t.name:"Error",message:typeof t.message=="string"?t.message:"",stack:typeof t.stack=="string"?t.stack:null}:{type:"Error",message:de(t),stack:null}}function pe(t){let e=[],n=new Set([t]),r=I(t)?t.cause:void 0;for(;r!=null&&e.length<o.causeDepth&&!n.has(r);)n.add(r),e.push(C(r)),r=I(r)?r.cause:void 0;return e}function I(t){return t instanceof Error||typeof t=="object"&&t!==null&&typeof t.message=="string"&&typeof t.stack=="string"}function de(t){if(typeof t=="string")return t;try{let e=JSON.stringify(t);return e===void 0?String(t):e}catch{return String(t)}}var $=[/^Script error\.?$/,/^(TypeError: )?Failed to fetch$/,/^(TypeError: )?NetworkError when attempting to fetch resource\.?$/,/^(TypeError: )?Load failed$/,/^AbortError\b/,/The (operation|user) aborted a request/,/The operation was aborted/,/^ResizeObserver loop/],q=[/^(chrome|moz|safari|safari-web|ms-browser)-extension:\/\//,/^webkit-masked-url:\/\//],ge=/\b[a-z][a-z0-9+.-]*:\/\/[^\s)]+/i;function E(t,e){return e.some(n=>typeof n=="string"?t.includes(n):n.test(t))}function K(t,e){let{type:n,message:r}=t.exception;return E(r,e)||E(`${n}: ${r}`,e)||n==="AbortError"&&E("AbortError",e)}function F(t,e,n){var i,s;let r=e||((s=ge.exec((i=t.exception.stack)!=null?i:""))==null?void 0:s[0]);return r!==void 0&&E(r,n)}var v=class{constructor(e,n){this.maxEventsPerPage=e;this.duplicateWindowMs=n;this.lastSeen=new Map;this.accepted=0}admit(e,n){if(this.accepted>=this.maxEventsPerPage)return!1;let{type:r,message:i,stack:s}=e.exception,a=`${r}
6
6
  ${i}
7
7
  ${(s!=null?s:"").split(`
8
8
  `,3).join(`
9
- `)}`,l=this.lastSeen.get(a);return l!==void 0&&n-l<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var de=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],ge=/[^\s@]+@[^\s@]+\.[^\s@]+/;function K(t){let e={},n=0;for(let r of Object.keys(t)){if(n===o.tagEntries)break;let i=r.toLowerCase();r.length===0||r.length>o.tagKeyLength||de.some(s=>i.includes(s))||(e[r]=d(String(t[r]),o.tagValueLength),n+=1)}return e}function y(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function T(t){let e=y(t);return e!==null&&ge.test(e)?null:e}var he=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function F(t){let e={},n=0;for(let[r,i]of Object.entries(t)){let s=g(r);if(n===o.debugIdEntries)break;s.length>0&&s.length<=o.debugIdKeyLength&&he.test(String(i))&&(e[s]=String(i),n+=1)}return e}var fe=1e3,z=240*1024,me=60*1024,G=512,j=100,Ee=5,ve=1800*1e3,ye={429:6e4,503:1e4},be=6e4,b=class{constructor(e){this.options=e;this.queue=[];this.pausedUntil=0;this.timer=null;this.loop=null;this.inFlight=new Set}enqueue(e){if(this.queue.length>=j){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:Se(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(fe)}async flush(e=2e3){this.cancelTimer();let n;return await Promise.race([this.drain().then(()=>Promise.all(this.inFlight)),new Promise(r=>{n=setTimeout(r,e)})]),clearTimeout(n),this.queue.length===0&&this.inFlight.size===0}flushOnUnload(){if(this.cancelTimer(),this.options.now()<this.pausedUntil)return;let e=me;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=xe(n),this.track(this.post(n,!0))}}close(){this.cancelTimer(),this.queue=[]}schedule(e){if(this.timer!==null||this.queue.length===0)return;let n=Math.max(e,this.pausedUntil-this.options.now());this.timer=setTimeout(()=>{this.timer=null,this.drain()},n)}cancelTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}drain(){return this.loop===null&&(this.loop=this.run().finally(()=>{this.loop=null})),this.loop}async run(){for(;this.queue.length>0&&this.options.now()>=this.pausedUntil;){let e=this.takeBatch(z);if(e.length===0)break;await this.track(this.post(e,!1))}this.schedule(0)}track(e){return this.inFlight.add(e),e.finally(()=>this.inFlight.delete(e))}takeBatch(e){let n=this.options.now();this.queue=this.queue.filter(s=>{let a=s.attempts>=Ee||s.firstAttemptAt!==null&&n-s.firstAttemptAt>ve;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=G;for(;this.queue.length>0&&r.length<o.eventsPerRequest&&i+this.queue[0].bytes<=e;){let s=this.queue.shift();i+=s.bytes,r.push(s)}return r.length===0&&this.queue.length>0&&e===z&&r.push(this.queue.shift()),r}async post(e,n){var l,p;let r=this.options.now();for(let c of e)c.attempts+=1,(l=c.firstAttemptAt)!=null||(c.firstAttemptAt=r);let i={...this.options.envelope,sentAt:new Date(r).toISOString(),events:e.map(c=>c.event)},s,a=null;try{let c=await this.options.fetch(this.options.url,{method:"POST",headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:n});s=c.status,a=c.headers.get("Retry-After")}catch{this.requeue(e);let c=Math.max(...e.map(h=>h.attempts));this.pause(Math.min(1e3*2**c,be));return}if(s===429||s===503){this.requeue(e),this.pause((p=we(a,this.options.now()))!=null?p:ye[s]);return}(s<200||s>=300)&&this.options.debug(`request rejected with ${s}; ${e.length} events dropped`)}requeue(e){this.queue.unshift(...e),this.queue.length=Math.min(this.queue.length,j)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function we(t,e){if(t===null||t.trim()==="")return null;let n=Number(t);if(Number.isFinite(n))return Math.max(0,n*1e3);let r=Date.parse(t);return Number.isNaN(r)?null:Math.max(0,r-e)}function xe(t){return t.reduce((e,n)=>e+n.bytes,G)}function Se(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function w(){let t=new Uint8Array(16),e=globalThis.crypto;if(e!=null&&e.getRandomValues)e.getRandomValues(t);else for(let r=0;r<16;r+=1)t[r]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0"));return`${n.slice(0,4).join("")}-${n.slice(4,6).join("")}-${n.slice(6,8).join("")}-${n.slice(8,10).join("")}-${n.slice(10).join("")}`}var Y="vigilon.session";function X(){try{let t=window.sessionStorage.getItem(Y);if(t)return t;let e=w();return window.sessionStorage.setItem(Y,e),e}catch{return null}}var ke="@vigilon/browser",_="0.2.14",Ie="https://ingest.vigilon.io",Te="/v1/browser/events",_e=6e4,x=class{constructor(e){this.options=e;this.tags={};this.debugIds=new m(globalThis);this.userId=null;this.removeListeners=[];var r,i,s,a;let n=((r=e.endpoint)!=null?r:Ie).replace(/\/+$/,"");this.transport=new b({url:`${n}${Te}`,apiKey:e.apiKey,envelope:{schema:U,sdk:{name:ke,version:_},service:{name:e.serviceName,environment:e.environment,...e.serviceVersion?{version:d(e.serviceVersion,o.serviceVersionLength)}:{}}},fetch:(l,p)=>window.fetch(l,p),now:()=>Date.now(),debug:l=>this.debug(l)}),this.floodGuard=new v((i=e.maxEventsPerPage)!=null?i:100,_e),this.ignoreErrors=[...V,...(s=e.ignoreErrors)!=null?s:[]],this.denyUrls=[...C,...(a=e.denyUrls)!=null?a:[]]}install(){this.listen(window,"error",e=>{let{error:n,message:r,filename:i}=e;this.capture(n!=null?n:r,{mechanism:"onerror",handled:!1},i)}),this.listen(window,"unhandledrejection",e=>{this.capture(e.reason,{mechanism:"unhandledrejection",handled:!1})}),this.listen(document,"visibilitychange",()=>{document.visibilityState==="hidden"&&this.transport.flushOnUnload()}),this.listen(window,"pagehide",()=>this.transport.flushOnUnload())}capture(e,n={},r){try{return this.captureUnsafe(e,n,r)}catch(i){return this.debug(`capture failed: ${String(i)}`),null}}setUser(e){this.userId=e===null?null:T(e.id),e!==null&&this.userId===null&&this.debug("user.id dropped: it must be an opaque ID of at most 128 characters, not an email address")}setTag(e,n){this.tags[e]=String(n)}flush(e){return this.transport.flush(e)}async close(e){for(let r of this.removeListeners.splice(0))r();let n=await this.transport.flush(e);return this.transport.close(),n}captureUnsafe(e,n,r){var h,R,L,P;let i=(h=this.options.sampleRate)!=null?h:1;if(i<1&&Math.random()>=i)return null;let s=M(e,(R=n.mechanism)!=null?R:"manual",(L=n.handled)!=null?L:!0);n.componentStack&&(s.componentStack=n.componentStack);let a=this.debugIds.forStacks([s.stack,...((P=s.cause)!=null?P:[]).map(S=>S.stack)]),l=X(),p={type:"exception",eventId:w(),timestamp:new Date().toISOString(),exception:s,...a?{debugIds:a}:{},page:{url:window.location.href,...n.route?{route:n.route}:{}},session:l===null?null:{id:l},user:this.userId===null?null:{id:this.userId},traceId:null,tags:{...this.tags}};if($(p,this.ignoreErrors)||q(p,r,this.denyUrls))return null;if(this.options.beforeSend){try{p=this.options.beforeSend(p)}catch(S){return this.debug(`beforeSend threw; event dropped: ${String(S)}`),null}if(p===null)return null}let c=Re(p);return this.floodGuard.admit(c,Date.now())?(this.transport.enqueue(c),c.eventId):null}listen(e,n,r){e.addEventListener(n,r),this.removeListeners.push(()=>e.removeEventListener(n,r))}debug(e){this.options.debug&&console.warn(`[Vigilon] ${e}`)}};function Re(t){var i,s,a;let e={type:"exception",eventId:t.eventId,timestamp:t.timestamp,exception:D(t.exception),page:{url:d(g(String(t.page.url)),o.pageUrlLength)}};t.exception.componentStack&&(e.exception.componentStack=d(t.exception.componentStack,o.componentStackLength)),t.page.route&&(e.page.route=d(t.page.route,o.pageRouteLength));let n=y((i=t.session)==null?void 0:i.id);e.session=n===null?null:{id:n};let r=T((s=t.user)==null?void 0:s.id);return e.user=r===null?null:{id:r},e.traceId=y(t.traceId),e.tags=K((a=t.tags)!=null?a:{}),t.debugIds&&(e.debugIds=F(t.debugIds)),e}function H(t){if(!t||typeof t.apiKey!="string"||t.apiKey.length===0)return"apiKey is required";for(let[e,n]of[["serviceName",t.serviceName],["environment",t.environment]])if(typeof n!="string"||n.length===0||n.length>o.serviceNameLength||!A.test(n))return`${e} must be 1 to 100 characters of letters, digits, and . _ : / -`;return null}var u=null;function Le(t){if(typeof window=="undefined"||typeof document=="undefined")return!1;let e=H(t);return e!==null?(console.error(`[Vigilon] not started: ${e}`),!1):(t.serviceVersion||console.warn("[Vigilon] serviceVersion is not set; errors will not be linked to a release"),u!==null&&u.close(0),u=new x(t),u.install(),!0)}function Pe(t,e){return u===null?null:u.capture(t,e)}function Ae(t){u==null||u.setUser(t)}function Ue(t,e){u==null||u.setTag(t,e)}function Oe(t){return u===null?Promise.resolve(!0):u.flush(t)}async function Be(t){if(u===null)return!0;let e=u;return u=null,e.close(t)}return te(Me);})();
9
+ `)}`,c=this.lastSeen.get(a);return c!==void 0&&n-c<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var he=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],fe=/[^\s@]+@[^\s@]+\.[^\s@]+/;function z(t){let e={},n=0;for(let r of Object.keys(t)){if(n===o.tagEntries)break;let i=r.toLowerCase();r.length===0||r.length>o.tagKeyLength||he.some(s=>i.includes(s))||(e[r]=p(String(t[r]),o.tagValueLength),n+=1)}return e}function y(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function T(t){let e=y(t);return e!==null&&fe.test(e)?null:e}var me=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function j(t){let e={},n=0;for(let[r,i]of Object.entries(t)){let s=g(r);if(n===o.debugIdEntries)break;s.length>0&&s.length<=o.debugIdKeyLength&&me.test(String(i))&&(e[s]=String(i),n+=1)}return e}var Ee=1e3,G=240*1024,ve=60*1024,X=512,Y=100,ye=5,be=1800*1e3,we={429:6e4,503:1e4},xe=6e4,b=class{constructor(e){this.options=e;this.queue=[];this.pausedUntil=0;this.timer=null;this.loop=null;this.inFlight=new Set}enqueue(e){if(this.queue.length>=Y){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:Ie(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(Ee)}async flush(e=2e3){this.cancelTimer();let n;return await Promise.race([this.drain().then(()=>Promise.all(this.inFlight)),new Promise(r=>{n=setTimeout(r,e)})]),clearTimeout(n),this.queue.length===0&&this.inFlight.size===0}flushOnUnload(){if(this.cancelTimer(),this.options.now()<this.pausedUntil)return;let e=ve;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=ke(n),this.track(this.post(n,!0))}}close(){this.cancelTimer(),this.queue=[]}schedule(e){if(this.timer!==null||this.queue.length===0)return;let n=Math.max(e,this.pausedUntil-this.options.now());this.timer=setTimeout(()=>{this.timer=null,this.drain()},n)}cancelTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}drain(){return this.loop===null&&(this.loop=this.run().finally(()=>{this.loop=null})),this.loop}async run(){for(;this.queue.length>0&&this.options.now()>=this.pausedUntil;){let e=this.takeBatch(G);if(e.length===0)break;await this.track(this.post(e,!1))}this.schedule(0)}track(e){return this.inFlight.add(e),e.finally(()=>this.inFlight.delete(e))}takeBatch(e){let n=this.options.now();this.queue=this.queue.filter(s=>{let a=s.attempts>=ye||s.firstAttemptAt!==null&&n-s.firstAttemptAt>be;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=X;for(;this.queue.length>0&&r.length<o.eventsPerRequest&&i+this.queue[0].bytes<=e;){let s=this.queue.shift();i+=s.bytes,r.push(s)}return r.length===0&&this.queue.length>0&&e===G&&r.push(this.queue.shift()),r}async post(e,n){var c,d;let r=this.options.now();for(let l of e)l.attempts+=1,(c=l.firstAttemptAt)!=null||(l.firstAttemptAt=r);let i={...this.options.envelope,sentAt:new Date(r).toISOString(),events:e.map(l=>l.event)},s,a=null;try{let l=await this.options.fetch(this.options.url,{method:"POST",headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:n});s=l.status,a=l.headers.get("Retry-After")}catch{this.requeue(e);let l=Math.max(...e.map(h=>h.attempts));this.pause(Math.min(1e3*2**l,xe));return}if(s===429||s===503){this.requeue(e),this.pause((d=Se(a,this.options.now()))!=null?d:we[s]);return}(s<200||s>=300)&&this.options.debug(`request rejected with ${s}; ${e.length} events dropped`)}requeue(e){this.queue.unshift(...e),this.queue.length=Math.min(this.queue.length,Y)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function Se(t,e){if(t===null||t.trim()==="")return null;let n=Number(t);if(Number.isFinite(n))return Math.max(0,n*1e3);let r=Date.parse(t);return Number.isNaN(r)?null:Math.max(0,r-e)}function ke(t){return t.reduce((e,n)=>e+n.bytes,X)}function Ie(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function w(){let t=new Uint8Array(16),e=globalThis.crypto;if(e!=null&&e.getRandomValues)e.getRandomValues(t);else for(let r=0;r<16;r+=1)t[r]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0"));return`${n.slice(0,4).join("")}-${n.slice(4,6).join("")}-${n.slice(6,8).join("")}-${n.slice(8,10).join("")}-${n.slice(10).join("")}`}var H="vigilon.session";function W(){try{let t=window.sessionStorage.getItem(H);if(t)return t;let e=w();return window.sessionStorage.setItem(H,e),e}catch{return null}}var Te="@vigilon/browser",R="0.2.15",Re="https://ingest.vigilon.io",_e="/v1/browser/events",Le=6e4,x=class{constructor(e){this.options=e;this.tags={};this.debugIds=new m(globalThis);this.userId=null;this.route=null;this.removeListeners=[];var r,i,s,a;let n=((r=e.endpoint)!=null?r:Re).replace(/\/+$/,"");this.transport=new b({url:`${n}${_e}`,apiKey:e.apiKey,envelope:{schema:B,sdk:{name:Te,version:R},service:{name:e.serviceName,environment:e.environment,...e.serviceVersion?{version:p(e.serviceVersion,o.serviceVersionLength)}:{}}},fetch:(c,d)=>window.fetch(c,d),now:()=>Date.now(),debug:c=>this.debug(c)}),this.floodGuard=new v((i=e.maxEventsPerPage)!=null?i:100,Le),this.ignoreErrors=[...$,...(s=e.ignoreErrors)!=null?s:[]],this.denyUrls=[...q,...(a=e.denyUrls)!=null?a:[]]}install(){this.listen(window,"error",e=>{let{error:n,message:r,filename:i}=e;this.capture(n!=null?n:r,{mechanism:"onerror",handled:!1},i)}),this.listen(window,"unhandledrejection",e=>{this.capture(e.reason,{mechanism:"unhandledrejection",handled:!1})}),this.listen(document,"visibilitychange",()=>{document.visibilityState==="hidden"&&this.transport.flushOnUnload()}),this.listen(window,"pagehide",()=>this.transport.flushOnUnload())}capture(e,n={},r){try{return this.captureUnsafe(e,n,r)}catch(i){return this.debug(`capture failed: ${String(i)}`),null}}setUser(e){this.userId=e===null?null:T(e.id),e!==null&&this.userId===null&&this.debug("user.id dropped: it must be an opaque ID of at most 128 characters, not an email address")}setTag(e,n){this.tags[e]=String(n)}setRoute(e){this.route=e===null||e===""?null:String(e)}flush(e){return this.transport.flush(e)}async close(e){for(let r of this.removeListeners.splice(0))r();let n=await this.transport.flush(e);return this.transport.close(),n}captureUnsafe(e,n,r){var _,L,P,A,U;let i=(_=this.options.sampleRate)!=null?_:1;if(i<1&&Math.random()>=i)return null;let s=N(e,(L=n.mechanism)!=null?L:"manual",(P=n.handled)!=null?P:!0);n.componentStack&&(s.componentStack=n.componentStack);let a=this.debugIds.forStacks([s.stack,...((A=s.cause)!=null?A:[]).map(S=>S.stack)]),c=W(),d=(U=n.route)!=null?U:this.route,l={type:"exception",eventId:w(),timestamp:new Date().toISOString(),exception:s,...a?{debugIds:a}:{},page:{url:window.location.href,...d?{route:d}:{}},session:c===null?null:{id:c},user:this.userId===null?null:{id:this.userId},traceId:null,tags:{...this.tags}};if(K(l,this.ignoreErrors)||F(l,r,this.denyUrls))return null;if(this.options.beforeSend){try{l=this.options.beforeSend(l)}catch(S){return this.debug(`beforeSend threw; event dropped: ${String(S)}`),null}if(l===null)return null}let h=Pe(l);return this.floodGuard.admit(h,Date.now())?(this.transport.enqueue(h),h.eventId):null}listen(e,n,r){e.addEventListener(n,r),this.removeListeners.push(()=>e.removeEventListener(n,r))}debug(e){this.options.debug&&console.warn(`[Vigilon] ${e}`)}};function Pe(t){var i,s,a;let e={type:"exception",eventId:t.eventId,timestamp:t.timestamp,exception:V(t.exception),page:{url:p(g(String(t.page.url)),o.pageUrlLength)}};t.exception.componentStack&&(e.exception.componentStack=p(t.exception.componentStack,o.componentStackLength)),t.page.route&&(e.page.route=p(t.page.route,o.pageRouteLength));let n=y((i=t.session)==null?void 0:i.id);e.session=n===null?null:{id:n};let r=T((s=t.user)==null?void 0:s.id);return e.user=r===null?null:{id:r},e.traceId=y(t.traceId),e.tags=z((a=t.tags)!=null?a:{}),t.debugIds&&(e.debugIds=j(t.debugIds)),e}function J(t){if(!t||typeof t.apiKey!="string"||t.apiKey.length===0)return"apiKey is required";for(let[e,n]of[["serviceName",t.serviceName],["environment",t.environment]])if(typeof n!="string"||n.length===0||n.length>o.serviceNameLength||!O.test(n))return`${e} must be 1 to 100 characters of letters, digits, and . _ : / -`;return null}var u=null;function Ae(t){if(typeof window=="undefined"||typeof document=="undefined")return!1;let e=J(t);return e!==null?(console.error(`[Vigilon] not started: ${e}`),!1):(t.serviceVersion||console.warn("[Vigilon] serviceVersion is not set; errors will not be linked to a release"),u!==null&&u.close(0),u=new x(t),u.install(),!0)}function Ue(t,e){return u===null?null:u.capture(t,e)}function Oe(t){u==null||u.setUser(t)}function Be(t){u==null||u.setRoute(t)}function Me(t,e){u==null||u.setTag(t,e)}function De(t){return u===null?Promise.resolve(!0):u.flush(t)}async function Ne(t){if(u===null)return!0;let e=u;return u=null,e.close(t)}return re(Ve);})();
10
10
  //# sourceMappingURL=vigilon.min.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["src_exports","__export","SDK_VERSION","captureException","close","flush","init","setTag","setUser","LIMITS","SERVICE_TOKEN_PATTERN","SCHEMA_ID","truncate","value","maxLength","code","end","truncateStack","stack","lines","kept","LIMITS","USERINFO","URL_IN_TEXT","TRAILER","scrubUrl","url","query","fragment","cut","scrubUrlsInText","text","token","_a","_b","trailer","REGISTRIES","FRAME_URL","DebugIdIndex","scope","stacks","text","scrubUrlsInText","found","url","id","registries","name","isRegistry","entries","total","registry","byUrl","stack","scriptUrl","line","match","scrubUrl","value","toException","error","mechanism","handled","exception","toErrorParts","cause","toCauseChain","limitException","limited","limitParts","LIMITS","parts","truncate","truncateStack","scrubUrlsInText","isErrorLike","formatValue","chain","seen","value","json","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","FIRST_URL","matchesAny","value","patterns","pattern","isIgnoredError","event","type","message","isDeniedUrl","sourceUrl","_a","_b","url","FloodGuard","maxEventsPerPage","duplicateWindowMs","event","now","type","message","stack","key","previous","DENIED_TAG_KEY_FRAGMENTS","EMAIL_LIKE","sanitizeTags","tags","result","count","key","LIMITS","lowered","fragment","truncate","sanitizeIdentifier","value","sanitizeUserId","id","UUID","sanitizeDebugIds","debugIds","url","scrubUrl","BATCH_DELAY_MS","MAX_BODY_BYTES","KEEPALIVE_BUDGET_BYTES","ENVELOPE_OVERHEAD_BYTES","MAX_QUEUE","MAX_ATTEMPTS","MAX_RETRY_AGE_MS","DEFAULT_RETRY_AFTER_MS","MAX_BACKOFF_MS","Transport","options","event","byteLength","timeoutMs","timeout","resolve","budget","batch","batchBytes","delayMs","wait","request","maxBytes","now","pending","expired","bytes","LIMITS","keepalive","_a","_b","envelope","status","retryAfter","response","attempts","parseRetryAfter","ms","value","seconds","date","total","uuid","bytes","crypto","i","hex","byte","SESSION_KEY","sessionId","existing","created","uuid","SDK_NAME","SDK_VERSION","DEFAULT_ENDPOINT","EVENTS_PATH","DUPLICATE_WINDOW_MS","BrowserClient","options","DebugIdIndex","_a","_b","_c","_d","endpoint","Transport","SCHEMA_ID","truncate","LIMITS","input","init","message","FloodGuard","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","event","error","filename","context","sourceUrl","failure","user","sanitizeUserId","key","value","timeoutMs","remove","flushed","sampleRate","exception","toException","debugIds","cause","session","sessionId","uuid","isIgnoredError","isDeniedUrl","limited","limitEvent","target","type","handler","limitException","scrubUrl","sanitizeIdentifier","sanitizeTags","sanitizeDebugIds","invalidOptions","name","SERVICE_TOKEN_PATTERN","client","init","options","problem","invalidOptions","BrowserClient","captureException","error","context","setUser","user","setTag","key","value","flush","timeoutMs","close","closing"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["src_exports","__export","SDK_VERSION","captureException","close","flush","init","setRoute","setTag","setUser","LIMITS","SERVICE_TOKEN_PATTERN","SCHEMA_ID","truncate","value","maxLength","code","end","truncateStack","stack","lines","kept","LIMITS","USERINFO","URL_IN_TEXT","TRAILER","scrubUrl","url","query","fragment","cut","scrubUrlsInText","text","token","_a","_b","trailer","REGISTRIES","FRAME_URL","DebugIdIndex","scope","stacks","text","scrubUrlsInText","found","url","id","registries","name","isRegistry","entries","total","registry","byUrl","stack","scriptUrl","line","match","scrubUrl","value","toException","error","mechanism","handled","exception","toErrorParts","cause","toCauseChain","limitException","limited","limitParts","LIMITS","parts","truncate","truncateStack","scrubUrlsInText","isErrorLike","formatValue","chain","seen","value","json","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","FIRST_URL","matchesAny","value","patterns","pattern","isIgnoredError","event","type","message","isDeniedUrl","sourceUrl","_a","_b","url","FloodGuard","maxEventsPerPage","duplicateWindowMs","event","now","type","message","stack","key","previous","DENIED_TAG_KEY_FRAGMENTS","EMAIL_LIKE","sanitizeTags","tags","result","count","key","LIMITS","lowered","fragment","truncate","sanitizeIdentifier","value","sanitizeUserId","id","UUID","sanitizeDebugIds","debugIds","url","scrubUrl","BATCH_DELAY_MS","MAX_BODY_BYTES","KEEPALIVE_BUDGET_BYTES","ENVELOPE_OVERHEAD_BYTES","MAX_QUEUE","MAX_ATTEMPTS","MAX_RETRY_AGE_MS","DEFAULT_RETRY_AFTER_MS","MAX_BACKOFF_MS","Transport","options","event","byteLength","timeoutMs","timeout","resolve","budget","batch","batchBytes","delayMs","wait","request","maxBytes","now","pending","expired","bytes","LIMITS","keepalive","_a","_b","envelope","status","retryAfter","response","attempts","parseRetryAfter","ms","value","seconds","date","total","uuid","bytes","crypto","i","hex","byte","SESSION_KEY","sessionId","existing","created","uuid","SDK_NAME","SDK_VERSION","DEFAULT_ENDPOINT","EVENTS_PATH","DUPLICATE_WINDOW_MS","BrowserClient","options","DebugIdIndex","_a","_b","_c","_d","endpoint","Transport","SCHEMA_ID","truncate","LIMITS","input","init","message","FloodGuard","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","event","error","filename","context","sourceUrl","failure","user","sanitizeUserId","key","value","route","timeoutMs","remove","flushed","_e","sampleRate","exception","toException","debugIds","cause","session","sessionId","uuid","isIgnoredError","isDeniedUrl","limited","limitEvent","target","type","handler","limitException","scrubUrl","sanitizeIdentifier","sanitizeTags","sanitizeDebugIds","invalidOptions","name","SERVICE_TOKEN_PATTERN","client","init","options","problem","invalidOptions","BrowserClient","captureException","error","context","setUser","user","setRoute","route","setTag","key","value","flush","timeoutMs","close","closing"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vigilon/browser",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Vigilon error monitoring for browser applications",
5
5
  "license": "ISC",
6
6
  "author": "Vigilon",