@vigilon/browser 0.2.13-beta.pr-5.7 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/vigilon.min.js +1 -1
- package/dist/vigilon.min.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -556,7 +556,7 @@ function sessionId() {
|
|
|
556
556
|
|
|
557
557
|
// src/browser/client.ts
|
|
558
558
|
var SDK_NAME = "@vigilon/browser";
|
|
559
|
-
var SDK_VERSION = "0.2.13
|
|
559
|
+
var SDK_VERSION = "0.2.13";
|
|
560
560
|
var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
|
|
561
561
|
var EVENTS_PATH = "/v1/browser/events";
|
|
562
562
|
var DUPLICATE_WINDOW_MS = 6e4;
|
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.13
|
|
527
|
+
var SDK_VERSION = "0.2.13";
|
|
528
528
|
var DEFAULT_ENDPOINT = "https://ingest.vigilon.io";
|
|
529
529
|
var EVENTS_PATH = "/v1/browser/events";
|
|
530
530
|
var DUPLICATE_WINDOW_MS = 6e4;
|
package/dist/vigilon.min.js
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
${i}
|
|
7
7
|
${(s!=null?s:"").split(`
|
|
8
8
|
`,3).join(`
|
|
9
|
-
`)}`,l=this.lastSeen.get(a);return l!==void 0&&n-l<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var de=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],ge=/[^\s@]+@[^\s@]+\.[^\s@]+/;function K(t){let e={},n=0;for(let r of Object.keys(t)){if(n===o.tagEntries)break;let i=r.toLowerCase();r.length===0||r.length>o.tagKeyLength||de.some(s=>i.includes(s))||(e[r]=d(String(t[r]),o.tagValueLength),n+=1)}return e}function y(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function T(t){let e=y(t);return e!==null&&ge.test(e)?null:e}var he=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function F(t){let e={},n=0;for(let[r,i]of Object.entries(t)){let s=g(r);if(n===o.debugIdEntries)break;s.length>0&&s.length<=o.debugIdKeyLength&&he.test(String(i))&&(e[s]=String(i),n+=1)}return e}var fe=1e3,z=240*1024,me=60*1024,G=512,j=100,Ee=5,ve=1800*1e3,ye={429:6e4,503:1e4},be=6e4,b=class{constructor(e){this.options=e;this.queue=[];this.pausedUntil=0;this.timer=null;this.loop=null;this.inFlight=new Set}enqueue(e){if(this.queue.length>=j){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:Se(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(fe)}async flush(e=2e3){this.cancelTimer();let n;return await Promise.race([this.drain().then(()=>Promise.all(this.inFlight)),new Promise(r=>{n=setTimeout(r,e)})]),clearTimeout(n),this.queue.length===0&&this.inFlight.size===0}flushOnUnload(){if(this.cancelTimer(),this.options.now()<this.pausedUntil)return;let e=me;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=xe(n),this.track(this.post(n,!0))}}close(){this.cancelTimer(),this.queue=[]}schedule(e){if(this.timer!==null||this.queue.length===0)return;let n=Math.max(e,this.pausedUntil-this.options.now());this.timer=setTimeout(()=>{this.timer=null,this.drain()},n)}cancelTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}drain(){return this.loop===null&&(this.loop=this.run().finally(()=>{this.loop=null})),this.loop}async run(){for(;this.queue.length>0&&this.options.now()>=this.pausedUntil;){let e=this.takeBatch(z);if(e.length===0)break;await this.track(this.post(e,!1))}this.schedule(0)}track(e){return this.inFlight.add(e),e.finally(()=>this.inFlight.delete(e))}takeBatch(e){let n=this.options.now();this.queue=this.queue.filter(s=>{let a=s.attempts>=Ee||s.firstAttemptAt!==null&&n-s.firstAttemptAt>ve;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=G;for(;this.queue.length>0&&r.length<o.eventsPerRequest&&i+this.queue[0].bytes<=e;){let s=this.queue.shift();i+=s.bytes,r.push(s)}return r.length===0&&this.queue.length>0&&e===z&&r.push(this.queue.shift()),r}async post(e,n){var l,p;let r=this.options.now();for(let c of e)c.attempts+=1,(l=c.firstAttemptAt)!=null||(c.firstAttemptAt=r);let i={...this.options.envelope,sentAt:new Date(r).toISOString(),events:e.map(c=>c.event)},s,a=null;try{let c=await this.options.fetch(this.options.url,{method:"POST",headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:n});s=c.status,a=c.headers.get("Retry-After")}catch{this.requeue(e);let c=Math.max(...e.map(h=>h.attempts));this.pause(Math.min(1e3*2**c,be));return}if(s===429||s===503){this.requeue(e),this.pause((p=we(a,this.options.now()))!=null?p:ye[s]);return}(s<200||s>=300)&&this.options.debug(`request rejected with ${s}; ${e.length} events dropped`)}requeue(e){this.queue.unshift(...e),this.queue.length=Math.min(this.queue.length,j)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function we(t,e){if(t===null||t.trim()==="")return null;let n=Number(t);if(Number.isFinite(n))return Math.max(0,n*1e3);let r=Date.parse(t);return Number.isNaN(r)?null:Math.max(0,r-e)}function xe(t){return t.reduce((e,n)=>e+n.bytes,G)}function Se(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function w(){let t=new Uint8Array(16),e=globalThis.crypto;if(e!=null&&e.getRandomValues)e.getRandomValues(t);else for(let r=0;r<16;r+=1)t[r]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0"));return`${n.slice(0,4).join("")}-${n.slice(4,6).join("")}-${n.slice(6,8).join("")}-${n.slice(8,10).join("")}-${n.slice(10).join("")}`}var Y="vigilon.session";function X(){try{let t=window.sessionStorage.getItem(Y);if(t)return t;let e=w();return window.sessionStorage.setItem(Y,e),e}catch{return null}}var ke="@vigilon/browser",_="0.2.13
|
|
9
|
+
`)}`,l=this.lastSeen.get(a);return l!==void 0&&n-l<this.duplicateWindowMs?!1:(this.lastSeen.set(a,n),this.accepted+=1,!0)}};var de=["password","passwd","secret","token","authorization","cookie","apikey","api_key","api-key","credential","private_key"],ge=/[^\s@]+@[^\s@]+\.[^\s@]+/;function K(t){let e={},n=0;for(let r of Object.keys(t)){if(n===o.tagEntries)break;let i=r.toLowerCase();r.length===0||r.length>o.tagKeyLength||de.some(s=>i.includes(s))||(e[r]=d(String(t[r]),o.tagValueLength),n+=1)}return e}function y(t){return typeof t=="string"&&t.length>0&&t.length<=o.identifierLength?t:null}function T(t){let e=y(t);return e!==null&&ge.test(e)?null:e}var he=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;function F(t){let e={},n=0;for(let[r,i]of Object.entries(t)){let s=g(r);if(n===o.debugIdEntries)break;s.length>0&&s.length<=o.debugIdKeyLength&&he.test(String(i))&&(e[s]=String(i),n+=1)}return e}var fe=1e3,z=240*1024,me=60*1024,G=512,j=100,Ee=5,ve=1800*1e3,ye={429:6e4,503:1e4},be=6e4,b=class{constructor(e){this.options=e;this.queue=[];this.pausedUntil=0;this.timer=null;this.loop=null;this.inFlight=new Set}enqueue(e){if(this.queue.length>=j){this.options.debug("queue full; event dropped");return}this.queue.push({event:e,bytes:Se(JSON.stringify(e)),attempts:0,firstAttemptAt:null}),this.schedule(fe)}async flush(e=2e3){this.cancelTimer();let n;return await Promise.race([this.drain().then(()=>Promise.all(this.inFlight)),new Promise(r=>{n=setTimeout(r,e)})]),clearTimeout(n),this.queue.length===0&&this.inFlight.size===0}flushOnUnload(){if(this.cancelTimer(),this.options.now()<this.pausedUntil)return;let e=me;for(;this.queue.length>0;){let n=this.takeBatch(e);if(n.length===0)break;e-=xe(n),this.track(this.post(n,!0))}}close(){this.cancelTimer(),this.queue=[]}schedule(e){if(this.timer!==null||this.queue.length===0)return;let n=Math.max(e,this.pausedUntil-this.options.now());this.timer=setTimeout(()=>{this.timer=null,this.drain()},n)}cancelTimer(){this.timer!==null&&(clearTimeout(this.timer),this.timer=null)}drain(){return this.loop===null&&(this.loop=this.run().finally(()=>{this.loop=null})),this.loop}async run(){for(;this.queue.length>0&&this.options.now()>=this.pausedUntil;){let e=this.takeBatch(z);if(e.length===0)break;await this.track(this.post(e,!1))}this.schedule(0)}track(e){return this.inFlight.add(e),e.finally(()=>this.inFlight.delete(e))}takeBatch(e){let n=this.options.now();this.queue=this.queue.filter(s=>{let a=s.attempts>=Ee||s.firstAttemptAt!==null&&n-s.firstAttemptAt>ve;return a&&this.options.debug(`event ${s.event.eventId} dropped after retries`),!a});let r=[],i=G;for(;this.queue.length>0&&r.length<o.eventsPerRequest&&i+this.queue[0].bytes<=e;){let s=this.queue.shift();i+=s.bytes,r.push(s)}return r.length===0&&this.queue.length>0&&e===z&&r.push(this.queue.shift()),r}async post(e,n){var l,p;let r=this.options.now();for(let c of e)c.attempts+=1,(l=c.firstAttemptAt)!=null||(c.firstAttemptAt=r);let i={...this.options.envelope,sentAt:new Date(r).toISOString(),events:e.map(c=>c.event)},s,a=null;try{let c=await this.options.fetch(this.options.url,{method:"POST",headers:{Authorization:`Bearer ${this.options.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(i),keepalive:n});s=c.status,a=c.headers.get("Retry-After")}catch{this.requeue(e);let c=Math.max(...e.map(h=>h.attempts));this.pause(Math.min(1e3*2**c,be));return}if(s===429||s===503){this.requeue(e),this.pause((p=we(a,this.options.now()))!=null?p:ye[s]);return}(s<200||s>=300)&&this.options.debug(`request rejected with ${s}; ${e.length} events dropped`)}requeue(e){this.queue.unshift(...e),this.queue.length=Math.min(this.queue.length,j)}pause(e){this.pausedUntil=Math.max(this.pausedUntil,this.options.now()+e),this.cancelTimer(),this.schedule(0)}};function we(t,e){if(t===null||t.trim()==="")return null;let n=Number(t);if(Number.isFinite(n))return Math.max(0,n*1e3);let r=Date.parse(t);return Number.isNaN(r)?null:Math.max(0,r-e)}function xe(t){return t.reduce((e,n)=>e+n.bytes,G)}function Se(t){return typeof TextEncoder=="undefined"?t.length*3:new TextEncoder().encode(t).length}function w(){let t=new Uint8Array(16),e=globalThis.crypto;if(e!=null&&e.getRandomValues)e.getRandomValues(t);else for(let r=0;r<16;r+=1)t[r]=Math.floor(Math.random()*256);t[6]=t[6]&15|64,t[8]=t[8]&63|128;let n=Array.from(t,r=>r.toString(16).padStart(2,"0"));return`${n.slice(0,4).join("")}-${n.slice(4,6).join("")}-${n.slice(6,8).join("")}-${n.slice(8,10).join("")}-${n.slice(10).join("")}`}var Y="vigilon.session";function X(){try{let t=window.sessionStorage.getItem(Y);if(t)return t;let e=w();return window.sessionStorage.setItem(Y,e),e}catch{return null}}var ke="@vigilon/browser",_="0.2.13",Ie="https://ingest.vigilon.io",Te="/v1/browser/events",_e=6e4,x=class{constructor(e){this.options=e;this.tags={};this.debugIds=new m(globalThis);this.userId=null;this.removeListeners=[];var r,i,s,a;let n=((r=e.endpoint)!=null?r:Ie).replace(/\/+$/,"");this.transport=new b({url:`${n}${Te}`,apiKey:e.apiKey,envelope:{schema:U,sdk:{name:ke,version:_},service:{name:e.serviceName,environment:e.environment,...e.serviceVersion?{version:d(e.serviceVersion,o.serviceVersionLength)}:{}}},fetch:(l,p)=>window.fetch(l,p),now:()=>Date.now(),debug:l=>this.debug(l)}),this.floodGuard=new v((i=e.maxEventsPerPage)!=null?i:100,_e),this.ignoreErrors=[...V,...(s=e.ignoreErrors)!=null?s:[]],this.denyUrls=[...C,...(a=e.denyUrls)!=null?a:[]]}install(){this.listen(window,"error",e=>{let{error:n,message:r,filename:i}=e;this.capture(n!=null?n:r,{mechanism:"onerror",handled:!1},i)}),this.listen(window,"unhandledrejection",e=>{this.capture(e.reason,{mechanism:"unhandledrejection",handled:!1})}),this.listen(document,"visibilitychange",()=>{document.visibilityState==="hidden"&&this.transport.flushOnUnload()}),this.listen(window,"pagehide",()=>this.transport.flushOnUnload())}capture(e,n={},r){try{return this.captureUnsafe(e,n,r)}catch(i){return this.debug(`capture failed: ${String(i)}`),null}}setUser(e){this.userId=e===null?null:T(e.id),e!==null&&this.userId===null&&this.debug("user.id dropped: it must be an opaque ID of at most 128 characters, not an email address")}setTag(e,n){this.tags[e]=String(n)}flush(e){return this.transport.flush(e)}async close(e){for(let r of this.removeListeners.splice(0))r();let n=await this.transport.flush(e);return this.transport.close(),n}captureUnsafe(e,n,r){var h,R,L,P;let i=(h=this.options.sampleRate)!=null?h:1;if(i<1&&Math.random()>=i)return null;let s=M(e,(R=n.mechanism)!=null?R:"manual",(L=n.handled)!=null?L:!0);n.componentStack&&(s.componentStack=n.componentStack);let a=this.debugIds.forStacks([s.stack,...((P=s.cause)!=null?P:[]).map(S=>S.stack)]),l=X(),p={type:"exception",eventId:w(),timestamp:new Date().toISOString(),exception:s,...a?{debugIds:a}:{},page:{url:window.location.href,...n.route?{route:n.route}:{}},session:l===null?null:{id:l},user:this.userId===null?null:{id:this.userId},traceId:null,tags:{...this.tags}};if($(p,this.ignoreErrors)||q(p,r,this.denyUrls))return null;if(this.options.beforeSend){try{p=this.options.beforeSend(p)}catch(S){return this.debug(`beforeSend threw; event dropped: ${String(S)}`),null}if(p===null)return null}let c=Re(p);return this.floodGuard.admit(c,Date.now())?(this.transport.enqueue(c),c.eventId):null}listen(e,n,r){e.addEventListener(n,r),this.removeListeners.push(()=>e.removeEventListener(n,r))}debug(e){this.options.debug&&console.warn(`[Vigilon] ${e}`)}};function Re(t){var i,s,a;let e={type:"exception",eventId:t.eventId,timestamp:t.timestamp,exception:D(t.exception),page:{url:d(g(String(t.page.url)),o.pageUrlLength)}};t.exception.componentStack&&(e.exception.componentStack=d(t.exception.componentStack,o.componentStackLength)),t.page.route&&(e.page.route=d(t.page.route,o.pageRouteLength));let n=y((i=t.session)==null?void 0:i.id);e.session=n===null?null:{id:n};let r=T((s=t.user)==null?void 0:s.id);return e.user=r===null?null:{id:r},e.traceId=y(t.traceId),e.tags=K((a=t.tags)!=null?a:{}),t.debugIds&&(e.debugIds=F(t.debugIds)),e}function H(t){if(!t||typeof t.apiKey!="string"||t.apiKey.length===0)return"apiKey is required";for(let[e,n]of[["serviceName",t.serviceName],["environment",t.environment]])if(typeof n!="string"||n.length===0||n.length>o.serviceNameLength||!A.test(n))return`${e} must be 1 to 100 characters of letters, digits, and . _ : / -`;return null}var u=null;function Le(t){if(typeof window=="undefined"||typeof document=="undefined")return!1;let e=H(t);return e!==null?(console.error(`[Vigilon] not started: ${e}`),!1):(t.serviceVersion||console.warn("[Vigilon] serviceVersion is not set; errors will not be linked to a release"),u!==null&&u.close(0),u=new x(t),u.install(),!0)}function Pe(t,e){return u===null?null:u.capture(t,e)}function Ae(t){u==null||u.setUser(t)}function Ue(t,e){u==null||u.setTag(t,e)}function Oe(t){return u===null?Promise.resolve(!0):u.flush(t)}async function Be(t){if(u===null)return!0;let e=u;return u=null,e.close(t)}return te(Me);})();
|
|
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/** 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"]}
|