@vigilon/browser 0.2.14-beta.pr-6.8 → 0.2.15-beta.pr-7.9
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 +12 -0
- package/dist/index.cjs +13 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.mjs +12 -3
- package/dist/index.mjs.map +1 -1
- package/dist/vigilon.min.js +5 -5
- package/dist/vigilon.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,18 @@ setTag("plan", "pro");
|
|
|
58
58
|
|
|
59
59
|
`captureException` accepts any thrown value. Non-`Error` values are recorded with their string or JSON form as the message, and an error's `cause` chain is captured up to five levels.
|
|
60
60
|
|
|
61
|
+
## Pages
|
|
62
|
+
|
|
63
|
+
Vigilon groups an error's affected pages by route template, such as `/users/:id`, so a page with IDs in its URL still counts as one page. Without a template it derives one from the URL path, replacing segments that look like IDs. If your router knows the template, set it on every navigation:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { setRoute } from "@vigilon/browser";
|
|
67
|
+
|
|
68
|
+
setRoute("/users/:id"); // the template, never the URL; pass null to clear it
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`@vigilon/react` does this for TanStack Router and React Router.
|
|
72
|
+
|
|
61
73
|
## Configuration
|
|
62
74
|
|
|
63
75
|
| Option | Default | |
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(src_exports, {
|
|
|
25
25
|
close: () => close,
|
|
26
26
|
flush: () => flush,
|
|
27
27
|
init: () => init,
|
|
28
|
+
setRoute: () => setRoute,
|
|
28
29
|
setTag: () => setTag,
|
|
29
30
|
setUser: () => setUser
|
|
30
31
|
});
|
|
@@ -556,7 +557,7 @@ function sessionId() {
|
|
|
556
557
|
|
|
557
558
|
// src/browser/client.ts
|
|
558
559
|
var SDK_NAME = "@vigilon/browser";
|
|
559
|
-
var SDK_VERSION = "0.2.
|
|
560
|
+
var SDK_VERSION = "0.2.15-beta.pr-7.9";
|
|
560
561
|
var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
|
|
561
562
|
var EVENTS_PATH = "/v1/browser/events";
|
|
562
563
|
var DUPLICATE_WINDOW_MS = 6e4;
|
|
@@ -566,6 +567,7 @@ var BrowserClient = class {
|
|
|
566
567
|
this.tags = {};
|
|
567
568
|
this.debugIds = new DebugIdIndex(globalThis);
|
|
568
569
|
this.userId = null;
|
|
570
|
+
this.route = null;
|
|
569
571
|
this.removeListeners = [];
|
|
570
572
|
var _a, _b, _c, _d;
|
|
571
573
|
const endpoint = ((_a = options.endpoint) != null ? _a : DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
@@ -627,6 +629,9 @@ var BrowserClient = class {
|
|
|
627
629
|
setTag(key, value) {
|
|
628
630
|
this.tags[key] = String(value);
|
|
629
631
|
}
|
|
632
|
+
setRoute(route) {
|
|
633
|
+
this.route = route === null || route === "" ? null : String(route);
|
|
634
|
+
}
|
|
630
635
|
flush(timeoutMs) {
|
|
631
636
|
return this.transport.flush(timeoutMs);
|
|
632
637
|
}
|
|
@@ -639,7 +644,7 @@ var BrowserClient = class {
|
|
|
639
644
|
return flushed;
|
|
640
645
|
}
|
|
641
646
|
captureUnsafe(error, context, sourceUrl) {
|
|
642
|
-
var _a, _b, _c, _d;
|
|
647
|
+
var _a, _b, _c, _d, _e;
|
|
643
648
|
const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
|
|
644
649
|
if (sampleRate < 1 && Math.random() >= sampleRate) {
|
|
645
650
|
return null;
|
|
@@ -657,6 +662,7 @@ var BrowserClient = class {
|
|
|
657
662
|
...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
|
|
658
663
|
]);
|
|
659
664
|
const session = sessionId();
|
|
665
|
+
const route = (_e = context.route) != null ? _e : this.route;
|
|
660
666
|
let event = {
|
|
661
667
|
type: "exception",
|
|
662
668
|
eventId: uuid(),
|
|
@@ -665,7 +671,7 @@ var BrowserClient = class {
|
|
|
665
671
|
...debugIds ? { debugIds } : {},
|
|
666
672
|
page: {
|
|
667
673
|
url: window.location.href,
|
|
668
|
-
...
|
|
674
|
+
...route ? { route } : {}
|
|
669
675
|
},
|
|
670
676
|
session: session === null ? null : { id: session },
|
|
671
677
|
user: this.userId === null ? null : { id: this.userId },
|
|
@@ -776,6 +782,9 @@ function captureException(error, context) {
|
|
|
776
782
|
function setUser(user) {
|
|
777
783
|
client == null ? void 0 : client.setUser(user);
|
|
778
784
|
}
|
|
785
|
+
function setRoute(route) {
|
|
786
|
+
client == null ? void 0 : client.setRoute(route);
|
|
787
|
+
}
|
|
779
788
|
function setTag(key, value) {
|
|
780
789
|
client == null ? void 0 : client.setTag(key, value);
|
|
781
790
|
}
|
|
@@ -797,6 +806,7 @@ async function close(timeoutMs) {
|
|
|
797
806
|
close,
|
|
798
807
|
flush,
|
|
799
808
|
init,
|
|
809
|
+
setRoute,
|
|
800
810
|
setTag,
|
|
801
811
|
setUser
|
|
802
812
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["init"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["init"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -67,7 +67,7 @@ interface VigilonBrowserOptions {
|
|
|
67
67
|
interface CaptureContext {
|
|
68
68
|
mechanism?: ExceptionMechanism;
|
|
69
69
|
handled?: boolean;
|
|
70
|
-
/** Overrides the route template for this event. */
|
|
70
|
+
/** Overrides the route template set with setRoute, for this event only. */
|
|
71
71
|
route?: string;
|
|
72
72
|
componentStack?: string;
|
|
73
73
|
}
|
|
@@ -95,6 +95,13 @@ declare function captureException(error: unknown, context?: CaptureContext): str
|
|
|
95
95
|
declare function setUser(user: {
|
|
96
96
|
id: string;
|
|
97
97
|
} | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Sets the route template of the current page, such as `/users/:id`, for
|
|
100
|
+
* later events; pass null to clear it. Vigilon groups pages by it, so it must
|
|
101
|
+
* be a template, never a URL with IDs in it. Without one, Vigilon derives the
|
|
102
|
+
* page from the URL. Router integrations call this on every navigation.
|
|
103
|
+
*/
|
|
104
|
+
declare function setRoute(route: string | null): void;
|
|
98
105
|
/** Attaches a tag to later events. */
|
|
99
106
|
declare function setTag(key: string, value: string): void;
|
|
100
107
|
/** Sends queued events now. Resolves true when everything was sent. */
|
|
@@ -102,4 +109,4 @@ declare function flush(timeoutMs?: number): Promise<boolean>;
|
|
|
102
109
|
/** Flushes, then stops capturing errors. */
|
|
103
110
|
declare function close(timeoutMs?: number): Promise<boolean>;
|
|
104
111
|
|
|
105
|
-
export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setTag, setUser };
|
|
112
|
+
export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setRoute, setTag, setUser };
|
package/dist/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ interface VigilonBrowserOptions {
|
|
|
67
67
|
interface CaptureContext {
|
|
68
68
|
mechanism?: ExceptionMechanism;
|
|
69
69
|
handled?: boolean;
|
|
70
|
-
/** Overrides the route template for this event. */
|
|
70
|
+
/** Overrides the route template set with setRoute, for this event only. */
|
|
71
71
|
route?: string;
|
|
72
72
|
componentStack?: string;
|
|
73
73
|
}
|
|
@@ -95,6 +95,13 @@ declare function captureException(error: unknown, context?: CaptureContext): str
|
|
|
95
95
|
declare function setUser(user: {
|
|
96
96
|
id: string;
|
|
97
97
|
} | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Sets the route template of the current page, such as `/users/:id`, for
|
|
100
|
+
* later events; pass null to clear it. Vigilon groups pages by it, so it must
|
|
101
|
+
* be a template, never a URL with IDs in it. Without one, Vigilon derives the
|
|
102
|
+
* page from the URL. Router integrations call this on every navigation.
|
|
103
|
+
*/
|
|
104
|
+
declare function setRoute(route: string | null): void;
|
|
98
105
|
/** Attaches a tag to later events. */
|
|
99
106
|
declare function setTag(key: string, value: string): void;
|
|
100
107
|
/** Sends queued events now. Resolves true when everything was sent. */
|
|
@@ -102,4 +109,4 @@ declare function flush(timeoutMs?: number): Promise<boolean>;
|
|
|
102
109
|
/** Flushes, then stops capturing errors. */
|
|
103
110
|
declare function close(timeoutMs?: number): Promise<boolean>;
|
|
104
111
|
|
|
105
|
-
export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setTag, setUser };
|
|
112
|
+
export { type BrowserEvent, type BrowserException, type CaptureContext, type ExceptionCause, type ExceptionMechanism, SDK_VERSION, type VigilonBrowserOptions, captureException, close, flush, init, setRoute, setTag, setUser };
|
package/dist/index.mjs
CHANGED
|
@@ -524,7 +524,7 @@ function sessionId() {
|
|
|
524
524
|
|
|
525
525
|
// src/browser/client.ts
|
|
526
526
|
var SDK_NAME = "@vigilon/browser";
|
|
527
|
-
var SDK_VERSION = "0.2.
|
|
527
|
+
var SDK_VERSION = "0.2.15-beta.pr-7.9";
|
|
528
528
|
var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
|
|
529
529
|
var EVENTS_PATH = "/v1/browser/events";
|
|
530
530
|
var DUPLICATE_WINDOW_MS = 6e4;
|
|
@@ -534,6 +534,7 @@ var BrowserClient = class {
|
|
|
534
534
|
this.tags = {};
|
|
535
535
|
this.debugIds = new DebugIdIndex(globalThis);
|
|
536
536
|
this.userId = null;
|
|
537
|
+
this.route = null;
|
|
537
538
|
this.removeListeners = [];
|
|
538
539
|
var _a, _b, _c, _d;
|
|
539
540
|
const endpoint = ((_a = options.endpoint) != null ? _a : DEFAULT_ENDPOINT).replace(/\/+$/, "");
|
|
@@ -595,6 +596,9 @@ var BrowserClient = class {
|
|
|
595
596
|
setTag(key, value) {
|
|
596
597
|
this.tags[key] = String(value);
|
|
597
598
|
}
|
|
599
|
+
setRoute(route) {
|
|
600
|
+
this.route = route === null || route === "" ? null : String(route);
|
|
601
|
+
}
|
|
598
602
|
flush(timeoutMs) {
|
|
599
603
|
return this.transport.flush(timeoutMs);
|
|
600
604
|
}
|
|
@@ -607,7 +611,7 @@ var BrowserClient = class {
|
|
|
607
611
|
return flushed;
|
|
608
612
|
}
|
|
609
613
|
captureUnsafe(error, context, sourceUrl) {
|
|
610
|
-
var _a, _b, _c, _d;
|
|
614
|
+
var _a, _b, _c, _d, _e;
|
|
611
615
|
const sampleRate = (_a = this.options.sampleRate) != null ? _a : 1;
|
|
612
616
|
if (sampleRate < 1 && Math.random() >= sampleRate) {
|
|
613
617
|
return null;
|
|
@@ -625,6 +629,7 @@ var BrowserClient = class {
|
|
|
625
629
|
...((_d = exception.cause) != null ? _d : []).map((cause) => cause.stack)
|
|
626
630
|
]);
|
|
627
631
|
const session = sessionId();
|
|
632
|
+
const route = (_e = context.route) != null ? _e : this.route;
|
|
628
633
|
let event = {
|
|
629
634
|
type: "exception",
|
|
630
635
|
eventId: uuid(),
|
|
@@ -633,7 +638,7 @@ var BrowserClient = class {
|
|
|
633
638
|
...debugIds ? { debugIds } : {},
|
|
634
639
|
page: {
|
|
635
640
|
url: window.location.href,
|
|
636
|
-
...
|
|
641
|
+
...route ? { route } : {}
|
|
637
642
|
},
|
|
638
643
|
session: session === null ? null : { id: session },
|
|
639
644
|
user: this.userId === null ? null : { id: this.userId },
|
|
@@ -744,6 +749,9 @@ function captureException(error, context) {
|
|
|
744
749
|
function setUser(user) {
|
|
745
750
|
client == null ? void 0 : client.setUser(user);
|
|
746
751
|
}
|
|
752
|
+
function setRoute(route) {
|
|
753
|
+
client == null ? void 0 : client.setRoute(route);
|
|
754
|
+
}
|
|
747
755
|
function setTag(key, value) {
|
|
748
756
|
client == null ? void 0 : client.setTag(key, value);
|
|
749
757
|
}
|
|
@@ -764,6 +772,7 @@ export {
|
|
|
764
772
|
close,
|
|
765
773
|
flush,
|
|
766
774
|
init,
|
|
775
|
+
setRoute,
|
|
767
776
|
setTag,
|
|
768
777
|
setUser
|
|
769
778
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts","../src/index.ts"],"sourcesContent":["/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n","/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n"],"mappings":"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","names":["init"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts","../src/index.ts"],"sourcesContent":["/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n","/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n"],"mappings":";AAKO,IAAM,SAAS;AAAA,EACpB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,wBAAwB,IAAI;AAAA,EAC5B,sBAAsB,KAAK;AAAA,EAC3B,qBAAqB;AAAA,EACrB,sBAAsB,KAAK;AAAA,EAC3B,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,kBAAkB,IAAI;AAAA,EACtB,eAAe,IAAI;AAAA,EACnB,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,gBAAgB;AAClB;AAGO,IAAM,wBAAwB;AAE9B,IAAM,YAAY;;;ACzBlB,SAAS,SAAS,OAAe,WAA2B;AACjE,MAAI,MAAM,UAAU,WAAW;AAC7B,WAAO;AAAA,EACT;AACA,QAAM,OAAO,MAAM,WAAW,YAAY,CAAC;AAC3C,QAAM,MAAM,QAAQ,SAAU,QAAQ,QAAS,YAAY,IAAI;AAC/D,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAGO,SAAS,cAAc,OAAuB;AACnD,QAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,QAAM,OACJ,MAAM,SAAS,OAAO,sBAClB,MAAM,MAAM,GAAG,OAAO,mBAAmB,EAAE,KAAK,IAAI,IACpD;AACN,SAAO,SAAS,MAAM,OAAO,oBAAoB;AACnD;AAOA,IAAM,WAAW;AAEjB,IAAM,cAAc;AAEpB,IAAM,UAAU;AAGT,SAAS,SAAS,OAAuB;AAC9C,QAAM,MAAM,MAAM,QAAQ,UAAU,IAAI;AACxC,QAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,QAAM,WAAW,IAAI,QAAQ,GAAG;AAChC,QAAM,MACJ,UAAU,KAAK,WAAW,aAAa,KAAK,QAAQ,KAAK,IAAI,OAAO,QAAQ;AAC9E,SAAO,QAAQ,KAAK,MAAM,IAAI,MAAM,GAAG,GAAG;AAC5C;AAGO,SAAS,gBAAgB,MAAsB;AACpD,SAAO,KAAK,QAAQ,aAAa,CAAC,UAAU;AA7C9C;AA8CI,UAAM,WAAU,mBAAQ,KAAK,KAAK,MAAlB,mBAAsB,OAAtB,YAA4B;AAC5C,WAAO,SAAS,MAAM,MAAM,GAAG,MAAM,SAAS,QAAQ,MAAM,CAAC,IAAI;AAAA,EACnE,CAAC;AACH;;;ACvCA,IAAM,aAAa,CAAC,oBAAoB,mBAAmB,WAAW;AAKtE,IAAM,YAAY;AAIX,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAA6B,OAAgC;AAAhC;AAH7B,SAAQ,iBAAiB;AACzB,SAAQ,QAAgC,CAAC;AAAA,EAEqB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9D,UAAU,QAA8E;AACtF,UAAM,OAAO,gBAAgB,OAAO,OAAO,OAAO,EAAE,KAAK,IAAI,CAAC;AAC9D,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO;AAAA,IACT;AACA,QAAI;AACJ,eAAW,CAAC,KAAK,EAAE,KAAK,OAAO,QAAQ,KAAK,WAAW,CAAC,GAAG;AAEzD,UAAI,KAAK,SAAS,GAAG,GAAG,GAAG,GAAG;AAC5B,wCAAU,CAAC;AACX,cAAM,GAAG,IAAI;AAAA,MACf;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,aAAqC;AAC3C,UAAM,aAAa,WAAW,IAAI,CAAC,SAAS,KAAK,MAAM,IAAI,CAAC,EAAE,OAAO,UAAU;AAC/E,UAAM,UAAU,WAAW,OAAO,CAAC,OAAO,aAAa,QAAQ,OAAO,KAAK,QAAQ,EAAE,QAAQ,CAAC;AAC9F,QAAI,YAAY,KAAK,gBAAgB;AACnC,aAAO,KAAK;AAAA,IACd;AACA,UAAM,QAAgC,CAAC;AACvC,eAAW,YAAY,YAAY;AACjC,iBAAW,CAAC,OAAO,EAAE,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAClD,cAAM,MAAM,UAAU,KAAK;AAC3B,YAAI,QAAQ,QAAQ,OAAO,OAAO,YAAY,EAAE,OAAO,QAAQ;AAC7D,gBAAM,GAAG,IAAI;AAAA,QACf;AAAA,MACF;AAAA,IACF;AACA,SAAK,iBAAiB;AACtB,SAAK,QAAQ;AACb,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,OAA8B;AAC/C,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,QAAQ,UAAU,KAAK,IAAI;AACjC,QAAI,OAAO;AACT,aAAO,SAAS,MAAM,CAAC,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAmC;AACrD,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;;;AChEO,SAAS,YACd,OACA,WACA,SACkB;AAClB,QAAM,QAAQ,aAAa,KAAK;AAChC,QAAM,YAA8B,EAAE,GAAG,OAAO,WAAW,QAAQ;AACnE,QAAM,QAAQ,aAAa,KAAK;AAChC,MAAI,MAAM,SAAS,GAAG;AACpB,cAAU,QAAQ;AAAA,EACpB;AACA,SAAO;AACT;AAGO,SAAS,eAAe,WAA+C;AAC5E,QAAM,UAA4B;AAAA,IAChC,GAAG;AAAA,IACH,GAAG,WAAW,SAAS;AAAA,EACzB;AACA,MAAI,UAAU,OAAO;AACnB,YAAQ,QAAQ,UAAU,MACvB,MAAM,GAAG,OAAO,UAAU,EAC1B,IAAI,CAAC,UAAU,WAAW,KAAK,CAAmB;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAIL;AACb,SAAO;AAAA,IACL,MAAM,SAAS,OAAO,MAAM,IAAI,GAAG,OAAO,mBAAmB;AAAA,IAC7D,SAAS,SAAS,OAAO,MAAM,OAAO,GAAG,OAAO,sBAAsB;AAAA,IACtE,OACE,OAAO,MAAM,UAAU,YAAY,MAAM,MAAM,SAAS,IACpD,cAAc,gBAAgB,MAAM,KAAK,CAAC,IAC1C;AAAA,EACR;AACF;AAEA,SAAS,aAAa,OAA4B;AAChD,MAAI,YAAY,KAAK,GAAG;AACtB,WAAO;AAAA,MACL,MAAM,OAAO,MAAM,SAAS,YAAY,MAAM,OAAO,MAAM,OAAO;AAAA,MAClE,SAAS,OAAO,MAAM,YAAY,WAAW,MAAM,UAAU;AAAA,MAC7D,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,IACzD;AAAA,EACF;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,YAAY,KAAK,GAAG,OAAO,KAAK;AACnE;AAEA,SAAS,aAAa,OAAkC;AACtD,QAAM,QAA0B,CAAC;AACjC,QAAM,OAAO,oBAAI,IAAa,CAAC,KAAK,CAAC;AACrC,MAAI,QAAQ,YAAY,KAAK,IAAI,MAAM,QAAQ;AAC/C,SAAO,UAAU,UAAa,UAAU,QAAQ,MAAM,SAAS,OAAO,YAAY;AAChF,QAAI,KAAK,IAAI,KAAK,GAAG;AACnB;AAAA,IACF;AACA,SAAK,IAAI,KAAK;AACd,UAAM,KAAK,aAAa,KAAK,CAAC;AAC9B,YAAQ,YAAY,KAAK,IAAI,MAAM,QAAQ;AAAA,EAC7C;AACA,SAAO;AACT;AASA,SAAS,YAAY,OAAoC;AACvD,SACE,iBAAiB,SAChB,OAAO,UAAU,YAChB,UAAU,QACV,OAAQ,MAAoB,YAAY,YACxC,OAAQ,MAAoB,UAAU;AAE5C;AAEA,SAAS,YAAY,OAAwB;AAC3C,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,WAAO,SAAS,SAAY,OAAO,KAAK,IAAI;AAAA,EAC9C,QAAQ;AACN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;;;ACtGO,IAAM,wBAAmC;AAAA,EAC9C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,IAAM,oBAA+B;AAAA,EAC1C;AAAA,EACA;AACF;AAEA,IAAM,YAAY;AAEX,SAAS,WAAW,OAAe,UAA8B;AACtE,SAAO,SAAS;AAAA,IAAK,CAAC,YACpB,OAAO,YAAY,WAAW,MAAM,SAAS,OAAO,IAAI,QAAQ,KAAK,KAAK;AAAA,EAC5E;AACF;AAEO,SAAS,eAAe,OAAqB,UAA8B;AAChF,QAAM,EAAE,MAAM,QAAQ,IAAI,MAAM;AAChC,SACE,WAAW,SAAS,QAAQ,KAC5B,WAAW,GAAG,IAAI,KAAK,OAAO,IAAI,QAAQ,KACzC,SAAS,gBAAgB,WAAW,cAAc,QAAQ;AAE/D;AAMO,SAAS,YACd,OACA,WACA,UACS;AAnDX;AAoDE,QAAM,MAAM,eAAa,eAAU,MAAK,WAAM,UAAU,UAAhB,YAAyB,EAAE,MAA1C,mBAA8C;AACvE,SAAO,QAAQ,UAAa,WAAW,KAAK,QAAQ;AACtD;;;AChDO,IAAM,aAAN,MAAiB;AAAA,EAItB,YACmB,kBACA,mBACjB;AAFiB;AACA;AALnB,SAAiB,WAAW,oBAAI,IAAoB;AACpD,SAAQ,WAAW;AAAA,EAKhB;AAAA,EAEH,MAAM,OAAqB,KAAsB;AAC/C,QAAI,KAAK,YAAY,KAAK,kBAAkB;AAC1C,aAAO;AAAA,IACT;AACA,UAAM,EAAE,MAAM,SAAS,MAAM,IAAI,MAAM;AACvC,UAAM,MAAM,GAAG,IAAI;AAAA,EAAK,OAAO;AAAA,GAAM,wBAAS,IAAI,MAAM,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC;AAC3E,UAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,QAAI,aAAa,UAAa,MAAM,WAAW,KAAK,mBAAmB;AACrE,aAAO;AAAA,IACT;AACA,SAAK,SAAS,IAAI,KAAK,GAAG;AAC1B,SAAK,YAAY;AACjB,WAAO;AAAA,EACT;AACF;;;ACxBA,IAAM,2BAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,aAAa;AAGZ,SAAS,aACd,MACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,MAAI,QAAQ;AACZ,aAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,QAAI,UAAU,OAAO,YAAY;AAC/B;AAAA,IACF;AACA,UAAM,UAAU,IAAI,YAAY;AAChC,QACE,IAAI,WAAW,KACf,IAAI,SAAS,OAAO,gBACpB,yBAAyB,KAAK,CAAC,aAAa,QAAQ,SAAS,QAAQ,CAAC,GACtE;AACA;AAAA,IACF;AACA,WAAO,GAAG,IAAI,SAAS,OAAO,KAAK,GAAG,CAAC,GAAG,OAAO,cAAc;AAC/D,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAMO,SAAS,mBAAmB,OAA+B;AAChE,SAAO,OAAO,UAAU,YACtB,MAAM,SAAS,KACf,MAAM,UAAU,OAAO,mBACrB,QACA;AACN;AAGO,SAAS,eAAe,OAA+B;AAC5D,QAAM,KAAK,mBAAmB,KAAK;AACnC,SAAO,OAAO,QAAQ,WAAW,KAAK,EAAE,IAAI,OAAO;AACrD;AAEA,IAAM,OAAO;AAGN,SAAS,iBACd,UACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,MAAI,QAAQ;AACZ,aAAW,CAAC,KAAK,EAAE,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAChD,UAAM,MAAM,SAAS,GAAG;AACxB,QAAI,UAAU,OAAO,gBAAgB;AACnC;AAAA,IACF;AACA,QAAI,IAAI,SAAS,KAAK,IAAI,UAAU,OAAO,oBAAoB,KAAK,KAAK,OAAO,EAAE,CAAC,GAAG;AACpF,aAAO,GAAG,IAAI,OAAO,EAAE;AACvB,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO;AACT;;;ACjDA,IAAM,iBAAiB;AAEvB,IAAM,iBAAiB,MAAM;AAE7B,IAAM,yBAAyB,KAAK;AACpC,IAAM,0BAA0B;AAChC,IAAM,YAAY;AAClB,IAAM,eAAe;AAErB,IAAM,mBAAmB,KAAK,KAAK;AACnC,IAAM,yBAAyB,EAAE,KAAK,KAAQ,KAAK,IAAO;AAC1D,IAAM,iBAAiB;AAQhB,IAAM,YAAN,MAAgB;AAAA,EAOrB,YAA6B,SAA2B;AAA3B;AAN7B,SAAQ,QAAmB,CAAC;AAC5B,SAAQ,cAAc;AACtB,SAAQ,QAA8C;AACtD,SAAQ,OAA6B;AACrC,SAAiB,WAAW,oBAAI,IAAmB;AAAA,EAEM;AAAA,EAEzD,QAAQ,OAA2B;AACjC,QAAI,KAAK,MAAM,UAAU,WAAW;AAClC,WAAK,QAAQ,MAAM,2BAA2B;AAC9C;AAAA,IACF;AACA,SAAK,MAAM,KAAK;AAAA,MACd;AAAA,MACA,OAAO,WAAW,KAAK,UAAU,KAAK,CAAC;AAAA,MACvC,UAAU;AAAA,MACV,gBAAgB;AAAA,IAClB,CAAC;AACD,SAAK,SAAS,cAAc;AAAA,EAC9B;AAAA;AAAA,EAGA,MAAM,MAAM,YAAY,KAAwB;AAC9C,SAAK,YAAY;AACjB,QAAI;AACJ,UAAM,QAAQ,KAAK;AAAA,MACjB,KAAK,MAAM,EAAE,KAAK,MAAM,QAAQ,IAAI,KAAK,QAAQ,CAAC;AAAA,MAClD,IAAI,QAAc,CAAC,YAAY;AAC7B,kBAAU,WAAW,SAAS,SAAS;AAAA,MACzC,CAAC;AAAA,IACH,CAAC;AACD,iBAAa,OAAO;AACpB,WAAO,KAAK,MAAM,WAAW,KAAK,KAAK,SAAS,SAAS;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAsB;AACpB,SAAK,YAAY;AACjB,QAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,aAAa;AACzC;AAAA,IACF;AACA,QAAI,SAAS;AACb,WAAO,KAAK,MAAM,SAAS,GAAG;AAC5B,YAAM,QAAQ,KAAK,UAAU,MAAM;AACnC,UAAI,MAAM,WAAW,GAAG;AACtB;AAAA,MACF;AACA,gBAAU,WAAW,KAAK;AAC1B,WAAK,MAAM,KAAK,KAAK,OAAO,IAAI,CAAC;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,YAAY;AACjB,SAAK,QAAQ,CAAC;AAAA,EAChB;AAAA,EAEQ,SAAS,SAAuB;AACtC,QAAI,KAAK,UAAU,QAAQ,KAAK,MAAM,WAAW,GAAG;AAClD;AAAA,IACF;AACA,UAAM,OAAO,KAAK,IAAI,SAAS,KAAK,cAAc,KAAK,QAAQ,IAAI,CAAC;AACpE,SAAK,QAAQ,WAAW,MAAM;AAC5B,WAAK,QAAQ;AACb,WAAK,KAAK,MAAM;AAAA,IAClB,GAAG,IAAI;AAAA,EACT;AAAA,EAEQ,cAAoB;AAC1B,QAAI,KAAK,UAAU,MAAM;AACvB,mBAAa,KAAK,KAAK;AACvB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEQ,QAAuB;AAC7B,QAAI,KAAK,SAAS,MAAM;AACtB,WAAK,OAAO,KAAK,IAAI,EAAE,QAAQ,MAAM;AACnC,aAAK,OAAO;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,MAAqB;AACjC,WAAO,KAAK,MAAM,SAAS,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,aAAa;AACtE,YAAM,QAAQ,KAAK,UAAU,cAAc;AAC3C,UAAI,MAAM,WAAW,GAAG;AACtB;AAAA,MACF;AACA,YAAM,KAAK,MAAM,KAAK,KAAK,OAAO,KAAK,CAAC;AAAA,IAC1C;AACA,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA,EAEQ,MAAM,SAAuC;AACnD,SAAK,SAAS,IAAI,OAAO;AACzB,WAAO,QAAQ,QAAQ,MAAM,KAAK,SAAS,OAAO,OAAO,CAAC;AAAA,EAC5D;AAAA;AAAA,EAGQ,UAAU,UAA6B;AAC7C,UAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,SAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,YAAY;AAC1C,YAAM,UACJ,QAAQ,YAAY,gBACnB,QAAQ,mBAAmB,QAC1B,MAAM,QAAQ,iBAAiB;AACnC,UAAI,SAAS;AACX,aAAK,QAAQ,MAAM,SAAS,QAAQ,MAAM,OAAO,wBAAwB;AAAA,MAC3E;AACA,aAAO,CAAC;AAAA,IACV,CAAC;AACD,UAAM,QAAmB,CAAC;AAC1B,QAAI,QAAQ;AACZ,WACE,KAAK,MAAM,SAAS,KACpB,MAAM,SAAS,OAAO,oBACtB,QAAQ,KAAK,MAAM,CAAC,EAAE,SAAS,UAC/B;AACA,YAAM,UAAU,KAAK,MAAM,MAAM;AACjC,eAAS,QAAQ;AACjB,YAAM,KAAK,OAAO;AAAA,IACpB;AAGA,QAAI,MAAM,WAAW,KAAK,KAAK,MAAM,SAAS,KAAK,aAAa,gBAAgB;AAC9E,YAAM,KAAK,KAAK,MAAM,MAAM,CAAY;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,KAAK,OAAkB,WAAmC;AA7L1E;AA8LI,UAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,eAAW,WAAW,OAAO;AAC3B,cAAQ,YAAY;AACpB,oBAAQ,mBAAR,oBAAQ,iBAAmB;AAAA,IAC7B;AACA,UAAM,WAAiC;AAAA,MACrC,GAAG,KAAK,QAAQ;AAAA,MAChB,QAAQ,IAAI,KAAK,GAAG,EAAE,YAAY;AAAA,MAClC,QAAQ,MAAM,IAAI,CAAC,YAAY,QAAQ,KAAK;AAAA,IAC9C;AACA,QAAI;AACJ,QAAI,aAA4B;AAChC,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,QAAQ,MAAM,KAAK,QAAQ,KAAK;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,eAAe,UAAU,KAAK,QAAQ,MAAM;AAAA,UAC5C,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,QAAQ;AAAA,QAC7B;AAAA,MACF,CAAC;AACD,eAAS,SAAS;AAClB,mBAAa,SAAS,QAAQ,IAAI,aAAa;AAAA,IACjD,QAAQ;AAEN,WAAK,QAAQ,KAAK;AAClB,YAAM,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,QAAQ,QAAQ,CAAC;AACrE,WAAK,MAAM,KAAK,IAAI,MAAO,KAAK,UAAU,cAAc,CAAC;AACzD;AAAA,IACF;AACA,QAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAK,QAAQ,KAAK;AAClB,WAAK,OAAM,qBAAgB,YAAY,KAAK,QAAQ,IAAI,CAAC,MAA9C,YAAmD,uBAAuB,MAAM,CAAC;AAC5F;AAAA,IACF;AACA,QAAI,SAAS,OAAO,UAAU,KAAK;AACjC,WAAK,QAAQ,MAAM,yBAAyB,MAAM,KAAK,MAAM,MAAM,iBAAiB;AAAA,IACtF;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAwB;AACtC,SAAK,MAAM,QAAQ,GAAG,KAAK;AAC3B,SAAK,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,QAAQ,SAAS;AAAA,EAC3D;AAAA,EAEQ,MAAM,IAAkB;AAC9B,SAAK,cAAc,KAAK,IAAI,KAAK,aAAa,KAAK,QAAQ,IAAI,IAAI,EAAE;AACrE,SAAK,YAAY;AACjB,SAAK,SAAS,CAAC;AAAA,EACjB;AACF;AAGO,SAAS,gBAAgB,OAAsB,KAA4B;AAChF,MAAI,UAAU,QAAQ,MAAM,KAAK,MAAM,IAAI;AACzC,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,KAAK;AAC5B,MAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,WAAO,KAAK,IAAI,GAAG,UAAU,GAAI;AAAA,EACnC;AACA,QAAM,OAAO,KAAK,MAAM,KAAK;AAC7B,SAAO,OAAO,MAAM,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,OAAO,GAAG;AAC3D;AAEA,SAAS,WAAW,OAA0B;AAC5C,SAAO,MAAM,OAAO,CAAC,OAAO,YAAY,QAAQ,QAAQ,OAAO,uBAAuB;AACxF;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,OAAO,gBAAgB,cAC1B,MAAM,SAAS,IACf,IAAI,YAAY,EAAE,OAAO,KAAK,EAAE;AACtC;;;ACvQO,SAAS,OAAe;AAC7B,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,QAAM,SAAU,WAAmC;AACnD,MAAI,iCAAQ,iBAAiB;AAC3B,WAAO,gBAAgB,KAAK;AAAA,EAC9B,OAAO;AACL,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,YAAM,CAAC,IAAI,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG;AAAA,IAC3C;AAAA,EACF;AACA,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,MAAM,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAC1E,SAAO,GAAG,IAAI,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAC/D,MAAM,GAAG,CAAC,EACV,KAAK,EAAE,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC;AACpE;;;ACfA,IAAM,cAAc;AAOb,SAAS,YAA2B;AACzC,MAAI;AACF,UAAM,WAAW,OAAO,eAAe,QAAQ,WAAW;AAC1D,QAAI,UAAU;AACZ,aAAO;AAAA,IACT;AACA,UAAM,UAAU,KAAK;AACrB,WAAO,eAAe,QAAQ,aAAa,OAAO;AAClD,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACIO,IAAM,WAAW;AACjB,IAAM,cAAc;AAC3B,IAAM,mBAAmB;AACzB,IAAM,cAAc;AAsCpB,IAAM,sBAAsB;AAErB,IAAM,gBAAN,MAAoB;AAAA,EAWzB,YAA6B,SAAgC;AAAhC;AAN7B,SAAiB,OAA+B,CAAC;AACjD,SAAiB,WAAW,IAAI,aAAa,UAAgD;AAC7F,SAAQ,SAAwB;AAChC,SAAQ,QAAuB;AAC/B,SAAiB,kBAAqC,CAAC;AA7EzD;AAgFI,UAAM,aAAY,aAAQ,aAAR,YAAoB,kBAAkB,QAAQ,QAAQ,EAAE;AAC1E,SAAK,YAAY,IAAI,UAAU;AAAA,MAC7B,KAAK,GAAG,QAAQ,GAAG,WAAW;AAAA,MAC9B,QAAQ,QAAQ;AAAA,MAChB,UAAU;AAAA,QACR,QAAQ;AAAA,QACR,KAAK,EAAE,MAAM,UAAU,SAAS,YAAY;AAAA,QAC5C,SAAS;AAAA,UACP,MAAM,QAAQ;AAAA,UACd,aAAa,QAAQ;AAAA,UACrB,GAAI,QAAQ,iBACR,EAAE,SAAS,SAAS,QAAQ,gBAAgB,OAAO,oBAAoB,EAAE,IACzE,CAAC;AAAA,QACP;AAAA,MACF;AAAA,MACA,OAAO,CAAC,OAAOA,UAAS,OAAO,MAAM,OAAOA,KAAI;AAAA,MAChD,KAAK,MAAM,KAAK,IAAI;AAAA,MACpB,OAAO,CAAC,YAAY,KAAK,MAAM,OAAO;AAAA,IACxC,CAAC;AACD,SAAK,aAAa,IAAI;AAAA,OACpB,aAAQ,qBAAR,YAA4B;AAAA,MAC5B;AAAA,IACF;AACA,SAAK,eAAe,CAAC,GAAG,uBAAuB,IAAI,aAAQ,iBAAR,YAAwB,CAAC,CAAE;AAC9E,SAAK,WAAW,CAAC,GAAG,mBAAmB,IAAI,aAAQ,aAAR,YAAoB,CAAC,CAAE;AAAA,EACpE;AAAA,EAEA,UAAgB;AACd,SAAK,OAAO,QAAQ,SAAS,CAAC,UAAU;AACtC,YAAM,EAAE,OAAO,SAAS,SAAS,IAAI;AAGrC,WAAK,QAAQ,wBAAS,SAAS,EAAE,WAAW,WAAW,SAAS,MAAM,GAAG,QAAQ;AAAA,IACnF,CAAC;AACD,SAAK,OAAO,QAAQ,sBAAsB,CAAC,UAAU;AACnD,WAAK,QAAS,MAAgC,QAAQ;AAAA,QACpD,WAAW;AAAA,QACX,SAAS;AAAA,MACX,CAAC;AAAA,IACH,CAAC;AAID,SAAK,OAAO,UAAU,oBAAoB,MAAM;AAC9C,UAAI,SAAS,oBAAoB,UAAU;AACzC,aAAK,UAAU,cAAc;AAAA,MAC/B;AAAA,IACF,CAAC;AACD,SAAK,OAAO,QAAQ,YAAY,MAAM,KAAK,UAAU,cAAc,CAAC;AAAA,EACtE;AAAA,EAEA,QAAQ,OAAgB,UAA0B,CAAC,GAAG,WAAmC;AACvF,QAAI;AACF,aAAO,KAAK,cAAc,OAAO,SAAS,SAAS;AAAA,IACrD,SAAS,SAAS;AAEhB,WAAK,MAAM,mBAAmB,OAAO,OAAO,CAAC,EAAE;AAC/C,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,QAAQ,MAAmC;AACzC,SAAK,SAAS,SAAS,OAAO,OAAO,eAAe,KAAK,EAAE;AAC3D,QAAI,SAAS,QAAQ,KAAK,WAAW,MAAM;AACzC,WAAK,MAAM,0FAA0F;AAAA,IACvG;AAAA,EACF;AAAA,EAEA,OAAO,KAAa,OAAqB;AACvC,SAAK,KAAK,GAAG,IAAI,OAAO,KAAK;AAAA,EAC/B;AAAA,EAEA,SAAS,OAA4B;AACnC,SAAK,QAAQ,UAAU,QAAQ,UAAU,KAAK,OAAO,OAAO,KAAK;AAAA,EACnE;AAAA,EAEA,MAAM,WAAsC;AAC1C,WAAO,KAAK,UAAU,MAAM,SAAS;AAAA,EACvC;AAAA,EAEA,MAAM,MAAM,WAAsC;AAChD,eAAW,UAAU,KAAK,gBAAgB,OAAO,CAAC,GAAG;AACnD,aAAO;AAAA,IACT;AACA,UAAM,UAAU,MAAM,KAAK,UAAU,MAAM,SAAS;AACpD,SAAK,UAAU,MAAM;AACrB,WAAO;AAAA,EACT;AAAA,EAEQ,cACN,OACA,SACA,WACe;AA7KnB;AA8KI,UAAM,cAAa,UAAK,QAAQ,eAAb,YAA2B;AAC9C,QAAI,aAAa,KAAK,KAAK,OAAO,KAAK,YAAY;AACjD,aAAO;AAAA,IACT;AACA,UAAM,YAAY;AAAA,MAChB;AAAA,OACA,aAAQ,cAAR,YAAqB;AAAA,OACrB,aAAQ,YAAR,YAAmB;AAAA,IACrB;AACA,QAAI,QAAQ,gBAAgB;AAC1B,gBAAU,iBAAiB,QAAQ;AAAA,IACrC;AACA,UAAM,WAAW,KAAK,SAAS,UAAU;AAAA,MACvC,UAAU;AAAA,MACV,KAAI,eAAU,UAAV,YAAmB,CAAC,GAAG,IAAI,CAAC,UAAU,MAAM,KAAK;AAAA,IACvD,CAAC;AACD,UAAM,UAAU,UAAU;AAC1B,UAAM,SAAQ,aAAQ,UAAR,YAAiB,KAAK;AACpC,QAAI,QAA6B;AAAA,MAC/B,MAAM;AAAA,MACN,SAAS,KAAK;AAAA,MACd,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MAC/B,MAAM;AAAA,QACJ,KAAK,OAAO,SAAS;AAAA,QACrB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,MAC3B;AAAA,MACA,SAAS,YAAY,OAAO,OAAO,EAAE,IAAI,QAAQ;AAAA,MACjD,MAAM,KAAK,WAAW,OAAO,OAAO,EAAE,IAAI,KAAK,OAAO;AAAA,MACtD,SAAS;AAAA,MACT,MAAM,EAAE,GAAG,KAAK,KAAK;AAAA,IACvB;AACA,QACE,eAAe,OAAO,KAAK,YAAY,KACvC,YAAY,OAAO,WAAW,KAAK,QAAQ,GAC3C;AACA,aAAO;AAAA,IACT;AACA,QAAI,KAAK,QAAQ,YAAY;AAC3B,UAAI;AACF,gBAAQ,KAAK,QAAQ,WAAW,KAAK;AAAA,MACvC,SAAS,SAAS;AAGhB,aAAK,MAAM,oCAAoC,OAAO,OAAO,CAAC,EAAE;AAChE,eAAO;AAAA,MACT;AACA,UAAI,UAAU,MAAM;AAClB,eAAO;AAAA,MACT;AAAA,IACF;AACA,UAAM,UAAU,WAAW,KAAK;AAChC,QAAI,CAAC,KAAK,WAAW,MAAM,SAAS,KAAK,IAAI,CAAC,GAAG;AAC/C,aAAO;AAAA,IACT;AACA,SAAK,UAAU,QAAQ,OAAO;AAC9B,WAAO,QAAQ;AAAA,EACjB;AAAA,EAEQ,OAAO,QAAqB,MAAc,SAAuC;AACvF,WAAO,iBAAiB,MAAM,OAAO;AACrC,SAAK,gBAAgB,KAAK,MAAM,OAAO,oBAAoB,MAAM,OAAO,CAAC;AAAA,EAC3E;AAAA,EAEQ,MAAM,SAAuB;AACnC,QAAI,KAAK,QAAQ,OAAO;AACtB,cAAQ,KAAK,aAAa,OAAO,EAAE;AAAA,IACrC;AAAA,EACF;AACF;AAMO,SAAS,WAAW,OAAmC;AA1P9D;AA2PE,QAAM,UAAwB;AAAA,IAC5B,MAAM;AAAA,IACN,SAAS,MAAM;AAAA,IACf,WAAW,MAAM;AAAA,IACjB,WAAW,eAAe,MAAM,SAAS;AAAA,IACzC,MAAM,EAAE,KAAK,SAAS,SAAS,OAAO,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,aAAa,EAAE;AAAA,EAChF;AACA,MAAI,MAAM,UAAU,gBAAgB;AAClC,YAAQ,UAAU,iBAAiB;AAAA,MACjC,MAAM,UAAU;AAAA,MAChB,OAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI,MAAM,KAAK,OAAO;AACpB,YAAQ,KAAK,QAAQ,SAAS,MAAM,KAAK,OAAO,OAAO,eAAe;AAAA,EACxE;AACA,QAAM,UAAU,oBAAmB,WAAM,YAAN,mBAAe,EAAE;AACpD,UAAQ,UAAU,YAAY,OAAO,OAAO,EAAE,IAAI,QAAQ;AAC1D,QAAM,OAAO,gBAAe,WAAM,SAAN,mBAAY,EAAE;AAC1C,UAAQ,OAAO,SAAS,OAAO,OAAO,EAAE,IAAI,KAAK;AACjD,UAAQ,UAAU,mBAAmB,MAAM,OAAO;AAClD,UAAQ,OAAO,cAAa,WAAM,SAAN,YAAc,CAAC,CAAC;AAC5C,MAAI,MAAM,UAAU;AAClB,YAAQ,WAAW,iBAAiB,MAAM,QAAQ;AAAA,EACpD;AACA,SAAO;AACT;AAGO,SAAS,eAAe,SAA+C;AAC5E,MAAI,CAAC,WAAW,OAAO,QAAQ,WAAW,YAAY,QAAQ,OAAO,WAAW,GAAG;AACjF,WAAO;AAAA,EACT;AACA,aAAW,CAAC,MAAM,KAAK,KAAK;AAAA,IAC1B,CAAC,eAAe,QAAQ,WAAW;AAAA,IACnC,CAAC,eAAe,QAAQ,WAAW;AAAA,EACrC,GAAY;AACV,QACE,OAAO,UAAU,YACjB,MAAM,WAAW,KACjB,MAAM,SAAS,OAAO,qBACtB,CAAC,sBAAsB,KAAK,KAAK,GACjC;AACA,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AACT;;;ACnRA,IAAI,SAA+B;AAO5B,SAAS,KAAK,SAAyC;AAC5D,MAAI,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACpE,WAAO;AAAA,EACT;AACA,QAAM,UAAU,eAAe,OAAO;AACtC,MAAI,YAAY,MAAM;AACpB,YAAQ,MAAM,0BAA0B,OAAO,EAAE;AACjD,WAAO;AAAA,EACT;AACA,MAAI,CAAC,QAAQ,gBAAgB;AAC3B,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,MAAI,WAAW,MAAM;AACnB,SAAK,OAAO,MAAM,CAAC;AAAA,EACrB;AACA,WAAS,IAAI,cAAc,OAAO;AAClC,SAAO,QAAQ;AACf,SAAO;AACT;AAOO,SAAS,iBACd,OACA,SACe;AACf,SAAO,WAAW,OAAO,OAAO,OAAO,QAAQ,OAAO,OAAO;AAC/D;AAGO,SAAS,QAAQ,MAAmC;AACzD,mCAAQ,QAAQ;AAClB;AAQO,SAAS,SAAS,OAA4B;AACnD,mCAAQ,SAAS;AACnB;AAGO,SAAS,OAAO,KAAa,OAAqB;AACvD,mCAAQ,OAAO,KAAK;AACtB;AAGO,SAAS,MAAM,WAAsC;AAC1D,SAAO,WAAW,OAAO,QAAQ,QAAQ,IAAI,IAAI,OAAO,MAAM,SAAS;AACzE;AAGA,eAAsB,MAAM,WAAsC;AAChE,MAAI,WAAW,MAAM;AACnB,WAAO;AAAA,EACT;AACA,QAAM,UAAU;AAChB,WAAS;AACT,SAAO,QAAQ,MAAM,SAAS;AAChC;","names":["init"]}
|
package/dist/vigilon.min.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var Vigilon=(()=>{var k=Object.defineProperty;var
|
|
1
|
+
"use strict";var Vigilon=(()=>{var k=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var ee=Object.prototype.hasOwnProperty;var te=(t,e)=>{for(var n in e)k(t,n,{get:e[n],enumerable:!0})},ne=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Z(e))!ee.call(t,i)&&i!==n&&k(t,i,{get:()=>e[i],enumerable:!(r=Q(e,i))||r.enumerable});return t};var re=t=>ne(k({},"__esModule",{value:!0}),t);var Ve={};te(Ve,{SDK_VERSION:()=>R,captureException:()=>Ue,close:()=>Ne,flush:()=>De,init:()=>Ae,setRoute:()=>Be,setTag:()=>Me,setUser:()=>Oe});var o={eventsPerRequest:20,serviceNameLength:100,serviceVersionLength:200,exceptionTypeLength:256,exceptionMessageLength:8192,exceptionStackLength:65536,exceptionStackLines:250,componentStackLength:16384,causeDepth:5,debugIdEntries:100,debugIdKeyLength:2048,pageUrlLength:2048,pageRouteLength:200,identifierLength:128,tagEntries:20,tagKeyLength:64,tagValueLength:256},O=/^[A-Za-z0-9._:/-]+$/,B="vigilon.browser.event@1";function p(t,e){if(t.length<=e)return t;let n=t.charCodeAt(e-1),r=n>=55296&&n<=56319?e-1:e;return t.slice(0,r)}function M(t){let e=t.split(`
|
|
2
2
|
`),n=e.length>o.exceptionStackLines?e.slice(0,o.exceptionStackLines).join(`
|
|
3
|
-
`):t;return
|
|
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=
|
|
5
|
-
`)){let n=
|
|
3
|
+
`):t;return p(n,o.exceptionStackLength)}var ie=/^([a-z][a-z0-9+.-]*:\/\/)[^/?#@\s]*@/i,se=/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi,oe=/(:\d+:\d+)?([)\]}>"',;.]*)$/;function g(t){let e=t.replace(ie,"$1"),n=e.indexOf("?"),r=e.indexOf("#"),i=n===-1?r:r===-1?n:Math.min(n,r);return i===-1?e:e.slice(0,i)}function f(t){return t.replace(se,e=>{var r,i;let n=(i=(r=oe.exec(e))==null?void 0:r[0])!=null?i:"";return g(e.slice(0,e.length-n.length))+n})}var ae=["_vigilonDebugIds","_sentryDebugIds","_debugIds"],ue=/([a-z][a-z0-9+.-]*:\/\/\S+?):\d+:\d+/i,m=class{constructor(e){this.scope=e;this.indexedEntries=-1;this.byUrl={}}forStacks(e){let n=f(e.filter(Boolean).join(`
|
|
4
|
+
`));if(n.length===0)return;let r;for(let[i,s]of Object.entries(this.registered()))n.includes(`${i}:`)&&(r!=null||(r={}),r[i]=s);return r}registered(){let e=ae.map(i=>this.scope[i]).filter(ce),n=e.reduce((i,s)=>i+Object.keys(s).length,0);if(n===this.indexedEntries)return this.byUrl;let r={};for(let i of e)for(let[s,a]of Object.entries(i)){let c=le(s);c!==null&&typeof a=="string"&&!(c in r)&&(r[c]=a)}return this.indexedEntries=n,this.byUrl=r,r}};function le(t){for(let e of t.split(`
|
|
5
|
+
`)){let n=ue.exec(e);if(n)return g(n[1])}return null}function ce(t){return typeof t=="object"&&t!==null}function N(t,e,n){let i={...C(t),mechanism:e,handled:n},s=pe(t);return s.length>0&&(i.cause=s),i}function V(t){let e={...t,...D(t)};return t.cause&&(e.cause=t.cause.slice(0,o.causeDepth).map(n=>D(n))),e}function D(t){return{type:p(String(t.type),o.exceptionTypeLength),message:p(String(t.message),o.exceptionMessageLength),stack:typeof t.stack=="string"&&t.stack.length>0?M(f(t.stack)):null}}function C(t){return I(t)?{type:typeof t.name=="string"&&t.name?t.name:"Error",message:typeof t.message=="string"?t.message:"",stack:typeof t.stack=="string"?t.stack:null}:{type:"Error",message:de(t),stack:null}}function pe(t){let e=[],n=new Set([t]),r=I(t)?t.cause:void 0;for(;r!=null&&e.length<o.causeDepth&&!n.has(r);)n.add(r),e.push(C(r)),r=I(r)?r.cause:void 0;return e}function I(t){return t instanceof Error||typeof t=="object"&&t!==null&&typeof t.message=="string"&&typeof t.stack=="string"}function de(t){if(typeof t=="string")return t;try{let e=JSON.stringify(t);return e===void 0?String(t):e}catch{return String(t)}}var $=[/^Script error\.?$/,/^(TypeError: )?Failed to fetch$/,/^(TypeError: )?NetworkError when attempting to fetch resource\.?$/,/^(TypeError: )?Load failed$/,/^AbortError\b/,/The (operation|user) aborted a request/,/The operation was aborted/,/^ResizeObserver loop/],q=[/^(chrome|moz|safari|safari-web|ms-browser)-extension:\/\//,/^webkit-masked-url:\/\//],ge=/\b[a-z][a-z0-9+.-]*:\/\/[^\s)]+/i;function E(t,e){return e.some(n=>typeof n=="string"?t.includes(n):n.test(t))}function K(t,e){let{type:n,message:r}=t.exception;return E(r,e)||E(`${n}: ${r}`,e)||n==="AbortError"&&E("AbortError",e)}function F(t,e,n){var i,s;let r=e||((s=ge.exec((i=t.exception.stack)!=null?i:""))==null?void 0:s[0]);return r!==void 0&&E(r,n)}var v=class{constructor(e,n){this.maxEventsPerPage=e;this.duplicateWindowMs=n;this.lastSeen=new Map;this.accepted=0}admit(e,n){if(this.accepted>=this.maxEventsPerPage)return!1;let{type:r,message:i,stack:s}=e.exception,a=`${r}
|
|
6
6
|
${i}
|
|
7
7
|
${(s!=null?s:"").split(`
|
|
8
8
|
`,3).join(`
|
|
9
|
-
`)}`,
|
|
9
|
+
`)}`,c=this.lastSeen.get(a);return c!==void 0&&n-c<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var he=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],fe=/[^\s@]+@[^\s@]+\.[^\s@]+/;function z(t){let e={},n=0;for(let r of Object.keys(t)){if(n===o.tagEntries)break;let i=r.toLowerCase();r.length===0||r.length>o.tagKeyLength||he.some(s=>i.includes(s))||(e[r]=p(String(t[r]),o.tagValueLength),n+=1)}return e}function y(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function T(t){let e=y(t);return e!==null&&fe.test(e)?null:e}var me=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function j(t){let e={},n=0;for(let[r,i]of Object.entries(t)){let s=g(r);if(n===o.debugIdEntries)break;s.length>0&&s.length<=o.debugIdKeyLength&&me.test(String(i))&&(e[s]=String(i),n+=1)}return e}var Ee=1e3,G=240*1024,ve=60*1024,X=512,Y=100,ye=5,be=1800*1e3,we={429:6e4,503:1e4},xe=6e4,b=class{constructor(e){this.options=e;this.queue=[];this.pausedUntil=0;this.timer=null;this.loop=null;this.inFlight=new Set}enqueue(e){if(this.queue.length>=Y){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:Ie(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(Ee)}async flush(e=2e3){this.cancelTimer();let n;return await Promise.race([this.drain().then(()=>Promise.all(this.inFlight)),new Promise(r=>{n=setTimeout(r,e)})]),clearTimeout(n),this.queue.length===0&&this.inFlight.size===0}flushOnUnload(){if(this.cancelTimer(),this.options.now()<this.pausedUntil)return;let e=ve;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=ke(n),this.track(this.post(n,!0))}}close(){this.cancelTimer(),this.queue=[]}schedule(e){if(this.timer!==null||this.queue.length===0)return;let n=Math.max(e,this.pausedUntil-this.options.now());this.timer=setTimeout(()=>{this.timer=null,this.drain()},n)}cancelTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}drain(){return this.loop===null&&(this.loop=this.run().finally(()=>{this.loop=null})),this.loop}async run(){for(;this.queue.length>0&&this.options.now()>=this.pausedUntil;){let e=this.takeBatch(G);if(e.length===0)break;await this.track(this.post(e,!1))}this.schedule(0)}track(e){return this.inFlight.add(e),e.finally(()=>this.inFlight.delete(e))}takeBatch(e){let n=this.options.now();this.queue=this.queue.filter(s=>{let a=s.attempts>=ye||s.firstAttemptAt!==null&&n-s.firstAttemptAt>be;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=X;for(;this.queue.length>0&&r.length<o.eventsPerRequest&&i+this.queue[0].bytes<=e;){let s=this.queue.shift();i+=s.bytes,r.push(s)}return r.length===0&&this.queue.length>0&&e===G&&r.push(this.queue.shift()),r}async post(e,n){var c,d;let r=this.options.now();for(let l of e)l.attempts+=1,(c=l.firstAttemptAt)!=null||(l.firstAttemptAt=r);let i={...this.options.envelope,sentAt:new Date(r).toISOString(),events:e.map(l=>l.event)},s,a=null;try{let l=await this.options.fetch(this.options.url,{method:"POST",headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:n});s=l.status,a=l.headers.get("Retry-After")}catch{this.requeue(e);let l=Math.max(...e.map(h=>h.attempts));this.pause(Math.min(1e3*2**l,xe));return}if(s===429||s===503){this.requeue(e),this.pause((d=Se(a,this.options.now()))!=null?d:we[s]);return}(s<200||s>=300)&&this.options.debug(`request rejected with ${s}; ${e.length} events dropped`)}requeue(e){this.queue.unshift(...e),this.queue.length=Math.min(this.queue.length,Y)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function Se(t,e){if(t===null||t.trim()==="")return null;let n=Number(t);if(Number.isFinite(n))return Math.max(0,n*1e3);let r=Date.parse(t);return Number.isNaN(r)?null:Math.max(0,r-e)}function ke(t){return t.reduce((e,n)=>e+n.bytes,X)}function Ie(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function w(){let t=new Uint8Array(16),e=globalThis.crypto;if(e!=null&&e.getRandomValues)e.getRandomValues(t);else for(let r=0;r<16;r+=1)t[r]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0"));return`${n.slice(0,4).join("")}-${n.slice(4,6).join("")}-${n.slice(6,8).join("")}-${n.slice(8,10).join("")}-${n.slice(10).join("")}`}var H="vigilon.session";function W(){try{let t=window.sessionStorage.getItem(H);if(t)return t;let e=w();return window.sessionStorage.setItem(H,e),e}catch{return null}}var Te="@vigilon/browser",R="0.2.15-beta.pr-7.9",Re="https://ingest.vigilon.io",_e="/v1/browser/events",Le=6e4,x=class{constructor(e){this.options=e;this.tags={};this.debugIds=new m(globalThis);this.userId=null;this.route=null;this.removeListeners=[];var r,i,s,a;let n=((r=e.endpoint)!=null?r:Re).replace(/\/+$/,"");this.transport=new b({url:`${n}${_e}`,apiKey:e.apiKey,envelope:{schema:B,sdk:{name:Te,version:R},service:{name:e.serviceName,environment:e.environment,...e.serviceVersion?{version:p(e.serviceVersion,o.serviceVersionLength)}:{}}},fetch:(c,d)=>window.fetch(c,d),now:()=>Date.now(),debug:c=>this.debug(c)}),this.floodGuard=new v((i=e.maxEventsPerPage)!=null?i:100,Le),this.ignoreErrors=[...$,...(s=e.ignoreErrors)!=null?s:[]],this.denyUrls=[...q,...(a=e.denyUrls)!=null?a:[]]}install(){this.listen(window,"error",e=>{let{error:n,message:r,filename:i}=e;this.capture(n!=null?n:r,{mechanism:"onerror",handled:!1},i)}),this.listen(window,"unhandledrejection",e=>{this.capture(e.reason,{mechanism:"unhandledrejection",handled:!1})}),this.listen(document,"visibilitychange",()=>{document.visibilityState==="hidden"&&this.transport.flushOnUnload()}),this.listen(window,"pagehide",()=>this.transport.flushOnUnload())}capture(e,n={},r){try{return this.captureUnsafe(e,n,r)}catch(i){return this.debug(`capture failed: ${String(i)}`),null}}setUser(e){this.userId=e===null?null:T(e.id),e!==null&&this.userId===null&&this.debug("user.id dropped: it must be an opaque ID of at most 128 characters, not an email address")}setTag(e,n){this.tags[e]=String(n)}setRoute(e){this.route=e===null||e===""?null:String(e)}flush(e){return this.transport.flush(e)}async close(e){for(let r of this.removeListeners.splice(0))r();let n=await this.transport.flush(e);return this.transport.close(),n}captureUnsafe(e,n,r){var _,L,P,A,U;let i=(_=this.options.sampleRate)!=null?_:1;if(i<1&&Math.random()>=i)return null;let s=N(e,(L=n.mechanism)!=null?L:"manual",(P=n.handled)!=null?P:!0);n.componentStack&&(s.componentStack=n.componentStack);let a=this.debugIds.forStacks([s.stack,...((A=s.cause)!=null?A:[]).map(S=>S.stack)]),c=W(),d=(U=n.route)!=null?U:this.route,l={type:"exception",eventId:w(),timestamp:new Date().toISOString(),exception:s,...a?{debugIds:a}:{},page:{url:window.location.href,...d?{route:d}:{}},session:c===null?null:{id:c},user:this.userId===null?null:{id:this.userId},traceId:null,tags:{...this.tags}};if(K(l,this.ignoreErrors)||F(l,r,this.denyUrls))return null;if(this.options.beforeSend){try{l=this.options.beforeSend(l)}catch(S){return this.debug(`beforeSend threw; event dropped: ${String(S)}`),null}if(l===null)return null}let h=Pe(l);return this.floodGuard.admit(h,Date.now())?(this.transport.enqueue(h),h.eventId):null}listen(e,n,r){e.addEventListener(n,r),this.removeListeners.push(()=>e.removeEventListener(n,r))}debug(e){this.options.debug&&console.warn(`[Vigilon] ${e}`)}};function Pe(t){var i,s,a;let e={type:"exception",eventId:t.eventId,timestamp:t.timestamp,exception:V(t.exception),page:{url:p(g(String(t.page.url)),o.pageUrlLength)}};t.exception.componentStack&&(e.exception.componentStack=p(t.exception.componentStack,o.componentStackLength)),t.page.route&&(e.page.route=p(t.page.route,o.pageRouteLength));let n=y((i=t.session)==null?void 0:i.id);e.session=n===null?null:{id:n};let r=T((s=t.user)==null?void 0:s.id);return e.user=r===null?null:{id:r},e.traceId=y(t.traceId),e.tags=z((a=t.tags)!=null?a:{}),t.debugIds&&(e.debugIds=j(t.debugIds)),e}function J(t){if(!t||typeof t.apiKey!="string"||t.apiKey.length===0)return"apiKey is required";for(let[e,n]of[["serviceName",t.serviceName],["environment",t.environment]])if(typeof n!="string"||n.length===0||n.length>o.serviceNameLength||!O.test(n))return`${e} must be 1 to 100 characters of letters, digits, and . _ : / -`;return null}var u=null;function Ae(t){if(typeof window=="undefined"||typeof document=="undefined")return!1;let e=J(t);return e!==null?(console.error(`[Vigilon] not started: ${e}`),!1):(t.serviceVersion||console.warn("[Vigilon] serviceVersion is not set; errors will not be linked to a release"),u!==null&&u.close(0),u=new x(t),u.install(),!0)}function Ue(t,e){return u===null?null:u.capture(t,e)}function Oe(t){u==null||u.setUser(t)}function Be(t){u==null||u.setRoute(t)}function Me(t,e){u==null||u.setTag(t,e)}function De(t){return u===null?Promise.resolve(!0):u.flush(t)}async function Ne(t){if(u===null)return!0;let e=u;return u=null,e.close(t)}return re(Ve);})();
|
|
10
10
|
//# sourceMappingURL=vigilon.min.js.map
|
package/dist/vigilon.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template for this event. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(context.route ? { route: context.route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["src_exports","__export","SDK_VERSION","captureException","close","flush","init","setTag","setUser","LIMITS","SERVICE_TOKEN_PATTERN","SCHEMA_ID","truncate","value","maxLength","code","end","truncateStack","stack","lines","kept","LIMITS","USERINFO","URL_IN_TEXT","TRAILER","scrubUrl","url","query","fragment","cut","scrubUrlsInText","text","token","_a","_b","trailer","REGISTRIES","FRAME_URL","DebugIdIndex","scope","stacks","text","scrubUrlsInText","found","url","id","registries","name","isRegistry","entries","total","registry","byUrl","stack","scriptUrl","line","match","scrubUrl","value","toException","error","mechanism","handled","exception","toErrorParts","cause","toCauseChain","limitException","limited","limitParts","LIMITS","parts","truncate","truncateStack","scrubUrlsInText","isErrorLike","formatValue","chain","seen","value","json","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","FIRST_URL","matchesAny","value","patterns","pattern","isIgnoredError","event","type","message","isDeniedUrl","sourceUrl","_a","_b","url","FloodGuard","maxEventsPerPage","duplicateWindowMs","event","now","type","message","stack","key","previous","DENIED_TAG_KEY_FRAGMENTS","EMAIL_LIKE","sanitizeTags","tags","result","count","key","LIMITS","lowered","fragment","truncate","sanitizeIdentifier","value","sanitizeUserId","id","UUID","sanitizeDebugIds","debugIds","url","scrubUrl","BATCH_DELAY_MS","MAX_BODY_BYTES","KEEPALIVE_BUDGET_BYTES","ENVELOPE_OVERHEAD_BYTES","MAX_QUEUE","MAX_ATTEMPTS","MAX_RETRY_AGE_MS","DEFAULT_RETRY_AFTER_MS","MAX_BACKOFF_MS","Transport","options","event","byteLength","timeoutMs","timeout","resolve","budget","batch","batchBytes","delayMs","wait","request","maxBytes","now","pending","expired","bytes","LIMITS","keepalive","_a","_b","envelope","status","retryAfter","response","attempts","parseRetryAfter","ms","value","seconds","date","total","uuid","bytes","crypto","i","hex","byte","SESSION_KEY","sessionId","existing","created","uuid","SDK_NAME","SDK_VERSION","DEFAULT_ENDPOINT","EVENTS_PATH","DUPLICATE_WINDOW_MS","BrowserClient","options","DebugIdIndex","_a","_b","_c","_d","endpoint","Transport","SCHEMA_ID","truncate","LIMITS","input","init","message","FloodGuard","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","event","error","filename","context","sourceUrl","failure","user","sanitizeUserId","key","value","timeoutMs","remove","flushed","sampleRate","exception","toException","debugIds","cause","session","sessionId","uuid","isIgnoredError","isDeniedUrl","limited","limitEvent","target","type","handler","limitException","scrubUrl","sanitizeIdentifier","sanitizeTags","sanitizeDebugIds","invalidOptions","name","SERVICE_TOKEN_PATTERN","client","init","options","problem","invalidOptions","BrowserClient","captureException","error","context","setUser","user","setTag","key","value","flush","timeoutMs","close","closing"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/limits.ts","../src/core/text.ts","../src/core/debug-ids.ts","../src/core/exception.ts","../src/core/filters.ts","../src/core/flood.ts","../src/core/privacy.ts","../src/core/transport.ts","../src/core/uuid.ts","../src/browser/session.ts","../src/browser/client.ts"],"sourcesContent":["/**\n * @vigilon/browser: error monitoring for browser applications.\n *\n * Importing this module has no side effects, and every function is a safe\n * no-op until `init` runs in a browser, so it can be imported during SSR.\n */\nimport {\n BrowserClient,\n invalidOptions,\n SDK_VERSION,\n type CaptureContext,\n type VigilonBrowserOptions,\n} from \"./browser/client\";\n\nexport type { CaptureContext, VigilonBrowserOptions } from \"./browser/client\";\nexport type {\n BrowserEvent,\n BrowserException,\n ExceptionCause,\n ExceptionMechanism,\n} from \"./core/envelope\";\nexport { SDK_VERSION };\n\nlet client: BrowserClient | null = null;\n\n/**\n * Starts error monitoring: captures uncaught errors and unhandled promise\n * rejections and sends them to Vigilon. Call it once, as early as possible.\n * Returns false when it did not start (not in a browser, or invalid options).\n */\nexport function init(options: VigilonBrowserOptions): boolean {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return false;\n }\n const problem = invalidOptions(options);\n if (problem !== null) {\n console.error(`[Vigilon] not started: ${problem}`);\n return false;\n }\n if (!options.serviceVersion) {\n console.warn(\n \"[Vigilon] serviceVersion is not set; errors will not be linked to a release\",\n );\n }\n if (client !== null) {\n void client.close(0);\n }\n client = new BrowserClient(options);\n client.install();\n return true;\n}\n\n/**\n * Reports an error you caught yourself. Returns the event ID, or null when\n * the error was not sent (not initialised, filtered, sampled out, or dropped\n * by beforeSend).\n */\nexport function captureException(\n error: unknown,\n context?: CaptureContext,\n): string | null {\n return client === null ? null : client.capture(error, context);\n}\n\n/** Attaches an opaque user ID to later events. Pass null to clear it. */\nexport function setUser(user: { id: string } | null): void {\n client?.setUser(user);\n}\n\n/**\n * Sets the route template of the current page, such as `/users/:id`, for\n * later events; pass null to clear it. Vigilon groups pages by it, so it must\n * be a template, never a URL with IDs in it. Without one, Vigilon derives the\n * page from the URL. Router integrations call this on every navigation.\n */\nexport function setRoute(route: string | null): void {\n client?.setRoute(route);\n}\n\n/** Attaches a tag to later events. */\nexport function setTag(key: string, value: string): void {\n client?.setTag(key, value);\n}\n\n/** Sends queued events now. Resolves true when everything was sent. */\nexport function flush(timeoutMs?: number): Promise<boolean> {\n return client === null ? Promise.resolve(true) : client.flush(timeoutMs);\n}\n\n/** Flushes, then stops capturing errors. */\nexport async function close(timeoutMs?: number): Promise<boolean> {\n if (client === null) {\n return true;\n }\n const closing = client;\n client = null;\n return closing.close(timeoutMs);\n}\n","/**\n * The event format's limits. Vigilon enforces them when it receives events;\n * the SDK applies the same caps first so that it truncates rather than being\n * rejected. String lengths count UTF-16 code units.\n */\nexport const LIMITS = {\n eventsPerRequest: 20,\n serviceNameLength: 100,\n serviceVersionLength: 200,\n exceptionTypeLength: 256,\n exceptionMessageLength: 8 * 1024,\n exceptionStackLength: 64 * 1024,\n exceptionStackLines: 250,\n componentStackLength: 16 * 1024,\n causeDepth: 5,\n debugIdEntries: 100,\n debugIdKeyLength: 2 * 1024,\n pageUrlLength: 2 * 1024,\n pageRouteLength: 200,\n identifierLength: 128,\n tagEntries: 20,\n tagKeyLength: 64,\n tagValueLength: 256,\n} as const;\n\n/** service.name and service.environment character set. */\nexport const SERVICE_TOKEN_PATTERN = /^[A-Za-z0-9._:/-]+$/;\n\nexport const SCHEMA_ID = \"vigilon.browser.event@1\";\n","import { LIMITS } from \"./limits\";\n\n/** Truncates to maxLength UTF-16 code units without splitting a surrogate pair. */\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n const code = value.charCodeAt(maxLength - 1);\n const end = code >= 0xd800 && code <= 0xdbff ? maxLength - 1 : maxLength;\n return value.slice(0, end);\n}\n\n/** Truncated from the bottom: line cap first, then length cap. */\nexport function truncateStack(stack: string): string {\n const lines = stack.split(\"\\n\");\n const kept =\n lines.length > LIMITS.exceptionStackLines\n ? lines.slice(0, LIMITS.exceptionStackLines).join(\"\\n\")\n : stack;\n return truncate(kept, LIMITS.exceptionStackLength);\n}\n\n/*\n * The URL scrub: remove the query string, the fragment, and any\n * user:password@ userinfo. Vigilon applies the same scrub again when it\n * receives an event, so a stack scrubbed here is left unchanged there.\n */\nconst USERINFO = /^([a-z][a-z0-9+.-]*:\\/\\/)[^/?#@\\s]*@/i;\n// A URL inside free text: scheme:// up to the next whitespace.\nconst URL_IN_TEXT = /\\b[a-z][a-z0-9+.-]*:\\/\\/\\S+/gi;\n// A stack frame's :line:column and closing punctuation survive the scrub.\nconst TRAILER = /(:\\d+:\\d+)?([)\\]}>\"',;.]*)$/;\n\n/** Scrubs one URL. */\nexport function scrubUrl(value: string): string {\n const url = value.replace(USERINFO, \"$1\");\n const query = url.indexOf(\"?\");\n const fragment = url.indexOf(\"#\");\n const cut =\n query === -1 ? fragment : fragment === -1 ? query : Math.min(query, fragment);\n return cut === -1 ? url : url.slice(0, cut);\n}\n\n/** Scrubs every URL inside free text, keeping a stack frame parseable. */\nexport function scrubUrlsInText(text: string): string {\n return text.replace(URL_IN_TEXT, (token) => {\n const trailer = TRAILER.exec(token)?.[0] ?? \"\";\n return scrubUrl(token.slice(0, token.length - trailer.length)) + trailer;\n });\n}\n","import { scrubUrl, scrubUrlsInText } from \"./text\";\n\n/*\n * Debug IDs link a script to the source map uploaded for it. A build plugin\n * registers each script's ID at runtime in a global registry keyed by the\n * stack of an Error created inside that script, so the key's top frame names\n * the script's URL. Registries are read in order of precedence: Vigilon's\n * own, then Sentry's and the bundler-neutral one, so a build that already\n * registers IDs for another tool needs no second pass.\n */\nconst REGISTRIES = [\"_vigilonDebugIds\", \"_sentryDebugIds\", \"_debugIds\"] as const;\n\n// A frame's script URL: V8's `at url:1:2` and `at fn (url:1:2)`, and\n// SpiderMonkey's and JavaScriptCore's `fn@url:1:2`. The URL may contain\n// parentheses (Next.js route groups), so its end is the `:line:column`.\nconst FRAME_URL = /([a-z][a-z0-9+.-]*:\\/\\/\\S+?):\\d+:\\d+/i;\n\ntype Registry = Record<string, unknown>;\n\nexport class DebugIdIndex {\n private indexedEntries = -1;\n private byUrl: Record<string, string> = {};\n\n constructor(private readonly scope: Record<string, unknown>) {}\n\n /**\n * The debug IDs of the registered scripts that appear in these stacks,\n * keyed by scrubbed script URL, or undefined when none do.\n */\n forStacks(stacks: Array<string | null | undefined>): Record<string, string> | undefined {\n const text = scrubUrlsInText(stacks.filter(Boolean).join(\"\\n\"));\n if (text.length === 0) {\n return undefined;\n }\n let found: Record<string, string> | undefined;\n for (const [url, id] of Object.entries(this.registered())) {\n // The `:` of the frame's line number keeps `a.js` from matching `a.jsx`.\n if (text.includes(`${url}:`)) {\n found ??= {};\n found[url] = id;\n }\n }\n return found;\n }\n\n /** Script URL to debug ID, rebuilt only when a registry has grown. */\n private registered(): Record<string, string> {\n const registries = REGISTRIES.map((name) => this.scope[name]).filter(isRegistry);\n const entries = registries.reduce((total, registry) => total + Object.keys(registry).length, 0);\n if (entries === this.indexedEntries) {\n return this.byUrl;\n }\n const byUrl: Record<string, string> = {};\n for (const registry of registries) {\n for (const [stack, id] of Object.entries(registry)) {\n const url = scriptUrl(stack);\n if (url !== null && typeof id === \"string\" && !(url in byUrl)) {\n byUrl[url] = id;\n }\n }\n }\n this.indexedEntries = entries;\n this.byUrl = byUrl;\n return byUrl;\n }\n}\n\nfunction scriptUrl(stack: string): string | null {\n for (const line of stack.split(\"\\n\")) {\n const match = FRAME_URL.exec(line);\n if (match) {\n return scrubUrl(match[1]);\n }\n }\n return null;\n}\n\nfunction isRegistry(value: unknown): value is Registry {\n return typeof value === \"object\" && value !== null;\n}\n","import type { BrowserException, ExceptionCause, ExceptionMechanism } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\nimport { scrubUrlsInText, truncate, truncateStack } from \"./text\";\n\ninterface ErrorParts {\n type: string;\n message: string;\n stack: string | null;\n}\n\n/**\n * Normalises any thrown value into the event's exception. Non-Error values\n * follow @vigilon/node's toRecordedError: a string is the message, anything\n * else is JSON-serialised, falling back to String().\n */\nexport function toException(\n error: unknown,\n mechanism: ExceptionMechanism,\n handled: boolean,\n): BrowserException {\n const parts = toErrorParts(error);\n const exception: BrowserException = { ...parts, mechanism, handled };\n const cause = toCauseChain(error);\n if (cause.length > 0) {\n exception.cause = cause;\n }\n return exception;\n}\n\n/** Applies the format's caps and the URL scrub. */\nexport function limitException(exception: BrowserException): BrowserException {\n const limited: BrowserException = {\n ...exception,\n ...limitParts(exception),\n };\n if (exception.cause) {\n limited.cause = exception.cause\n .slice(0, LIMITS.causeDepth)\n .map((cause) => limitParts(cause) as ExceptionCause);\n }\n return limited;\n}\n\nfunction limitParts(parts: {\n type: string;\n message: string;\n stack?: string | null;\n}): ErrorParts {\n return {\n type: truncate(String(parts.type), LIMITS.exceptionTypeLength),\n message: truncate(String(parts.message), LIMITS.exceptionMessageLength),\n stack:\n typeof parts.stack === \"string\" && parts.stack.length > 0\n ? truncateStack(scrubUrlsInText(parts.stack))\n : null,\n };\n}\n\nfunction toErrorParts(error: unknown): ErrorParts {\n if (isErrorLike(error)) {\n return {\n type: typeof error.name === \"string\" && error.name ? error.name : \"Error\",\n message: typeof error.message === \"string\" ? error.message : \"\",\n stack: typeof error.stack === \"string\" ? error.stack : null,\n };\n }\n return { type: \"Error\", message: formatValue(error), stack: null };\n}\n\nfunction toCauseChain(error: unknown): ExceptionCause[] {\n const chain: ExceptionCause[] = [];\n const seen = new Set<unknown>([error]);\n let cause = isErrorLike(error) ? error.cause : undefined;\n while (cause !== undefined && cause !== null && chain.length < LIMITS.causeDepth) {\n if (seen.has(cause)) {\n break;\n }\n seen.add(cause);\n chain.push(toErrorParts(cause));\n cause = isErrorLike(cause) ? cause.cause : undefined;\n }\n return chain;\n}\n\ninterface ErrorLike {\n name?: unknown;\n message?: unknown;\n stack?: unknown;\n cause?: unknown;\n}\n\nfunction isErrorLike(value: unknown): value is ErrorLike {\n return (\n value instanceof Error ||\n (typeof value === \"object\" &&\n value !== null &&\n typeof (value as ErrorLike).message === \"string\" &&\n typeof (value as ErrorLike).stack === \"string\")\n );\n}\n\nfunction formatValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n const json = JSON.stringify(value);\n return json === undefined ? String(value) : json;\n } catch {\n return String(value);\n }\n}\n","import type { BrowserEvent } from \"./envelope\";\n\nexport type Pattern = string | RegExp;\n\n/**\n * Noise every application gets and no one can fix: opaque cross-origin\n * errors, failed or aborted network requests, and benign browser warnings.\n * Applications add their own through `ignoreErrors`.\n */\nexport const DEFAULT_IGNORE_ERRORS: Pattern[] = [\n /^Script error\\.?$/,\n /^(TypeError: )?Failed to fetch$/,\n /^(TypeError: )?NetworkError when attempting to fetch resource\\.?$/,\n /^(TypeError: )?Load failed$/,\n /^AbortError\\b/,\n /The (operation|user) aborted a request/,\n /The operation was aborted/,\n /^ResizeObserver loop/,\n];\n\n/** Errors thrown from browser extensions rather than the application. */\nexport const DEFAULT_DENY_URLS: Pattern[] = [\n /^(chrome|moz|safari|safari-web|ms-browser)-extension:\\/\\//,\n /^webkit-masked-url:\\/\\//,\n];\n\nconst FIRST_URL = /\\b[a-z][a-z0-9+.-]*:\\/\\/[^\\s)]+/i;\n\nexport function matchesAny(value: string, patterns: Pattern[]): boolean {\n return patterns.some((pattern) =>\n typeof pattern === \"string\" ? value.includes(pattern) : pattern.test(value),\n );\n}\n\nexport function isIgnoredError(event: BrowserEvent, patterns: Pattern[]): boolean {\n const { type, message } = event.exception;\n return (\n matchesAny(message, patterns) ||\n matchesAny(`${type}: ${message}`, patterns) ||\n (type === \"AbortError\" && matchesAny(\"AbortError\", patterns))\n );\n}\n\n/**\n * The script an error came from: the onerror filename when there is one,\n * else the first URL in the stack, which is the frame where it was thrown.\n */\nexport function isDeniedUrl(\n event: BrowserEvent,\n sourceUrl: string | undefined,\n patterns: Pattern[],\n): boolean {\n const url = sourceUrl || FIRST_URL.exec(event.exception.stack ?? \"\")?.[0];\n return url !== undefined && matchesAny(url, patterns);\n}\n","import type { BrowserEvent } from \"./envelope\";\n\n/**\n * Keeps an error loop from flooding ingestion: the same error is captured at\n * most once per window, and a page sends at most maxEventsPerPage events.\n */\nexport class FloodGuard {\n private readonly lastSeen = new Map<string, number>();\n private accepted = 0;\n\n constructor(\n private readonly maxEventsPerPage: number,\n private readonly duplicateWindowMs: number,\n ) {}\n\n admit(event: BrowserEvent, now: number): boolean {\n if (this.accepted >= this.maxEventsPerPage) {\n return false;\n }\n const { type, message, stack } = event.exception;\n const key = `${type}\\n${message}\\n${(stack ?? \"\").split(\"\\n\", 3).join(\"\\n\")}`;\n const previous = this.lastSeen.get(key);\n if (previous !== undefined && now - previous < this.duplicateWindowMs) {\n return false;\n }\n this.lastSeen.set(key, now);\n this.accepted += 1;\n return true;\n }\n}\n","import { LIMITS } from \"./limits\";\nimport { scrubUrl, truncate } from \"./text\";\n\n// Tag keys naming a credential are dropped. Vigilon drops them again when it\n// receives an event.\nconst DENIED_TAG_KEY_FRAGMENTS = [\n \"password\",\n \"passwd\",\n \"secret\",\n \"token\",\n \"authorization\",\n \"cookie\",\n \"apikey\",\n \"api_key\",\n \"api-key\",\n \"credential\",\n \"private_key\",\n];\n\nconst EMAIL_LIKE = /[^\\s@]+@[^\\s@]+\\.[^\\s@]+/;\n\n/** Applies the deny-list, the entry cap, and the key and value length caps. */\nexport function sanitizeTags(\n tags: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const key of Object.keys(tags)) {\n if (count === LIMITS.tagEntries) {\n break;\n }\n const lowered = key.toLowerCase();\n if (\n key.length === 0 ||\n key.length > LIMITS.tagKeyLength ||\n DENIED_TAG_KEY_FRAGMENTS.some((fragment) => lowered.includes(fragment))\n ) {\n continue;\n }\n result[key] = truncate(String(tags[key]), LIMITS.tagValueLength);\n count += 1;\n }\n return result;\n}\n\n/**\n * An identifier (session, user, trace ID) over the cap is nulled rather than\n * truncated, because a truncated ID would silently join unrelated values.\n */\nexport function sanitizeIdentifier(value: unknown): string | null {\n return typeof value === \"string\" &&\n value.length > 0 &&\n value.length <= LIMITS.identifierLength\n ? value\n : null;\n}\n\n/** user.id must be an opaque ID: anything that looks like an email is dropped. */\nexport function sanitizeUserId(value: unknown): string | null {\n const id = sanitizeIdentifier(value);\n return id !== null && EMAIL_LIKE.test(id) ? null : id;\n}\n\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Script URL to debug ID: scrubbed keys, UUID values, at most 100 entries. */\nexport function sanitizeDebugIds(\n debugIds: Record<string, string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n let count = 0;\n for (const [url, id] of Object.entries(debugIds)) {\n const key = scrubUrl(url);\n if (count === LIMITS.debugIdEntries) {\n break;\n }\n if (key.length > 0 && key.length <= LIMITS.debugIdKeyLength && UUID.test(String(id))) {\n result[key] = String(id);\n count += 1;\n }\n }\n return result;\n}\n","import type { BrowserEvent, BrowserEventEnvelope } from \"./envelope\";\nimport { LIMITS } from \"./limits\";\n\ntype FetchLike = (\n input: string,\n init: {\n method: string;\n headers: Record<string, string>;\n body: string;\n keepalive: boolean;\n },\n) => Promise<{\n status: number;\n headers: { get(name: string): string | null };\n}>;\n\nexport interface TransportOptions {\n url: string;\n apiKey: string;\n envelope: Pick<BrowserEventEnvelope, \"schema\" | \"sdk\" | \"service\">;\n fetch: FetchLike;\n now: () => number;\n debug: (message: string) => void;\n}\n\ninterface Pending {\n event: BrowserEvent;\n bytes: number;\n attempts: number;\n firstAttemptAt: number | null;\n}\n\n/** Wait before sending, so a burst of errors goes out as one request. */\nconst BATCH_DELAY_MS = 1000;\n/** The endpoint rejects bodies over 256 KB; stay clear of it. */\nconst MAX_BODY_BYTES = 240 * 1024;\n/** Browsers allow 64 KB of keepalive bodies in flight per document. */\nconst KEEPALIVE_BUDGET_BYTES = 60 * 1024;\nconst ENVELOPE_OVERHEAD_BYTES = 512;\nconst MAX_QUEUE = 100;\nconst MAX_ATTEMPTS = 5;\n/** An event is dropped this long after its first attempt. */\nconst MAX_RETRY_AGE_MS = 30 * 60 * 1000;\nconst DEFAULT_RETRY_AFTER_MS = { 429: 60_000, 503: 10_000 } as const;\nconst MAX_BACKOFF_MS = 60_000;\n\n/**\n * Batches events into POSTs to the browser event endpoint. Retries only 429,\n * 503, and network failure; a Retry-After pauses every send. An event is\n * dropped after MAX_ATTEMPTS or 30 minutes after its first attempt, so\n * delivery is at-least-once within that bound, never guaranteed.\n */\nexport class Transport {\n private queue: Pending[] = [];\n private pausedUntil = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private loop: Promise<void> | null = null;\n private readonly inFlight = new Set<Promise<void>>();\n\n constructor(private readonly options: TransportOptions) {}\n\n enqueue(event: BrowserEvent): void {\n if (this.queue.length >= MAX_QUEUE) {\n this.options.debug(\"queue full; event dropped\");\n return;\n }\n this.queue.push({\n event,\n bytes: byteLength(JSON.stringify(event)),\n attempts: 0,\n firstAttemptAt: null,\n });\n this.schedule(BATCH_DELAY_MS);\n }\n\n /** Sends everything queued. Resolves true when nothing is left. */\n async flush(timeoutMs = 2000): Promise<boolean> {\n this.cancelTimer();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n this.drain().then(() => Promise.all(this.inFlight)),\n new Promise<void>((resolve) => {\n timeout = setTimeout(resolve, timeoutMs);\n }),\n ]);\n clearTimeout(timeout);\n return this.queue.length === 0 && this.inFlight.size === 0;\n }\n\n /**\n * Called when the page is hidden or unloading: sends what fits in the\n * keepalive budget synchronously, so the requests outlive the page.\n */\n flushOnUnload(): void {\n this.cancelTimer();\n if (this.options.now() < this.pausedUntil) {\n return;\n }\n let budget = KEEPALIVE_BUDGET_BYTES;\n while (this.queue.length > 0) {\n const batch = this.takeBatch(budget);\n if (batch.length === 0) {\n break;\n }\n budget -= batchBytes(batch);\n this.track(this.post(batch, true));\n }\n }\n\n close(): void {\n this.cancelTimer();\n this.queue = [];\n }\n\n private schedule(delayMs: number): void {\n if (this.timer !== null || this.queue.length === 0) {\n return;\n }\n const wait = Math.max(delayMs, this.pausedUntil - this.options.now());\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.drain();\n }, wait);\n }\n\n private cancelTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private drain(): Promise<void> {\n if (this.loop === null) {\n this.loop = this.run().finally(() => {\n this.loop = null;\n });\n }\n return this.loop;\n }\n\n private async run(): Promise<void> {\n while (this.queue.length > 0 && this.options.now() >= this.pausedUntil) {\n const batch = this.takeBatch(MAX_BODY_BYTES);\n if (batch.length === 0) {\n break;\n }\n await this.track(this.post(batch, false));\n }\n this.schedule(0);\n }\n\n private track(request: Promise<void>): Promise<void> {\n this.inFlight.add(request);\n return request.finally(() => this.inFlight.delete(request));\n }\n\n /** Takes up to 20 events that fit maxBytes, dropping expired ones first. */\n private takeBatch(maxBytes: number): Pending[] {\n const now = this.options.now();\n this.queue = this.queue.filter((pending) => {\n const expired =\n pending.attempts >= MAX_ATTEMPTS ||\n (pending.firstAttemptAt !== null &&\n now - pending.firstAttemptAt > MAX_RETRY_AGE_MS);\n if (expired) {\n this.options.debug(`event ${pending.event.eventId} dropped after retries`);\n }\n return !expired;\n });\n const batch: Pending[] = [];\n let bytes = ENVELOPE_OVERHEAD_BYTES;\n while (\n this.queue.length > 0 &&\n batch.length < LIMITS.eventsPerRequest &&\n bytes + this.queue[0].bytes <= maxBytes\n ) {\n const pending = this.queue.shift() as Pending;\n bytes += pending.bytes;\n batch.push(pending);\n }\n // One event larger than the budget still goes out on its own (outside an\n // unload), so it is not stuck at the head of the queue forever.\n if (batch.length === 0 && this.queue.length > 0 && maxBytes === MAX_BODY_BYTES) {\n batch.push(this.queue.shift() as Pending);\n }\n return batch;\n }\n\n private async post(batch: Pending[], keepalive: boolean): Promise<void> {\n const now = this.options.now();\n for (const pending of batch) {\n pending.attempts += 1;\n pending.firstAttemptAt ??= now;\n }\n const envelope: BrowserEventEnvelope = {\n ...this.options.envelope,\n sentAt: new Date(now).toISOString(),\n events: batch.map((pending) => pending.event),\n };\n let status: number;\n let retryAfter: string | null = null;\n try {\n const response = await this.options.fetch(this.options.url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(envelope),\n keepalive,\n });\n status = response.status;\n retryAfter = response.headers.get(\"Retry-After\");\n } catch {\n // A network failure is the one ambiguous outcome: retry with backoff.\n this.requeue(batch);\n const attempts = Math.max(...batch.map((pending) => pending.attempts));\n this.pause(Math.min(1000 * 2 ** attempts, MAX_BACKOFF_MS));\n return;\n }\n if (status === 429 || status === 503) {\n this.requeue(batch);\n this.pause(parseRetryAfter(retryAfter, this.options.now()) ?? DEFAULT_RETRY_AFTER_MS[status]);\n return;\n }\n if (status < 200 || status >= 300) {\n this.options.debug(`request rejected with ${status}; ${batch.length} events dropped`);\n }\n }\n\n private requeue(batch: Pending[]): void {\n this.queue.unshift(...batch);\n this.queue.length = Math.min(this.queue.length, MAX_QUEUE);\n }\n\n private pause(ms: number): void {\n this.pausedUntil = Math.max(this.pausedUntil, this.options.now() + ms);\n this.cancelTimer();\n this.schedule(0);\n }\n}\n\n/** Seconds, or an HTTP date. Unreadable values fall back to the default. */\nexport function parseRetryAfter(value: string | null, now: number): number | null {\n if (value === null || value.trim() === \"\") {\n return null;\n }\n const seconds = Number(value);\n if (Number.isFinite(seconds)) {\n return Math.max(0, seconds * 1000);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, date - now);\n}\n\nfunction batchBytes(batch: Pending[]): number {\n return batch.reduce((total, pending) => total + pending.bytes, ENVELOPE_OVERHEAD_BYTES);\n}\n\nfunction byteLength(value: string): number {\n return typeof TextEncoder === \"undefined\"\n ? value.length * 3\n : new TextEncoder().encode(value).length;\n}\n","/** A random (v4) UUID, without depending on crypto.randomUUID's secure-context rule. */\nexport function uuid(): string {\n const bytes = new Uint8Array(16);\n const crypto = (globalThis as { crypto?: Crypto }).crypto;\n if (crypto?.getRandomValues) {\n crypto.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i += 1) {\n bytes[i] = Math.floor(Math.random() * 256);\n }\n }\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\"));\n return `${hex.slice(0, 4).join(\"\")}-${hex.slice(4, 6).join(\"\")}-${hex\n .slice(6, 8)\n .join(\"\")}-${hex.slice(8, 10).join(\"\")}-${hex.slice(10).join(\"\")}`;\n}\n","import { uuid } from \"../core/uuid\";\n\nconst SESSION_KEY = \"vigilon.session\";\n\n/**\n * A random ID kept in sessionStorage, so one per tab and never a cookie.\n * Without storage (privacy modes, sandboxed iframes) it is null, and each\n * event then counts as its own session.\n */\nexport function sessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_KEY);\n if (existing) {\n return existing;\n }\n const created = uuid();\n window.sessionStorage.setItem(SESSION_KEY, created);\n return created;\n } catch {\n return null;\n }\n}\n","import { DebugIdIndex } from \"../core/debug-ids\";\nimport type { BrowserEvent, ExceptionMechanism } from \"../core/envelope\";\nimport { limitException, toException } from \"../core/exception\";\nimport {\n DEFAULT_DENY_URLS,\n DEFAULT_IGNORE_ERRORS,\n isDeniedUrl,\n isIgnoredError,\n type Pattern,\n} from \"../core/filters\";\nimport { FloodGuard } from \"../core/flood\";\nimport { LIMITS, SCHEMA_ID, SERVICE_TOKEN_PATTERN } from \"../core/limits\";\nimport {\n sanitizeDebugIds,\n sanitizeIdentifier,\n sanitizeTags,\n sanitizeUserId,\n} from \"../core/privacy\";\nimport { scrubUrl, truncate } from \"../core/text\";\nimport { Transport } from \"../core/transport\";\nimport { uuid } from \"../core/uuid\";\nimport { sessionId } from \"./session\";\n\ndeclare const __VIGILON_SDK_VERSION__: string;\n\nexport const SDK_NAME = \"@vigilon/browser\";\nexport const SDK_VERSION = __VIGILON_SDK_VERSION__;\nconst DEFAULT_ENDPOINT = \"https://ingest.vigilon.io\";\nconst EVENTS_PATH = \"/v1/browser/events\";\n\nexport interface VigilonBrowserOptions {\n /** A project API key from the Vigilon dashboard. */\n apiKey: string;\n /** The Service this app reports as. Letters, digits, and `._:/-`, up to 100 characters. */\n serviceName: string;\n /** The deployment environment, for example `production`. Same character set. */\n environment: string;\n /** The release, for example a build number or commit SHA. */\n serviceVersion?: string;\n /** The ingest base URL. Defaults to https://ingest.vigilon.io. */\n endpoint?: string;\n /** Extra errors to drop, matched against the message and against `type: message`. */\n ignoreErrors?: Pattern[];\n /** Extra script URLs whose errors are dropped. */\n denyUrls?: Pattern[];\n /**\n * Inspect, edit, or drop (return null) each event before it is sent. The\n * place to scrub free-text messages, which Vigilon cannot scrub for you.\n */\n beforeSend?: (event: BrowserEvent) => BrowserEvent | null;\n /** Fraction of errors to send, from 0 to 1. Defaults to 1. */\n sampleRate?: number;\n /** At most this many events per page load. Defaults to 100. */\n maxEventsPerPage?: number;\n /** Log SDK diagnostics to the console. */\n debug?: boolean;\n}\n\nexport interface CaptureContext {\n mechanism?: ExceptionMechanism;\n handled?: boolean;\n /** Overrides the route template set with setRoute, for this event only. */\n route?: string;\n componentStack?: string;\n}\n\nconst DUPLICATE_WINDOW_MS = 60_000;\n\nexport class BrowserClient {\n private readonly transport: Transport;\n private readonly floodGuard: FloodGuard;\n private readonly ignoreErrors: Pattern[];\n private readonly denyUrls: Pattern[];\n private readonly tags: Record<string, string> = {};\n private readonly debugIds = new DebugIdIndex(globalThis as unknown as Record<string, unknown>);\n private userId: string | null = null;\n private route: string | null = null;\n private readonly removeListeners: Array<() => void> = [];\n\n constructor(private readonly options: VigilonBrowserOptions) {\n const endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\\/+$/, \"\");\n this.transport = new Transport({\n url: `${endpoint}${EVENTS_PATH}`,\n apiKey: options.apiKey,\n envelope: {\n schema: SCHEMA_ID,\n sdk: { name: SDK_NAME, version: SDK_VERSION },\n service: {\n name: options.serviceName,\n environment: options.environment,\n ...(options.serviceVersion\n ? { version: truncate(options.serviceVersion, LIMITS.serviceVersionLength) }\n : {}),\n },\n },\n fetch: (input, init) => window.fetch(input, init),\n now: () => Date.now(),\n debug: (message) => this.debug(message),\n });\n this.floodGuard = new FloodGuard(\n options.maxEventsPerPage ?? 100,\n DUPLICATE_WINDOW_MS,\n );\n this.ignoreErrors = [...DEFAULT_IGNORE_ERRORS, ...(options.ignoreErrors ?? [])];\n this.denyUrls = [...DEFAULT_DENY_URLS, ...(options.denyUrls ?? [])];\n }\n\n install(): void {\n this.listen(window, \"error\", (event) => {\n const { error, message, filename } = event as ErrorEvent;\n // Without an error object (a cross-origin \"Script error.\"), the message\n // is all there is.\n this.capture(error ?? message, { mechanism: \"onerror\", handled: false }, filename);\n });\n this.listen(window, \"unhandledrejection\", (event) => {\n this.capture((event as PromiseRejectionEvent).reason, {\n mechanism: \"unhandledrejection\",\n handled: false,\n });\n });\n // visibilitychange to hidden is the reliable signal (Firefox has dropped\n // keepalive requests started from pagehide on tab close); pagehide covers\n // browsers that skip it.\n this.listen(document, \"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n this.transport.flushOnUnload();\n }\n });\n this.listen(window, \"pagehide\", () => this.transport.flushOnUnload());\n }\n\n capture(error: unknown, context: CaptureContext = {}, sourceUrl?: string): string | null {\n try {\n return this.captureUnsafe(error, context, sourceUrl);\n } catch (failure) {\n // Never let error reporting throw into the application.\n this.debug(`capture failed: ${String(failure)}`);\n return null;\n }\n }\n\n setUser(user: { id: string } | null): void {\n this.userId = user === null ? null : sanitizeUserId(user.id);\n if (user !== null && this.userId === null) {\n this.debug(\"user.id dropped: it must be an opaque ID of at most 128 characters, not an email address\");\n }\n }\n\n setTag(key: string, value: string): void {\n this.tags[key] = String(value);\n }\n\n setRoute(route: string | null): void {\n this.route = route === null || route === \"\" ? null : String(route);\n }\n\n flush(timeoutMs?: number): Promise<boolean> {\n return this.transport.flush(timeoutMs);\n }\n\n async close(timeoutMs?: number): Promise<boolean> {\n for (const remove of this.removeListeners.splice(0)) {\n remove();\n }\n const flushed = await this.transport.flush(timeoutMs);\n this.transport.close();\n return flushed;\n }\n\n private captureUnsafe(\n error: unknown,\n context: CaptureContext,\n sourceUrl?: string,\n ): string | null {\n const sampleRate = this.options.sampleRate ?? 1;\n if (sampleRate < 1 && Math.random() >= sampleRate) {\n return null;\n }\n const exception = toException(\n error,\n context.mechanism ?? \"manual\",\n context.handled ?? true,\n );\n if (context.componentStack) {\n exception.componentStack = context.componentStack;\n }\n const debugIds = this.debugIds.forStacks([\n exception.stack,\n ...(exception.cause ?? []).map((cause) => cause.stack),\n ]);\n const session = sessionId();\n const route = context.route ?? this.route;\n let event: BrowserEvent | null = {\n type: \"exception\",\n eventId: uuid(),\n timestamp: new Date().toISOString(),\n exception,\n ...(debugIds ? { debugIds } : {}),\n page: {\n url: window.location.href,\n ...(route ? { route } : {}),\n },\n session: session === null ? null : { id: session },\n user: this.userId === null ? null : { id: this.userId },\n traceId: null,\n tags: { ...this.tags },\n };\n if (\n isIgnoredError(event, this.ignoreErrors) ||\n isDeniedUrl(event, sourceUrl, this.denyUrls)\n ) {\n return null;\n }\n if (this.options.beforeSend) {\n try {\n event = this.options.beforeSend(event);\n } catch (failure) {\n // A throwing beforeSend may have been meant to scrub the event, so\n // the event is dropped rather than sent unscrubbed.\n this.debug(`beforeSend threw; event dropped: ${String(failure)}`);\n return null;\n }\n if (event === null) {\n return null;\n }\n }\n const limited = limitEvent(event);\n if (!this.floodGuard.admit(limited, Date.now())) {\n return null;\n }\n this.transport.enqueue(limited);\n return limited.eventId;\n }\n\n private listen(target: EventTarget, type: string, handler: (event: Event) => void): void {\n target.addEventListener(type, handler);\n this.removeListeners.push(() => target.removeEventListener(type, handler));\n }\n\n private debug(message: string): void {\n if (this.options.debug) {\n console.warn(`[Vigilon] ${message}`);\n }\n }\n}\n\n/**\n * Applies the contract's caps and privacy rules last, after beforeSend, so an\n * edited event can never exceed them.\n */\nexport function limitEvent(event: BrowserEvent): BrowserEvent {\n const limited: BrowserEvent = {\n type: \"exception\",\n eventId: event.eventId,\n timestamp: event.timestamp,\n exception: limitException(event.exception),\n page: { url: truncate(scrubUrl(String(event.page.url)), LIMITS.pageUrlLength) },\n };\n if (event.exception.componentStack) {\n limited.exception.componentStack = truncate(\n event.exception.componentStack,\n LIMITS.componentStackLength,\n );\n }\n if (event.page.route) {\n limited.page.route = truncate(event.page.route, LIMITS.pageRouteLength);\n }\n const session = sanitizeIdentifier(event.session?.id);\n limited.session = session === null ? null : { id: session };\n const user = sanitizeUserId(event.user?.id);\n limited.user = user === null ? null : { id: user };\n limited.traceId = sanitizeIdentifier(event.traceId);\n limited.tags = sanitizeTags(event.tags ?? {});\n if (event.debugIds) {\n limited.debugIds = sanitizeDebugIds(event.debugIds);\n }\n return limited;\n}\n\n/** Returns a reason the options cannot be used, or null when they can. */\nexport function invalidOptions(options: VigilonBrowserOptions): string | null {\n if (!options || typeof options.apiKey !== \"string\" || options.apiKey.length === 0) {\n return \"apiKey is required\";\n }\n for (const [name, value] of [\n [\"serviceName\", options.serviceName],\n [\"environment\", options.environment],\n ] as const) {\n if (\n typeof value !== \"string\" ||\n value.length === 0 ||\n value.length > LIMITS.serviceNameLength ||\n !SERVICE_TOKEN_PATTERN.test(value)\n ) {\n return `${name} must be 1 to 100 characters of letters, digits, and . _ : / -`;\n }\n }\n return null;\n}\n"],"mappings":"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","names":["src_exports","__export","SDK_VERSION","captureException","close","flush","init","setRoute","setTag","setUser","LIMITS","SERVICE_TOKEN_PATTERN","SCHEMA_ID","truncate","value","maxLength","code","end","truncateStack","stack","lines","kept","LIMITS","USERINFO","URL_IN_TEXT","TRAILER","scrubUrl","url","query","fragment","cut","scrubUrlsInText","text","token","_a","_b","trailer","REGISTRIES","FRAME_URL","DebugIdIndex","scope","stacks","text","scrubUrlsInText","found","url","id","registries","name","isRegistry","entries","total","registry","byUrl","stack","scriptUrl","line","match","scrubUrl","value","toException","error","mechanism","handled","exception","toErrorParts","cause","toCauseChain","limitException","limited","limitParts","LIMITS","parts","truncate","truncateStack","scrubUrlsInText","isErrorLike","formatValue","chain","seen","value","json","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","FIRST_URL","matchesAny","value","patterns","pattern","isIgnoredError","event","type","message","isDeniedUrl","sourceUrl","_a","_b","url","FloodGuard","maxEventsPerPage","duplicateWindowMs","event","now","type","message","stack","key","previous","DENIED_TAG_KEY_FRAGMENTS","EMAIL_LIKE","sanitizeTags","tags","result","count","key","LIMITS","lowered","fragment","truncate","sanitizeIdentifier","value","sanitizeUserId","id","UUID","sanitizeDebugIds","debugIds","url","scrubUrl","BATCH_DELAY_MS","MAX_BODY_BYTES","KEEPALIVE_BUDGET_BYTES","ENVELOPE_OVERHEAD_BYTES","MAX_QUEUE","MAX_ATTEMPTS","MAX_RETRY_AGE_MS","DEFAULT_RETRY_AFTER_MS","MAX_BACKOFF_MS","Transport","options","event","byteLength","timeoutMs","timeout","resolve","budget","batch","batchBytes","delayMs","wait","request","maxBytes","now","pending","expired","bytes","LIMITS","keepalive","_a","_b","envelope","status","retryAfter","response","attempts","parseRetryAfter","ms","value","seconds","date","total","uuid","bytes","crypto","i","hex","byte","SESSION_KEY","sessionId","existing","created","uuid","SDK_NAME","SDK_VERSION","DEFAULT_ENDPOINT","EVENTS_PATH","DUPLICATE_WINDOW_MS","BrowserClient","options","DebugIdIndex","_a","_b","_c","_d","endpoint","Transport","SCHEMA_ID","truncate","LIMITS","input","init","message","FloodGuard","DEFAULT_IGNORE_ERRORS","DEFAULT_DENY_URLS","event","error","filename","context","sourceUrl","failure","user","sanitizeUserId","key","value","route","timeoutMs","remove","flushed","_e","sampleRate","exception","toException","debugIds","cause","session","sessionId","uuid","isIgnoredError","isDeniedUrl","limited","limitEvent","target","type","handler","limitException","scrubUrl","sanitizeIdentifier","sanitizeTags","sanitizeDebugIds","invalidOptions","name","SERVICE_TOKEN_PATTERN","client","init","options","problem","invalidOptions","BrowserClient","captureException","error","context","setUser","user","setRoute","route","setTag","key","value","flush","timeoutMs","close","closing"]}
|