@vigilon/browser 0.2.11 → 0.2.13-beta.pr-5.7

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
@@ -78,6 +78,10 @@ Some noise is dropped by default: `Script error.` (an opaque error from a cross-
78
78
 
79
79
  `flush()` sends queued events now and `close()` flushes and stops capturing. Both resolve `true` when everything was sent.
80
80
 
81
+ ## Readable stack traces
82
+
83
+ Production scripts are minified, so a raw stack trace points at minified code. [`@vigilon/bundler-plugins`](../bundler-plugins#readme) gives each script your build emits a debug ID that links it to its source map. The SDK sends the debug IDs of the scripts in an error's stack with the error, reading them from the runtime registries that build tools write: `_vigilonDebugIds`, then `_sentryDebugIds` and `_debugIds`. A build that already injects debug IDs for Sentry needs no second pass.
84
+
81
85
  ## Privacy
82
86
 
83
87
  The SDK removes the query string, the fragment, and any `user:password@` from the page URL and from every URL in a stack trace. It drops tags whose key looks like a credential (`password`, `token`, `secret`, `authorization`, `cookie`, and similar), and a `setUser` ID that looks like an email address. The session ID is a random value in `sessionStorage`, one per tab; the SDK sets no cookies. Vigilon applies the same rules again on its side.
package/dist/index.cjs CHANGED
@@ -85,6 +85,67 @@ function scrubUrlsInText(text) {
85
85
  });
86
86
  }
87
87
 
88
+ // src/core/debug-ids.ts
89
+ var REGISTRIES = ["_vigilonDebugIds", "_sentryDebugIds", "_debugIds"];
90
+ var FRAME_URL = /([a-z][a-z0-9+.-]*:\/\/\S+?):\d+:\d+/i;
91
+ var DebugIdIndex = class {
92
+ constructor(scope) {
93
+ this.scope = scope;
94
+ this.indexedEntries = -1;
95
+ this.byUrl = {};
96
+ }
97
+ /**
98
+ * The debug IDs of the registered scripts that appear in these stacks,
99
+ * keyed by scrubbed script URL, or undefined when none do.
100
+ */
101
+ forStacks(stacks) {
102
+ const text = scrubUrlsInText(stacks.filter(Boolean).join("\n"));
103
+ if (text.length === 0) {
104
+ return void 0;
105
+ }
106
+ let found;
107
+ for (const [url, id] of Object.entries(this.registered())) {
108
+ if (text.includes(`${url}:`)) {
109
+ found != null ? found : found = {};
110
+ found[url] = id;
111
+ }
112
+ }
113
+ return found;
114
+ }
115
+ /** Script URL to debug ID, rebuilt only when a registry has grown. */
116
+ registered() {
117
+ const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);
118
+ const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);
119
+ if (entries === this.indexedEntries) {
120
+ return this.byUrl;
121
+ }
122
+ const byUrl = {};
123
+ for (const registry of registries) {
124
+ for (const [stack, id] of Object.entries(registry)) {
125
+ const url = scriptUrl(stack);
126
+ if (url !== null && typeof id === "string" && !(url in byUrl)) {
127
+ byUrl[url] = id;
128
+ }
129
+ }
130
+ }
131
+ this.indexedEntries = entries;
132
+ this.byUrl = byUrl;
133
+ return byUrl;
134
+ }
135
+ };
136
+ function scriptUrl(stack) {
137
+ for (const line of stack.split("\n")) {
138
+ const match = FRAME_URL.exec(line);
139
+ if (match) {
140
+ return scrubUrl(match[1]);
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ function isRegistry(value) {
146
+ return typeof value === "object" && value !== null;
147
+ }
148
+
88
149
  // src/core/exception.ts
89
150
  function toException(error, mechanism, handled) {
90
151
  const parts = toErrorParts(error);
@@ -495,7 +556,7 @@ function sessionId() {
495
556
 
496
557
  // src/browser/client.ts
497
558
  var SDK_NAME = "@vigilon/browser";
498
- var SDK_VERSION = "0.2.11";
559
+ var SDK_VERSION = "0.2.13-beta.pr-5.7";
499
560
  var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
500
561
  var EVENTS_PATH = "/v1/browser/events";
501
562
  var DUPLICATE_WINDOW_MS = 6e4;
@@ -503,6 +564,7 @@ var BrowserClient = class {
503
564
  constructor(options) {
504
565
  this.options = options;
505
566
  this.tags = {};
567
+ this.debugIds = new DebugIdIndex(globalThis);
506
568
  this.userId = null;
507
569
  this.removeListeners = [];
508
570
  var _a, _b, _c, _d;
@@ -577,7 +639,7 @@ var BrowserClient = class {
577
639
  return flushed;
578
640
  }
579
641
  captureUnsafe(error, context, sourceUrl) {
580
- var _a, _b, _c;
642
+ var _a, _b, _c, _d;
581
643
  const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
582
644
  if (sampleRate < 1 && Math.random() >= sampleRate) {
583
645
  return null;
@@ -590,12 +652,17 @@ var BrowserClient = class {
590
652
  if (context.componentStack) {
591
653
  exception.componentStack = context.componentStack;
592
654
  }
655
+ const debugIds = this.debugIds.forStacks([
656
+ exception.stack,
657
+ ...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
658
+ ]);
593
659
  const session = sessionId();
594
660
  let event = {
595
661
  type: "exception",
596
662
  eventId: uuid(),
597
663
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
598
664
  exception,
665
+ ...debugIds ? { debugIds } : {},
599
666
  page: {
600
667
  url: window.location.href,
601
668
  ...context.route ? { route: context.route } : {}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.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 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 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 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 session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\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/** 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"]}
package/dist/index.mjs CHANGED
@@ -53,6 +53,67 @@ function scrubUrlsInText(text) {
53
53
  });
54
54
  }
55
55
 
56
+ // src/core/debug-ids.ts
57
+ var REGISTRIES = ["_vigilonDebugIds", "_sentryDebugIds", "_debugIds"];
58
+ var FRAME_URL = /([a-z][a-z0-9+.-]*:\/\/\S+?):\d+:\d+/i;
59
+ var DebugIdIndex = class {
60
+ constructor(scope) {
61
+ this.scope = scope;
62
+ this.indexedEntries = -1;
63
+ this.byUrl = {};
64
+ }
65
+ /**
66
+ * The debug IDs of the registered scripts that appear in these stacks,
67
+ * keyed by scrubbed script URL, or undefined when none do.
68
+ */
69
+ forStacks(stacks) {
70
+ const text = scrubUrlsInText(stacks.filter(Boolean).join("\n"));
71
+ if (text.length === 0) {
72
+ return void 0;
73
+ }
74
+ let found;
75
+ for (const [url, id] of Object.entries(this.registered())) {
76
+ if (text.includes(`${url}:`)) {
77
+ found != null ? found : found = {};
78
+ found[url] = id;
79
+ }
80
+ }
81
+ return found;
82
+ }
83
+ /** Script URL to debug ID, rebuilt only when a registry has grown. */
84
+ registered() {
85
+ const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);
86
+ const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);
87
+ if (entries === this.indexedEntries) {
88
+ return this.byUrl;
89
+ }
90
+ const byUrl = {};
91
+ for (const registry of registries) {
92
+ for (const [stack, id] of Object.entries(registry)) {
93
+ const url = scriptUrl(stack);
94
+ if (url !== null && typeof id === "string" && !(url in byUrl)) {
95
+ byUrl[url] = id;
96
+ }
97
+ }
98
+ }
99
+ this.indexedEntries = entries;
100
+ this.byUrl = byUrl;
101
+ return byUrl;
102
+ }
103
+ };
104
+ function scriptUrl(stack) {
105
+ for (const line of stack.split("\n")) {
106
+ const match = FRAME_URL.exec(line);
107
+ if (match) {
108
+ return scrubUrl(match[1]);
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ function isRegistry(value) {
114
+ return typeof value === "object" && value !== null;
115
+ }
116
+
56
117
  // src/core/exception.ts
57
118
  function toException(error, mechanism, handled) {
58
119
  const parts = toErrorParts(error);
@@ -463,7 +524,7 @@ function sessionId() {
463
524
 
464
525
  // src/browser/client.ts
465
526
  var SDK_NAME = "@vigilon/browser";
466
- var SDK_VERSION = "0.2.11";
527
+ var SDK_VERSION = "0.2.13-beta.pr-5.7";
467
528
  var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
468
529
  var EVENTS_PATH = "/v1/browser/events";
469
530
  var DUPLICATE_WINDOW_MS = 6e4;
@@ -471,6 +532,7 @@ var BrowserClient = class {
471
532
  constructor(options) {
472
533
  this.options = options;
473
534
  this.tags = {};
535
+ this.debugIds = new DebugIdIndex(globalThis);
474
536
  this.userId = null;
475
537
  this.removeListeners = [];
476
538
  var _a, _b, _c, _d;
@@ -545,7 +607,7 @@ var BrowserClient = class {
545
607
  return flushed;
546
608
  }
547
609
  captureUnsafe(error, context, sourceUrl) {
548
- var _a, _b, _c;
610
+ var _a, _b, _c, _d;
549
611
  const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
550
612
  if (sampleRate < 1 && Math.random() >= sampleRate) {
551
613
  return null;
@@ -558,12 +620,17 @@ var BrowserClient = class {
558
620
  if (context.componentStack) {
559
621
  exception.componentStack = context.componentStack;
560
622
  }
623
+ const debugIds = this.debugIds.forStacks([
624
+ exception.stack,
625
+ ...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
626
+ ]);
561
627
  const session = sessionId();
562
628
  let event = {
563
629
  type: "exception",
564
630
  eventId: uuid(),
565
631
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
566
632
  exception,
633
+ ...debugIds ? { debugIds } : {},
567
634
  page: {
568
635
  url: window.location.href,
569
636
  ...context.route ? { route: context.route } : {}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/limits.ts","../src/core/text.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 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 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 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 session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\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 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,8 +1,10 @@
1
- "use strict";var Vigilon=(()=>{var b=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var X=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var W=(t,e)=>{for(var n in e)b(t,n,{get:e[n],enumerable:!0})},J=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of X(e))!H.call(t,i)&&i!==n&&b(t,i,{get:()=>e[i],enumerable:!(r=Y(e,i))||r.enumerable});return t};var Q=t=>J(b({},"__esModule",{value:!0}),t);var Le={};W(Le,{SDK_VERSION:()=>k,captureException:()=>Se,close:()=>_e,flush:()=>Te,init:()=>xe,setTag:()=>Ie,setUser:()=>ke});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},T=/^[A-Za-z0-9._:/-]+$/,_="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 L(t){let e=t.split(`
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(`
2
2
  `),n=e.length>o.exceptionStackLines?e.slice(0,o.exceptionStackLines).join(`
3
- `):t;return p(n,o.exceptionStackLength)}var Z=/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i,ee=/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi,te=/(:\d+:\d+)?([)\]}>"',;.]*)$/;function g(t){let e=t.replace(Z,"$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 P(t){return t.replace(ee,e=>{var r,i;let n=(i=(r=te.exec(e))==null?void 0:r[0])!=null?i:"";return g(e.slice(0,e.length-n.length))+n})}function A(t,e,n){let i={...B(t),mechanism:e,handled:n},s=ne(t);return s.length>0&&(i.cause=s),i}function U(t){let e={...t,...R(t)};return t.cause&&(e.cause=t.cause.slice(0,o.causeDepth).map(n=>R(n))),e}function R(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?L(P(t.stack)):null}}function B(t){return x(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:re(t),stack:null}}function ne(t){let e=[],n=new Set([t]),r=x(t)?t.cause:void 0;for(;r!=null&&e.length<o.causeDepth&&!n.has(r);)n.add(r),e.push(B(r)),r=x(r)?r.cause:void 0;return e}function x(t){return t instanceof Error||typeof t=="object"&&t!==null&&typeof t.message=="string"&&typeof t.stack=="string"}function re(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 O=[/^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/],M=[/^(chrome|moz|safari|safari-web|ms-browser)-extension:\/\//,/^webkit-masked-url:\/\//],ie=/\b[a-z][a-z0-9+.-]*:\/\/[^\s)]+/i;function m(t,e){return e.some(n=>typeof n=="string"?t.includes(n):n.test(t))}function N(t,e){let{type:n,message:r}=t.exception;return m(r,e)||m(`${n}: ${r}`,e)||n==="AbortError"&&m("AbortError",e)}function D(t,e,n){var i,s;let r=e||((s=ie.exec((i=t.exception.stack)!=null?i:""))==null?void 0:s[0]);return r!==void 0&&m(r,n)}var f=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 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}
4
6
  ${i}
5
7
  ${(s!=null?s:"").split(`
6
8
  `,3).join(`
7
- `)}`,l=this.lastSeen.get(a);return l!==void 0&&n-l<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var se=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],oe=/[^\s@]+@[^\s@]+\.[^\s@]+/;function V(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||se.some(s=>i.includes(s))||(e[r]=p(String(t[r]),o.tagValueLength),n+=1)}return e}function E(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function S(t){let e=E(t);return e!==null&&oe.test(e)?null:e}var ae=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function C(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&&ae.test(String(i))&&(e[s]=String(i),n+=1)}return e}var ue=1e3,$=240*1024,le=60*1024,K=512,q=100,ce=5,pe=1800*1e3,de={429:6e4,503:1e4},ge=6e4,v=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>=q){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:fe(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(ue)}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=le;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=me(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($);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>=ce||s.firstAttemptAt!==null&&n-s.firstAttemptAt>pe;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=K;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===$&&r.push(this.queue.shift()),r}async post(e,n){var l,d;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,ge));return}if(s===429||s===503){this.requeue(e),this.pause((d=he(a,this.options.now()))!=null?d:de[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,q)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function he(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 me(t){return t.reduce((e,n)=>e+n.bytes,K)}function fe(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function y(){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 F="vigilon.session";function z(){try{let t=window.sessionStorage.getItem(F);if(t)return t;let e=y();return window.sessionStorage.setItem(F,e),e}catch{return null}}var Ee="@vigilon/browser",k="0.2.11",ve="https://ingest.vigilon.io",ye="/v1/browser/events",we=6e4,w=class{constructor(e){this.options=e;this.tags={};this.userId=null;this.removeListeners=[];var r,i,s,a;let n=((r=e.endpoint)!=null?r:ve).replace(/\/+$/,"");this.transport=new v({url:`${n}${ye}`,apiKey:e.apiKey,envelope:{schema:_,sdk:{name:Ee,version:k},service:{name:e.serviceName,environment:e.environment,...e.serviceVersion?{version:p(e.serviceVersion,o.serviceVersionLength)}:{}}},fetch:(l,d)=>window.fetch(l,d),now:()=>Date.now(),debug:l=>this.debug(l)}),this.floodGuard=new f((i=e.maxEventsPerPage)!=null?i:100,we),this.ignoreErrors=[...O,...(s=e.ignoreErrors)!=null?s:[]],this.denyUrls=[...M,...(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:S(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 c,h,I;let i=(c=this.options.sampleRate)!=null?c:1;if(i<1&&Math.random()>=i)return null;let s=A(e,(h=n.mechanism)!=null?h:"manual",(I=n.handled)!=null?I:!0);n.componentStack&&(s.componentStack=n.componentStack);let a=z(),l={type:"exception",eventId:y(),timestamp:new Date().toISOString(),exception:s,page:{url:window.location.href,...n.route?{route:n.route}:{}},session:a===null?null:{id:a},user:this.userId===null?null:{id:this.userId},traceId:null,tags:{...this.tags}};if(N(l,this.ignoreErrors)||D(l,r,this.denyUrls))return null;if(this.options.beforeSend){try{l=this.options.beforeSend(l)}catch(G){return this.debug(`beforeSend threw; event dropped: ${String(G)}`),null}if(l===null)return null}let d=be(l);return this.floodGuard.admit(d,Date.now())?(this.transport.enqueue(d),d.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 be(t){var i,s,a;let e={type:"exception",eventId:t.eventId,timestamp:t.timestamp,exception:U(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=E((i=t.session)==null?void 0:i.id);e.session=n===null?null:{id:n};let r=S((s=t.user)==null?void 0:s.id);return e.user=r===null?null:{id:r},e.traceId=E(t.traceId),e.tags=V((a=t.tags)!=null?a:{}),t.debugIds&&(e.debugIds=C(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||!T.test(n))return`${e} must be 1 to 100 characters of letters, digits, and . _ : / -`;return null}var u=null;function xe(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 w(t),u.install(),!0)}function Se(t,e){return u===null?null:u.capture(t,e)}function ke(t){u==null||u.setUser(t)}function Ie(t,e){u==null||u.setTag(t,e)}function Te(t){return u===null?Promise.resolve(!0):u.flush(t)}async function _e(t){if(u===null)return!0;let e=u;return u=null,e.close(t)}return Q(Le);})();
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.13-beta.pr-5.7",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);})();
8
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/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 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 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 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 session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\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","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","_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","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/** 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":"8bAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,iBAAAE,EAAA,qBAAAC,GAAA,UAAAC,GAAA,UAAAC,GAAA,SAAAC,GAAA,WAAAC,GAAA,YAAAC,KCKO,IAAMC,EAAS,CACpB,iBAAkB,GAClB,kBAAmB,IACnB,qBAAsB,IACtB,oBAAqB,IACrB,uBAAwB,KACxB,qBAAsB,MACtB,oBAAqB,IACrB,qBAAsB,MACtB,WAAY,EACZ,eAAgB,IAChB,iBAAkB,KAClB,cAAe,KACf,gBAAiB,IACjB,iBAAkB,IAClB,WAAY,GACZ,aAAc,GACd,eAAgB,GAClB,EAGaC,EAAwB,sBAExBC,EAAY,0BCzBlB,SAASC,EAASC,EAAeC,EAA2B,CACjE,GAAID,EAAM,QAAUC,EAClB,OAAOD,EAET,IAAME,EAAOF,EAAM,WAAWC,EAAY,CAAC,EACrCE,EAAMD,GAAQ,OAAUA,GAAQ,MAASD,EAAY,EAAIA,EAC/D,OAAOD,EAAM,MAAM,EAAGG,CAAG,CAC3B,CAGO,SAASC,EAAcC,EAAuB,CACnD,IAAMC,EAAQD,EAAM,MAAM;AAAA,CAAI,EACxBE,EACJD,EAAM,OAASE,EAAO,oBAClBF,EAAM,MAAM,EAAGE,EAAO,mBAAmB,EAAE,KAAK;AAAA,CAAI,EACpDH,EACN,OAAON,EAASQ,EAAMC,EAAO,oBAAoB,CACnD,CAOA,IAAMC,GAAW,wCAEXC,GAAc,gCAEdC,GAAU,8BAGT,SAASC,EAASZ,EAAuB,CAC9C,IAAMa,EAAMb,EAAM,QAAQS,GAAU,IAAI,EAClCK,EAAQD,EAAI,QAAQ,GAAG,EACvBE,EAAWF,EAAI,QAAQ,GAAG,EAC1BG,EACJF,IAAU,GAAKC,EAAWA,IAAa,GAAKD,EAAQ,KAAK,IAAIA,EAAOC,CAAQ,EAC9E,OAAOC,IAAQ,GAAKH,EAAMA,EAAI,MAAM,EAAGG,CAAG,CAC5C,CAGO,SAASC,EAAgBC,EAAsB,CACpD,OAAOA,EAAK,QAAQR,GAAcS,GAAU,CA7C9C,IAAAC,EAAAC,EA8CI,IAAMC,GAAUD,GAAAD,EAAAT,GAAQ,KAAKQ,CAAK,IAAlB,YAAAC,EAAsB,KAAtB,KAAAC,EAA4B,GAC5C,OAAOT,EAASO,EAAM,MAAM,EAAGA,EAAM,OAASG,EAAQ,MAAM,CAAC,EAAIA,CACnE,CAAC,CACH,CCvCA,IAAMC,GAAa,CAAC,mBAAoB,kBAAmB,WAAW,EAKhEC,GAAY,wCAILC,EAAN,KAAmB,CAIxB,YAA6BC,EAAgC,CAAhC,WAAAA,EAH7B,KAAQ,eAAiB,GACzB,KAAQ,MAAgC,CAAC,CAEqB,CAM9D,UAAUC,EAA8E,CACtF,IAAMC,EAAOC,EAAgBF,EAAO,OAAO,OAAO,EAAE,KAAK;AAAA,CAAI,CAAC,EAC9D,GAAIC,EAAK,SAAW,EAClB,OAEF,IAAIE,EACJ,OAAW,CAACC,EAAKC,CAAE,IAAK,OAAO,QAAQ,KAAK,WAAW,CAAC,EAElDJ,EAAK,SAAS,GAAGG,CAAG,GAAG,IACzBD,GAAA,OAAAA,EAAU,CAAC,GACXA,EAAMC,CAAG,EAAIC,GAGjB,OAAOF,CACT,CAGQ,YAAqC,CAC3C,IAAMG,EAAaV,GAAW,IAAKW,GAAS,KAAK,MAAMA,CAAI,CAAC,EAAE,OAAOC,EAAU,EACzEC,EAAUH,EAAW,OAAO,CAACI,EAAOC,IAAaD,EAAQ,OAAO,KAAKC,CAAQ,EAAE,OAAQ,CAAC,EAC9F,GAAIF,IAAY,KAAK,eACnB,OAAO,KAAK,MAEd,IAAMG,EAAgC,CAAC,EACvC,QAAWD,KAAYL,EACrB,OAAW,CAACO,EAAOR,CAAE,IAAK,OAAO,QAAQM,CAAQ,EAAG,CAClD,IAAMP,EAAMU,GAAUD,CAAK,EACvBT,IAAQ,MAAQ,OAAOC,GAAO,UAAY,EAAED,KAAOQ,KACrDA,EAAMR,CAAG,EAAIC,EAEjB,CAEF,YAAK,eAAiBI,EACtB,KAAK,MAAQG,EACNA,CACT,CACF,EAEA,SAASE,GAAUD,EAA8B,CAC/C,QAAWE,KAAQF,EAAM,MAAM;AAAA,CAAI,EAAG,CACpC,IAAMG,EAAQnB,GAAU,KAAKkB,CAAI,EACjC,GAAIC,EACF,OAAOC,EAASD,EAAM,CAAC,CAAC,CAE5B,CACA,OAAO,IACT,CAEA,SAASR,GAAWU,EAAmC,CACrD,OAAO,OAAOA,GAAU,UAAYA,IAAU,IAChD,CChEO,SAASC,EACdC,EACAC,EACAC,EACkB,CAElB,IAAMC,EAA8B,CAAE,GADxBC,EAAaJ,CAAK,EACgB,UAAAC,EAAW,QAAAC,CAAQ,EAC7DG,EAAQC,GAAaN,CAAK,EAChC,OAAIK,EAAM,OAAS,IACjBF,EAAU,MAAQE,GAEbF,CACT,CAGO,SAASI,EAAeJ,EAA+C,CAC5E,IAAMK,EAA4B,CAChC,GAAGL,EACH,GAAGM,EAAWN,CAAS,CACzB,EACA,OAAIA,EAAU,QACZK,EAAQ,MAAQL,EAAU,MACvB,MAAM,EAAGO,EAAO,UAAU,EAC1B,IAAKL,GAAUI,EAAWJ,CAAK,CAAmB,GAEhDG,CACT,CAEA,SAASC,EAAWE,EAIL,CACb,MAAO,CACL,KAAMC,EAAS,OAAOD,EAAM,IAAI,EAAGD,EAAO,mBAAmB,EAC7D,QAASE,EAAS,OAAOD,EAAM,OAAO,EAAGD,EAAO,sBAAsB,EACtE,MACE,OAAOC,EAAM,OAAU,UAAYA,EAAM,MAAM,OAAS,EACpDE,EAAcC,EAAgBH,EAAM,KAAK,CAAC,EAC1C,IACR,CACF,CAEA,SAASP,EAAaJ,EAA4B,CAChD,OAAIe,EAAYf,CAAK,EACZ,CACL,KAAM,OAAOA,EAAM,MAAS,UAAYA,EAAM,KAAOA,EAAM,KAAO,QAClE,QAAS,OAAOA,EAAM,SAAY,SAAWA,EAAM,QAAU,GAC7D,MAAO,OAAOA,EAAM,OAAU,SAAWA,EAAM,MAAQ,IACzD,EAEK,CAAE,KAAM,QAAS,QAASgB,GAAYhB,CAAK,EAAG,MAAO,IAAK,CACnE,CAEA,SAASM,GAAaN,EAAkC,CACtD,IAAMiB,EAA0B,CAAC,EAC3BC,EAAO,IAAI,IAAa,CAAClB,CAAK,CAAC,EACjCK,EAAQU,EAAYf,CAAK,EAAIA,EAAM,MAAQ,OAC/C,KAA8BK,GAAU,MAAQY,EAAM,OAASP,EAAO,YAChE,CAAAQ,EAAK,IAAIb,CAAK,GAGlBa,EAAK,IAAIb,CAAK,EACdY,EAAM,KAAKb,EAAaC,CAAK,CAAC,EAC9BA,EAAQU,EAAYV,CAAK,EAAIA,EAAM,MAAQ,OAE7C,OAAOY,CACT,CASA,SAASF,EAAYI,EAAoC,CACvD,OACEA,aAAiB,OAChB,OAAOA,GAAU,UAChBA,IAAU,MACV,OAAQA,EAAoB,SAAY,UACxC,OAAQA,EAAoB,OAAU,QAE5C,CAEA,SAASH,GAAYG,EAAwB,CAC3C,GAAI,OAAOA,GAAU,SACnB,OAAOA,EAET,GAAI,CACF,IAAMC,EAAO,KAAK,UAAUD,CAAK,EACjC,OAAOC,IAAS,OAAY,OAAOD,CAAK,EAAIC,CAC9C,MAAQ,CACN,OAAO,OAAOD,CAAK,CACrB,CACF,CCtGO,IAAME,EAAmC,CAC9C,oBACA,kCACA,oEACA,8BACA,gBACA,yCACA,4BACA,sBACF,EAGaC,EAA+B,CAC1C,4DACA,yBACF,EAEMC,GAAY,mCAEX,SAASC,EAAWC,EAAeC,EAA8B,CACtE,OAAOA,EAAS,KAAMC,GACpB,OAAOA,GAAY,SAAWF,EAAM,SAASE,CAAO,EAAIA,EAAQ,KAAKF,CAAK,CAC5E,CACF,CAEO,SAASG,EAAeC,EAAqBH,EAA8B,CAChF,GAAM,CAAE,KAAAI,EAAM,QAAAC,CAAQ,EAAIF,EAAM,UAChC,OACEL,EAAWO,EAASL,CAAQ,GAC5BF,EAAW,GAAGM,CAAI,KAAKC,CAAO,GAAIL,CAAQ,GACzCI,IAAS,cAAgBN,EAAW,aAAcE,CAAQ,CAE/D,CAMO,SAASM,EACdH,EACAI,EACAP,EACS,CAnDX,IAAAQ,EAAAC,EAoDE,IAAMC,EAAMH,KAAaE,EAAAZ,GAAU,MAAKW,EAAAL,EAAM,UAAU,QAAhB,KAAAK,EAAyB,EAAE,IAA1C,YAAAC,EAA8C,IACvE,OAAOC,IAAQ,QAAaZ,EAAWY,EAAKV,CAAQ,CACtD,CChDO,IAAMW,EAAN,KAAiB,CAItB,YACmBC,EACAC,EACjB,CAFiB,sBAAAD,EACA,uBAAAC,EALnB,KAAiB,SAAW,IAAI,IAChC,KAAQ,SAAW,CAKhB,CAEH,MAAMC,EAAqBC,EAAsB,CAC/C,GAAI,KAAK,UAAY,KAAK,iBACxB,MAAO,GAET,GAAM,CAAE,KAAAC,EAAM,QAAAC,EAAS,MAAAC,CAAM,EAAIJ,EAAM,UACjCK,EAAM,GAAGH,CAAI;AAAA,EAAKC,CAAO;AAAA,GAAMC,GAAA,KAAAA,EAAS,IAAI,MAAM;AAAA,EAAM,CAAC,EAAE,KAAK;AAAA,CAAI,CAAC,GACrEE,EAAW,KAAK,SAAS,IAAID,CAAG,EACtC,OAAIC,IAAa,QAAaL,EAAMK,EAAW,KAAK,kBAC3C,IAET,KAAK,SAAS,IAAID,EAAKJ,CAAG,EAC1B,KAAK,UAAY,EACV,GACT,CACF,ECxBA,IAAMM,GAA2B,CAC/B,WACA,SACA,SACA,QACA,gBACA,SACA,SACA,UACA,UACA,aACA,aACF,EAEMC,GAAa,2BAGZ,SAASC,EACdC,EACwB,CACxB,IAAMC,EAAiC,CAAC,EACpCC,EAAQ,EACZ,QAAWC,KAAO,OAAO,KAAKH,CAAI,EAAG,CACnC,GAAIE,IAAUE,EAAO,WACnB,MAEF,IAAMC,EAAUF,EAAI,YAAY,EAE9BA,EAAI,SAAW,GACfA,EAAI,OAASC,EAAO,cACpBP,GAAyB,KAAMS,GAAaD,EAAQ,SAASC,CAAQ,CAAC,IAIxEL,EAAOE,CAAG,EAAII,EAAS,OAAOP,EAAKG,CAAG,CAAC,EAAGC,EAAO,cAAc,EAC/DF,GAAS,EACX,CACA,OAAOD,CACT,CAMO,SAASO,EAAmBC,EAA+B,CAChE,OAAO,OAAOA,GAAU,UACtBA,EAAM,OAAS,GACfA,EAAM,QAAUL,EAAO,iBACrBK,EACA,IACN,CAGO,SAASC,EAAeD,EAA+B,CAC5D,IAAME,EAAKH,EAAmBC,CAAK,EACnC,OAAOE,IAAO,MAAQb,GAAW,KAAKa,CAAE,EAAI,KAAOA,CACrD,CAEA,IAAMC,GAAO,kEAGN,SAASC,EACdC,EACwB,CACxB,IAAMb,EAAiC,CAAC,EACpCC,EAAQ,EACZ,OAAW,CAACa,EAAKJ,CAAE,IAAK,OAAO,QAAQG,CAAQ,EAAG,CAChD,IAAMX,EAAMa,EAASD,CAAG,EACxB,GAAIb,IAAUE,EAAO,eACnB,MAEED,EAAI,OAAS,GAAKA,EAAI,QAAUC,EAAO,kBAAoBQ,GAAK,KAAK,OAAOD,CAAE,CAAC,IACjFV,EAAOE,CAAG,EAAI,OAAOQ,CAAE,EACvBT,GAAS,EAEb,CACA,OAAOD,CACT,CCjDA,IAAMgB,GAAiB,IAEjBC,EAAiB,IAAM,KAEvBC,GAAyB,GAAK,KAC9BC,EAA0B,IAC1BC,EAAY,IACZC,GAAe,EAEfC,GAAmB,KAAU,IAC7BC,GAAyB,CAAE,IAAK,IAAQ,IAAK,GAAO,EACpDC,GAAiB,IAQVC,EAAN,KAAgB,CAOrB,YAA6BC,EAA2B,CAA3B,aAAAA,EAN7B,KAAQ,MAAmB,CAAC,EAC5B,KAAQ,YAAc,EACtB,KAAQ,MAA8C,KACtD,KAAQ,KAA6B,KACrC,KAAiB,SAAW,IAAI,GAEyB,CAEzD,QAAQC,EAA2B,CACjC,GAAI,KAAK,MAAM,QAAUP,EAAW,CAClC,KAAK,QAAQ,MAAM,2BAA2B,EAC9C,MACF,CACA,KAAK,MAAM,KAAK,CACd,MAAAO,EACA,MAAOC,GAAW,KAAK,UAAUD,CAAK,CAAC,EACvC,SAAU,EACV,eAAgB,IAClB,CAAC,EACD,KAAK,SAASX,EAAc,CAC9B,CAGA,MAAM,MAAMa,EAAY,IAAwB,CAC9C,KAAK,YAAY,EACjB,IAAIC,EACJ,aAAM,QAAQ,KAAK,CACjB,KAAK,MAAM,EAAE,KAAK,IAAM,QAAQ,IAAI,KAAK,QAAQ,CAAC,EAClD,IAAI,QAAeC,GAAY,CAC7BD,EAAU,WAAWC,EAASF,CAAS,CACzC,CAAC,CACH,CAAC,EACD,aAAaC,CAAO,EACb,KAAK,MAAM,SAAW,GAAK,KAAK,SAAS,OAAS,CAC3D,CAMA,eAAsB,CAEpB,GADA,KAAK,YAAY,EACb,KAAK,QAAQ,IAAI,EAAI,KAAK,YAC5B,OAEF,IAAIE,EAASd,GACb,KAAO,KAAK,MAAM,OAAS,GAAG,CAC5B,IAAMe,EAAQ,KAAK,UAAUD,CAAM,EACnC,GAAIC,EAAM,SAAW,EACnB,MAEFD,GAAUE,GAAWD,CAAK,EAC1B,KAAK,MAAM,KAAK,KAAKA,EAAO,EAAI,CAAC,CACnC,CACF,CAEA,OAAc,CACZ,KAAK,YAAY,EACjB,KAAK,MAAQ,CAAC,CAChB,CAEQ,SAASE,EAAuB,CACtC,GAAI,KAAK,QAAU,MAAQ,KAAK,MAAM,SAAW,EAC/C,OAEF,IAAMC,EAAO,KAAK,IAAID,EAAS,KAAK,YAAc,KAAK,QAAQ,IAAI,CAAC,EACpE,KAAK,MAAQ,WAAW,IAAM,CAC5B,KAAK,MAAQ,KACR,KAAK,MAAM,CAClB,EAAGC,CAAI,CACT,CAEQ,aAAoB,CACtB,KAAK,QAAU,OACjB,aAAa,KAAK,KAAK,EACvB,KAAK,MAAQ,KAEjB,CAEQ,OAAuB,CAC7B,OAAI,KAAK,OAAS,OAChB,KAAK,KAAO,KAAK,IAAI,EAAE,QAAQ,IAAM,CACnC,KAAK,KAAO,IACd,CAAC,GAEI,KAAK,IACd,CAEA,MAAc,KAAqB,CACjC,KAAO,KAAK,MAAM,OAAS,GAAK,KAAK,QAAQ,IAAI,GAAK,KAAK,aAAa,CACtE,IAAMH,EAAQ,KAAK,UAAUhB,CAAc,EAC3C,GAAIgB,EAAM,SAAW,EACnB,MAEF,MAAM,KAAK,MAAM,KAAK,KAAKA,EAAO,EAAK,CAAC,CAC1C,CACA,KAAK,SAAS,CAAC,CACjB,CAEQ,MAAMI,EAAuC,CACnD,YAAK,SAAS,IAAIA,CAAO,EAClBA,EAAQ,QAAQ,IAAM,KAAK,SAAS,OAAOA,CAAO,CAAC,CAC5D,CAGQ,UAAUC,EAA6B,CAC7C,IAAMC,EAAM,KAAK,QAAQ,IAAI,EAC7B,KAAK,MAAQ,KAAK,MAAM,OAAQC,GAAY,CAC1C,IAAMC,EACJD,EAAQ,UAAYnB,IACnBmB,EAAQ,iBAAmB,MAC1BD,EAAMC,EAAQ,eAAiBlB,GACnC,OAAImB,GACF,KAAK,QAAQ,MAAM,SAASD,EAAQ,MAAM,OAAO,wBAAwB,EAEpE,CAACC,CACV,CAAC,EACD,IAAMR,EAAmB,CAAC,EACtBS,EAAQvB,EACZ,KACE,KAAK,MAAM,OAAS,GACpBc,EAAM,OAASU,EAAO,kBACtBD,EAAQ,KAAK,MAAM,CAAC,EAAE,OAASJ,GAC/B,CACA,IAAME,EAAU,KAAK,MAAM,MAAM,EACjCE,GAASF,EAAQ,MACjBP,EAAM,KAAKO,CAAO,CACpB,CAGA,OAAIP,EAAM,SAAW,GAAK,KAAK,MAAM,OAAS,GAAKK,IAAarB,GAC9DgB,EAAM,KAAK,KAAK,MAAM,MAAM,CAAY,EAEnCA,CACT,CAEA,MAAc,KAAKA,EAAkBW,EAAmC,CA7L1E,IAAAC,EAAAC,EA8LI,IAAMP,EAAM,KAAK,QAAQ,IAAI,EAC7B,QAAWC,KAAWP,EACpBO,EAAQ,UAAY,GACpBK,EAAAL,EAAQ,iBAAR,OAAAA,EAAQ,eAAmBD,GAE7B,IAAMQ,EAAiC,CACrC,GAAG,KAAK,QAAQ,SAChB,OAAQ,IAAI,KAAKR,CAAG,EAAE,YAAY,EAClC,OAAQN,EAAM,IAAKO,GAAYA,EAAQ,KAAK,CAC9C,EACIQ,EACAC,EAA4B,KAChC,GAAI,CACF,IAAMC,EAAW,MAAM,KAAK,QAAQ,MAAM,KAAK,QAAQ,IAAK,CAC1D,OAAQ,OACR,QAAS,CACP,cAAe,UAAU,KAAK,QAAQ,MAAM,GAC5C,eAAgB,kBAClB,EACA,KAAM,KAAK,UAAUH,CAAQ,EAC7B,UAAAH,CACF,CAAC,EACDI,EAASE,EAAS,OAClBD,EAAaC,EAAS,QAAQ,IAAI,aAAa,CACjD,MAAQ,CAEN,KAAK,QAAQjB,CAAK,EAClB,IAAMkB,EAAW,KAAK,IAAI,GAAGlB,EAAM,IAAKO,GAAYA,EAAQ,QAAQ,CAAC,EACrE,KAAK,MAAM,KAAK,IAAI,IAAO,GAAKW,EAAU3B,EAAc,CAAC,EACzD,MACF,CACA,GAAIwB,IAAW,KAAOA,IAAW,IAAK,CACpC,KAAK,QAAQf,CAAK,EAClB,KAAK,OAAMa,EAAAM,GAAgBH,EAAY,KAAK,QAAQ,IAAI,CAAC,IAA9C,KAAAH,EAAmDvB,GAAuByB,CAAM,CAAC,EAC5F,MACF,EACIA,EAAS,KAAOA,GAAU,MAC5B,KAAK,QAAQ,MAAM,yBAAyBA,CAAM,KAAKf,EAAM,MAAM,iBAAiB,CAExF,CAEQ,QAAQA,EAAwB,CACtC,KAAK,MAAM,QAAQ,GAAGA,CAAK,EAC3B,KAAK,MAAM,OAAS,KAAK,IAAI,KAAK,MAAM,OAAQb,CAAS,CAC3D,CAEQ,MAAMiC,EAAkB,CAC9B,KAAK,YAAc,KAAK,IAAI,KAAK,YAAa,KAAK,QAAQ,IAAI,EAAIA,CAAE,EACrE,KAAK,YAAY,EACjB,KAAK,SAAS,CAAC,CACjB,CACF,EAGO,SAASD,GAAgBE,EAAsBf,EAA4B,CAChF,GAAIe,IAAU,MAAQA,EAAM,KAAK,IAAM,GACrC,OAAO,KAET,IAAMC,EAAU,OAAOD,CAAK,EAC5B,GAAI,OAAO,SAASC,CAAO,EACzB,OAAO,KAAK,IAAI,EAAGA,EAAU,GAAI,EAEnC,IAAMC,EAAO,KAAK,MAAMF,CAAK,EAC7B,OAAO,OAAO,MAAME,CAAI,EAAI,KAAO,KAAK,IAAI,EAAGA,EAAOjB,CAAG,CAC3D,CAEA,SAASL,GAAWD,EAA0B,CAC5C,OAAOA,EAAM,OAAO,CAACwB,EAAOjB,IAAYiB,EAAQjB,EAAQ,MAAOrB,CAAuB,CACxF,CAEA,SAASS,GAAW0B,EAAuB,CACzC,OAAO,OAAO,aAAgB,YAC1BA,EAAM,OAAS,EACf,IAAI,YAAY,EAAE,OAAOA,CAAK,EAAE,MACtC,CCvQO,SAASI,GAAe,CAC7B,IAAMC,EAAQ,IAAI,WAAW,EAAE,EACzBC,EAAU,WAAmC,OACnD,GAAIA,GAAA,MAAAA,EAAQ,gBACVA,EAAO,gBAAgBD,CAAK,MAE5B,SAASE,EAAI,EAAGA,EAAI,GAAIA,GAAK,EAC3BF,EAAME,CAAC,EAAI,KAAK,MAAM,KAAK,OAAO,EAAI,GAAG,EAG7CF,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,GAC/BA,EAAM,CAAC,EAAKA,EAAM,CAAC,EAAI,GAAQ,IAC/B,IAAMG,EAAM,MAAM,KAAKH,EAAQI,GAASA,EAAK,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAC1E,MAAO,GAAGD,EAAI,MAAM,EAAG,CAAC,EAAE,KAAK,EAAE,CAAC,IAAIA,EAAI,MAAM,EAAG,CAAC,EAAE,KAAK,EAAE,CAAC,IAAIA,EAC/D,MAAM,EAAG,CAAC,EACV,KAAK,EAAE,CAAC,IAAIA,EAAI,MAAM,EAAG,EAAE,EAAE,KAAK,EAAE,CAAC,IAAIA,EAAI,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EACpE,CCfA,IAAME,EAAc,kBAOb,SAASC,GAA2B,CACzC,GAAI,CACF,IAAMC,EAAW,OAAO,eAAe,QAAQF,CAAW,EAC1D,GAAIE,EACF,OAAOA,EAET,IAAMC,EAAUC,EAAK,EACrB,cAAO,eAAe,QAAQJ,EAAaG,CAAO,EAC3CA,CACT,MAAQ,CACN,OAAO,IACT,CACF,CCIO,IAAME,GAAW,mBACXC,EAAc,qBACrBC,GAAmB,4BACnBC,GAAc,qBAsCdC,GAAsB,IAEfC,EAAN,KAAoB,CAUzB,YAA6BC,EAAgC,CAAhC,aAAAA,EAL7B,KAAiB,KAA+B,CAAC,EACjD,KAAiB,SAAW,IAAIC,EAAa,UAAgD,EAC7F,KAAQ,OAAwB,KAChC,KAAiB,gBAAqC,CAAC,EA5EzD,IAAAC,EAAAC,EAAAC,EAAAC,EA+EI,IAAMC,IAAYJ,EAAAF,EAAQ,WAAR,KAAAE,EAAoBN,IAAkB,QAAQ,OAAQ,EAAE,EAC1E,KAAK,UAAY,IAAIW,EAAU,CAC7B,IAAK,GAAGD,CAAQ,GAAGT,EAAW,GAC9B,OAAQG,EAAQ,OAChB,SAAU,CACR,OAAQQ,EACR,IAAK,CAAE,KAAMd,GAAU,QAASC,CAAY,EAC5C,QAAS,CACP,KAAMK,EAAQ,YACd,YAAaA,EAAQ,YACrB,GAAIA,EAAQ,eACR,CAAE,QAASS,EAAST,EAAQ,eAAgBU,EAAO,oBAAoB,CAAE,EACzE,CAAC,CACP,CACF,EACA,MAAO,CAACC,EAAOC,IAAS,OAAO,MAAMD,EAAOC,CAAI,EAChD,IAAK,IAAM,KAAK,IAAI,EACpB,MAAQC,GAAY,KAAK,MAAMA,CAAO,CACxC,CAAC,EACD,KAAK,WAAa,IAAIC,GACpBX,EAAAH,EAAQ,mBAAR,KAAAG,EAA4B,IAC5BL,EACF,EACA,KAAK,aAAe,CAAC,GAAGiB,EAAuB,IAAIX,EAAAJ,EAAQ,eAAR,KAAAI,EAAwB,CAAC,CAAE,EAC9E,KAAK,SAAW,CAAC,GAAGY,EAAmB,IAAIX,EAAAL,EAAQ,WAAR,KAAAK,EAAoB,CAAC,CAAE,CACpE,CAEA,SAAgB,CACd,KAAK,OAAO,OAAQ,QAAUY,GAAU,CACtC,GAAM,CAAE,MAAAC,EAAO,QAAAL,EAAS,SAAAM,CAAS,EAAIF,EAGrC,KAAK,QAAQC,GAAA,KAAAA,EAASL,EAAS,CAAE,UAAW,UAAW,QAAS,EAAM,EAAGM,CAAQ,CACnF,CAAC,EACD,KAAK,OAAO,OAAQ,qBAAuBF,GAAU,CACnD,KAAK,QAASA,EAAgC,OAAQ,CACpD,UAAW,qBACX,QAAS,EACX,CAAC,CACH,CAAC,EAID,KAAK,OAAO,SAAU,mBAAoB,IAAM,CAC1C,SAAS,kBAAoB,UAC/B,KAAK,UAAU,cAAc,CAEjC,CAAC,EACD,KAAK,OAAO,OAAQ,WAAY,IAAM,KAAK,UAAU,cAAc,CAAC,CACtE,CAEA,QAAQC,EAAgBE,EAA0B,CAAC,EAAGC,EAAmC,CACvF,GAAI,CACF,OAAO,KAAK,cAAcH,EAAOE,EAASC,CAAS,CACrD,OAASC,EAAS,CAEhB,YAAK,MAAM,mBAAmB,OAAOA,CAAO,CAAC,EAAE,EACxC,IACT,CACF,CAEA,QAAQC,EAAmC,CACzC,KAAK,OAASA,IAAS,KAAO,KAAOC,EAAeD,EAAK,EAAE,EACvDA,IAAS,MAAQ,KAAK,SAAW,MACnC,KAAK,MAAM,0FAA0F,CAEzG,CAEA,OAAOE,EAAaC,EAAqB,CACvC,KAAK,KAAKD,CAAG,EAAI,OAAOC,CAAK,CAC/B,CAEA,MAAMC,EAAsC,CAC1C,OAAO,KAAK,UAAU,MAAMA,CAAS,CACvC,CAEA,MAAM,MAAMA,EAAsC,CAChD,QAAWC,KAAU,KAAK,gBAAgB,OAAO,CAAC,EAChDA,EAAO,EAET,IAAMC,EAAU,MAAM,KAAK,UAAU,MAAMF,CAAS,EACpD,YAAK,UAAU,MAAM,EACdE,CACT,CAEQ,cACNX,EACAE,EACAC,EACe,CAxKnB,IAAAnB,EAAAC,EAAAC,EAAAC,EAyKI,IAAMyB,GAAa5B,EAAA,KAAK,QAAQ,aAAb,KAAAA,EAA2B,EAC9C,GAAI4B,EAAa,GAAK,KAAK,OAAO,GAAKA,EACrC,OAAO,KAET,IAAMC,EAAYC,EAChBd,GACAf,EAAAiB,EAAQ,YAAR,KAAAjB,EAAqB,UACrBC,EAAAgB,EAAQ,UAAR,KAAAhB,EAAmB,EACrB,EACIgB,EAAQ,iBACVW,EAAU,eAAiBX,EAAQ,gBAErC,IAAMa,EAAW,KAAK,SAAS,UAAU,CACvCF,EAAU,MACV,KAAI1B,EAAA0B,EAAU,QAAV,KAAA1B,EAAmB,CAAC,GAAG,IAAK6B,GAAUA,EAAM,KAAK,CACvD,CAAC,EACKC,EAAUC,EAAU,EACtBnB,EAA6B,CAC/B,KAAM,YACN,QAASoB,EAAK,EACd,UAAW,IAAI,KAAK,EAAE,YAAY,EAClC,UAAAN,EACA,GAAIE,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,EAC/B,KAAM,CACJ,IAAK,OAAO,SAAS,KACrB,GAAIb,EAAQ,MAAQ,CAAE,MAAOA,EAAQ,KAAM,EAAI,CAAC,CAClD,EACA,QAASe,IAAY,KAAO,KAAO,CAAE,GAAIA,CAAQ,EACjD,KAAM,KAAK,SAAW,KAAO,KAAO,CAAE,GAAI,KAAK,MAAO,EACtD,QAAS,KACT,KAAM,CAAE,GAAG,KAAK,IAAK,CACvB,EACA,GACEG,EAAerB,EAAO,KAAK,YAAY,GACvCsB,EAAYtB,EAAOI,EAAW,KAAK,QAAQ,EAE3C,OAAO,KAET,GAAI,KAAK,QAAQ,WAAY,CAC3B,GAAI,CACFJ,EAAQ,KAAK,QAAQ,WAAWA,CAAK,CACvC,OAASK,EAAS,CAGhB,YAAK,MAAM,oCAAoC,OAAOA,CAAO,CAAC,EAAE,EACzD,IACT,CACA,GAAIL,IAAU,KACZ,OAAO,IAEX,CACA,IAAMuB,EAAUC,GAAWxB,CAAK,EAChC,OAAK,KAAK,WAAW,MAAMuB,EAAS,KAAK,IAAI,CAAC,GAG9C,KAAK,UAAU,QAAQA,CAAO,EACvBA,EAAQ,SAHN,IAIX,CAEQ,OAAOE,EAAqBC,EAAcC,EAAuC,CACvFF,EAAO,iBAAiBC,EAAMC,CAAO,EACrC,KAAK,gBAAgB,KAAK,IAAMF,EAAO,oBAAoBC,EAAMC,CAAO,CAAC,CAC3E,CAEQ,MAAM/B,EAAuB,CAC/B,KAAK,QAAQ,OACf,QAAQ,KAAK,aAAaA,CAAO,EAAE,CAEvC,CACF,EAMO,SAAS4B,GAAWxB,EAAmC,CApP9D,IAAAf,EAAAC,EAAAC,EAqPE,IAAMoC,EAAwB,CAC5B,KAAM,YACN,QAASvB,EAAM,QACf,UAAWA,EAAM,UACjB,UAAW4B,EAAe5B,EAAM,SAAS,EACzC,KAAM,CAAE,IAAKR,EAASqC,EAAS,OAAO7B,EAAM,KAAK,GAAG,CAAC,EAAGP,EAAO,aAAa,CAAE,CAChF,EACIO,EAAM,UAAU,iBAClBuB,EAAQ,UAAU,eAAiB/B,EACjCQ,EAAM,UAAU,eAChBP,EAAO,oBACT,GAEEO,EAAM,KAAK,QACbuB,EAAQ,KAAK,MAAQ/B,EAASQ,EAAM,KAAK,MAAOP,EAAO,eAAe,GAExE,IAAMyB,EAAUY,GAAmB7C,EAAAe,EAAM,UAAN,YAAAf,EAAe,EAAE,EACpDsC,EAAQ,QAAUL,IAAY,KAAO,KAAO,CAAE,GAAIA,CAAQ,EAC1D,IAAMZ,EAAOC,GAAerB,EAAAc,EAAM,OAAN,YAAAd,EAAY,EAAE,EAC1C,OAAAqC,EAAQ,KAAOjB,IAAS,KAAO,KAAO,CAAE,GAAIA,CAAK,EACjDiB,EAAQ,QAAUO,EAAmB9B,EAAM,OAAO,EAClDuB,EAAQ,KAAOQ,GAAa5C,EAAAa,EAAM,OAAN,KAAAb,EAAc,CAAC,CAAC,EACxCa,EAAM,WACRuB,EAAQ,SAAWS,EAAiBhC,EAAM,QAAQ,GAE7CuB,CACT,CAGO,SAASU,EAAelD,EAA+C,CAC5E,GAAI,CAACA,GAAW,OAAOA,EAAQ,QAAW,UAAYA,EAAQ,OAAO,SAAW,EAC9E,MAAO,qBAET,OAAW,CAACmD,EAAMzB,CAAK,GAAK,CAC1B,CAAC,cAAe1B,EAAQ,WAAW,EACnC,CAAC,cAAeA,EAAQ,WAAW,CACrC,EACE,GACE,OAAO0B,GAAU,UACjBA,EAAM,SAAW,GACjBA,EAAM,OAAShB,EAAO,mBACtB,CAAC0C,EAAsB,KAAK1B,CAAK,EAEjC,MAAO,GAAGyB,CAAI,iEAGlB,OAAO,IACT,CX7QA,IAAIE,EAA+B,KAO5B,SAASC,GAAKC,EAAyC,CAC5D,GAAI,OAAO,QAAW,aAAe,OAAO,UAAa,YACvD,MAAO,GAET,IAAMC,EAAUC,EAAeF,CAAO,EACtC,OAAIC,IAAY,MACd,QAAQ,MAAM,0BAA0BA,CAAO,EAAE,EAC1C,KAEJD,EAAQ,gBACX,QAAQ,KACN,6EACF,EAEEF,IAAW,MACRA,EAAO,MAAM,CAAC,EAErBA,EAAS,IAAIK,EAAcH,CAAO,EAClCF,EAAO,QAAQ,EACR,GACT,CAOO,SAASM,GACdC,EACAC,EACe,CACf,OAAOR,IAAW,KAAO,KAAOA,EAAO,QAAQO,EAAOC,CAAO,CAC/D,CAGO,SAASC,GAAQC,EAAmC,CACzDV,GAAA,MAAAA,EAAQ,QAAQU,EAClB,CAGO,SAASC,GAAOC,EAAaC,EAAqB,CACvDb,GAAA,MAAAA,EAAQ,OAAOY,EAAKC,EACtB,CAGO,SAASC,GAAMC,EAAsC,CAC1D,OAAOf,IAAW,KAAO,QAAQ,QAAQ,EAAI,EAAIA,EAAO,MAAMe,CAAS,CACzE,CAGA,eAAsBC,GAAMD,EAAsC,CAChE,GAAIf,IAAW,KACb,MAAO,GAET,IAAMiB,EAAUjB,EAChB,OAAAA,EAAS,KACFiB,EAAQ,MAAMF,CAAS,CAChC","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vigilon/browser",
3
- "version": "0.2.11",
3
+ "version": "0.2.13-beta.pr-5.7",
4
4
  "description": "Vigilon error monitoring for browser applications",
5
5
  "license": "ISC",
6
6
  "author": "Vigilon",