@upgoose/browser 0.1.1 → 0.1.2

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 CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var u=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var b=(r,e)=>{for(var t in e)u(r,t,{get:e[t],enumerable:!0})},k=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of w(e))!v.call(r,n)&&n!==t&&u(r,n,{get:()=>e[n],enumerable:!(s=y(e,n))||s.enumerable});return r};var E=r=>k(u({},"__esModule",{value:!0}),r);var $={};b($,{Upgoose:()=>l,default:()=>H,upgoose:()=>f});module.exports=E($);var a=[];function d(r,e){r.captureConsole!==!1&&C(e),r.captureClicks!==!1&&U(e),r.captureNavigation!==!1&&L(e),r.captureNetwork!==!1&&(R(e),x(e))}function g(){for(let r of a)try{r()}catch{}a.length=0}function C(r){let e={log:console.log,warn:console.warn,error:console.error};for(let t of["log","warn","error"]){let s=e[t];console[t]=(...n)=>{r({timestamp:Date.now(),category:"console",message:n.map(String).join(" ").slice(0,500),level:t==="error"?"error":t==="warn"?"warning":"info"}),s.apply(console,n)}}a.push(()=>{console.log=e.log,console.warn=e.warn,console.error=e.error})}function U(r){let e=t=>{let s=t.target;if(!s)return;let n=s.tagName?.toLowerCase(),o=s.textContent?.trim().slice(0,100)||"",i=S(s);r({timestamp:Date.now(),category:"ui",message:`click on ${n}${o?`: "${o}"`:""}`,data:{selector:i}})};document.addEventListener("click",e,{capture:!0,passive:!0}),a.push(()=>document.removeEventListener("click",e,{capture:!0}))}function L(r){let e=history.pushState;history.pushState=function(...n){return r({timestamp:Date.now(),category:"navigation",message:String(n[2]||""),data:{from:location.href}}),e.apply(this,n)};let t=history.replaceState;history.replaceState=function(...n){return r({timestamp:Date.now(),category:"navigation",message:String(n[2]||""),data:{from:location.href}}),t.apply(this,n)};let s=()=>{r({timestamp:Date.now(),category:"navigation",message:location.href})};window.addEventListener("popstate",s),a.push(()=>{history.pushState=e,history.replaceState=t,window.removeEventListener("popstate",s)})}function R(r){let e=window.fetch;window.fetch=async function(t,s){let n=typeof t=="string"?t:t instanceof URL?t.href:t.url,o=s?.method||"GET",i=Date.now();if(n.includes("/public/errors"))return e.call(window,t,s);let p=h(n);try{let c=await e.call(window,t,s);return r({timestamp:Date.now(),category:"fetch",message:`${o} ${p}`,level:c.ok?"info":"error",data:{status:c.status,duration:Date.now()-i}}),c}catch(c){throw r({timestamp:Date.now(),category:"fetch",message:`${o} ${p}`,level:"error",data:{error:String(c),duration:Date.now()-i}}),c}},a.push(()=>{window.fetch=e})}function x(r){let e=XMLHttpRequest.prototype.open,t=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(s,n){return this.__ug_method=s,this.__ug_url=String(n),e.apply(this,arguments)},XMLHttpRequest.prototype.send=function(){let s=Date.now(),n=this.__ug_method||"GET",o=this.__ug_url||"";if(o.includes("/public/errors"))return t.apply(this,arguments);let i=h(o);return this.addEventListener("loadend",function(){r({timestamp:Date.now(),category:"xhr",message:`${n} ${i}`,level:this.status>=400?"error":"info",data:{status:this.status,duration:Date.now()-s}})}),t.apply(this,arguments)},a.push(()=>{XMLHttpRequest.prototype.open=e,XMLHttpRequest.prototype.send=t})}function h(r){try{let e=new URL(r,location.origin),t=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let s of[...e.searchParams.keys()])t.some(n=>s.toLowerCase().includes(n))&&e.searchParams.set(s,"[REDACTED]");return e.hash="",e.toString()}catch{return r}}function S(r){let e=[],t=r;for(let s=0;s<3&&t&&t!==document.body;s++){let n=t.tagName.toLowerCase();if(t.id)n+=`#${t.id}`;else if(t.className&&typeof t.className=="string"){let o=t.className.trim().split(/\s+/).slice(0,2).join(".");o&&(n+=`.${o}`)}e.unshift(n),t=t.parentElement}return e.join(" > ")}var B=typeof window<"u"?window.fetch.bind(window):(()=>{});function m(r,e){let t=`${r.apiUrl}/public/errors`,s=JSON.stringify(e);try{B(t,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":r.apiKey},body:s,keepalive:!0}).catch(()=>{})}catch{}}var l=class{constructor(){this.config=null;this.breadcrumbs=[];this.user=null;this.tags={};this.initialized=!1;this.sending=!1;this.onError=e=>{let t=e.error||new Error(e.message||"Unknown error");this.captureException(t)};this.onUnhandledRejection=e=>{let t=e.reason;this.captureException(t)}}init(e){if(this.initialized){console.warn("[Upgoose] Already initialized. Call destroy() first to re-initialize.");return}if(!e.apiKey||!e.apiUrl){console.error("[Upgoose] apiKey and apiUrl are required.");return}this.config={sampleRate:1,maxBreadcrumbs:50,captureConsole:!0,captureClicks:!0,captureNavigation:!0,captureNetwork:!0,captureUnhandled:!0,...e},this.breadcrumbs=[],this.initialized=!0,d(this.config,t=>this.addBreadcrumb(t)),this.config.captureUnhandled&&this.setupGlobalHandlers()}captureException(e,t){if(!this.config)return;let{message:s,stack:n}=P(e);this.send({message:s,stack:n,level:t?.level||"error",tags:t?.tags,fingerprint:t?.fingerprint})}captureMessage(e,t="error"){this.config&&this.send({message:e,level:t})}setUser(e){this.user=e}setTag(e,t){this.tags[e]=t}addBreadcrumb(e){let t=this.config?.maxBreadcrumbs??50;this.breadcrumbs.push(e),this.breadcrumbs.length>t&&(this.breadcrumbs=this.breadcrumbs.slice(-t))}destroy(){g(),this.removeGlobalHandlers(),this.config=null,this.breadcrumbs=[],this.user=null,this.tags={},this.initialized=!1}setupGlobalHandlers(){window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onUnhandledRejection)}removeGlobalHandlers(){window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onUnhandledRejection)}send(e){let t=this.config;if(t&&!this.sending){this.sending=!0;try{if(typeof t.sampleRate=="number"&&Math.random()>t.sampleRate)return;let s={message:e.message.slice(0,2e3),stack:e.stack?.slice(0,1e4),level:e.level,environment:t.environment,release:t.release,tags:{...this.tags,...e.tags},context:{breadcrumbs:this.breadcrumbs.slice(-30),...this.user?{user:this.user}:{},page:{url:_(location.href),title:document.title},browser:D()},fingerprint:e.fingerprint},n=s;if(t.beforeSend)try{n=t.beforeSend(s)}catch{return}if(!n)return;m({apiKey:t.apiKey,apiUrl:t.apiUrl},n)}finally{this.sending=!1}}}};function P(r){return r instanceof Error?{message:r.message,stack:r.stack}:typeof r=="string"?{message:r}:r&&typeof r=="object"&&"message"in r?{message:String(r.message),stack:"stack"in r?String(r.stack):void 0}:{message:String(r)}}function _(r){try{let e=new URL(r),t=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let s of[...e.searchParams.keys()])t.some(n=>s.toLowerCase().includes(n))&&e.searchParams.set(s,"[REDACTED]");return e.hash="",e.toString()}catch{return r}}function D(){let r={};try{r.userAgent=navigator.userAgent,r.language=navigator.language,r.viewport=`${window.innerWidth}x${window.innerHeight}`,r.screen=`${screen.width}x${screen.height}`}catch{}return r}var f=new l,H=f;0&&(module.exports={Upgoose,upgoose});
1
+ "use strict";var u=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var b=(r,e)=>{for(var t in e)u(r,t,{get:e[t],enumerable:!0})},k=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of v(e))!w.call(r,n)&&n!==t&&u(r,n,{get:()=>e[n],enumerable:!(s=y(e,n))||s.enumerable});return r};var E=r=>k(u({},"__esModule",{value:!0}),r);var $={};b($,{Upgoose:()=>l,default:()=>H,upgoose:()=>m});module.exports=E($);var i=[];function d(r,e){r.captureConsole!==!1&&C(e),r.captureClicks!==!1&&U(e),r.captureNavigation!==!1&&L(e),r.captureNetwork!==!1&&(R(e),x(e))}function g(){for(let r of i)try{r()}catch{}i.length=0}function C(r){let e={log:console.log,warn:console.warn,error:console.error};for(let t of["log","warn","error"]){let s=e[t];console[t]=(...n)=>{r({timestamp:Date.now(),category:"console",message:n.map(String).join(" ").slice(0,500),level:t==="error"?"error":t==="warn"?"warning":"info"}),s.apply(console,n)}}i.push(()=>{console.log=e.log,console.warn=e.warn,console.error=e.error})}function U(r){let e=t=>{let s=t.target;if(!s)return;let n=s.tagName?.toLowerCase(),o=s.textContent?.trim().slice(0,100)||"",a=S(s);r({timestamp:Date.now(),category:"ui",message:`click on ${n}${o?`: "${o}"`:""}`,data:{selector:a}})};document.addEventListener("click",e,{capture:!0,passive:!0}),i.push(()=>document.removeEventListener("click",e,{capture:!0}))}function L(r){let e=history.pushState;history.pushState=function(...n){return r({timestamp:Date.now(),category:"navigation",message:String(n[2]||""),data:{from:location.href}}),e.apply(this,n)};let t=history.replaceState;history.replaceState=function(...n){return r({timestamp:Date.now(),category:"navigation",message:String(n[2]||""),data:{from:location.href}}),t.apply(this,n)};let s=()=>{r({timestamp:Date.now(),category:"navigation",message:location.href})};window.addEventListener("popstate",s),i.push(()=>{history.pushState=e,history.replaceState=t,window.removeEventListener("popstate",s)})}function R(r){let e=window.fetch;window.fetch=async function(t,s){let n=typeof t=="string"?t:t instanceof URL?t.href:t.url,o=s?.method||"GET",a=Date.now();if(n.includes("/public/errors"))return e.call(window,t,s);let p=h(n);try{let c=await e.call(window,t,s);return r({timestamp:Date.now(),category:"fetch",message:`${o} ${p}`,level:c.ok?"info":"error",data:{status:c.status,duration:Date.now()-a}}),c}catch(c){throw r({timestamp:Date.now(),category:"fetch",message:`${o} ${p}`,level:"error",data:{error:String(c),duration:Date.now()-a}}),c}},i.push(()=>{window.fetch=e})}function x(r){let e=XMLHttpRequest.prototype.open,t=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(s,n){return this.__ug_method=s,this.__ug_url=String(n),e.apply(this,arguments)},XMLHttpRequest.prototype.send=function(){let s=Date.now(),n=this.__ug_method||"GET",o=this.__ug_url||"";if(o.includes("/public/errors"))return t.apply(this,arguments);let a=h(o);return this.addEventListener("loadend",function(){r({timestamp:Date.now(),category:"xhr",message:`${n} ${a}`,level:this.status>=400?"error":"info",data:{status:this.status,duration:Date.now()-s}})}),t.apply(this,arguments)},i.push(()=>{XMLHttpRequest.prototype.open=e,XMLHttpRequest.prototype.send=t})}function h(r){try{let e=new URL(r,location.origin),t=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let s of[...e.searchParams.keys()])t.some(n=>s.toLowerCase().includes(n))&&e.searchParams.set(s,"[REDACTED]");return e.hash="",e.toString()}catch{return r}}function S(r){let e=[],t=r;for(let s=0;s<3&&t&&t!==document.body;s++){let n=t.tagName.toLowerCase();if(t.id)n+=`#${t.id}`;else if(t.className&&typeof t.className=="string"){let o=t.className.trim().split(/\s+/).slice(0,2).join(".");o&&(n+=`.${o}`)}e.unshift(n),t=t.parentElement}return e.join(" > ")}var _=typeof window<"u"?window.fetch.bind(window):(()=>{});function f(r,e){let t=`${r.apiUrl}/public/errors`,s=JSON.stringify(e);try{_(t,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":r.apiKey},body:s,keepalive:!0}).catch(()=>{})}catch{}}var l=class{constructor(){this.config=null;this.breadcrumbs=[];this.user=null;this.tags={};this.initialized=!1;this.sending=!1;this.onError=e=>{let t=e.error||new Error(e.message||"Unknown error");this.captureException(t)};this.onUnhandledRejection=e=>{let t=e.reason;this.captureException(t)}}init(e){if(this.initialized){console.warn("[Upgoose] Already initialized. Call destroy() first to re-initialize.");return}if(!e.apiKey||!e.apiUrl){console.error("[Upgoose] apiKey and apiUrl are required.");return}this.config={sampleRate:1,maxBreadcrumbs:50,captureConsole:!0,captureClicks:!0,captureNavigation:!0,captureNetwork:!0,captureUnhandled:!0,...e},this.breadcrumbs=[],this.initialized=!0,d(this.config,t=>this.addBreadcrumb(t)),this.config.captureUnhandled&&this.setupGlobalHandlers()}captureException(e,t){if(!this.config)return;let{message:s,stack:n}=B(e);this.send({message:s,stack:n,level:t?.level||"error",tags:t?.tags,fingerprint:t?.fingerprint})}test(){if(!this.config){console.warn("[Upgoose] Not initialized. Call init() first.");return}this.send({message:"Upgoose test event \u2014 integration verified",level:"warning",tags:{upgoose_test:"true"},fingerprint:"upgoose-test-event"}),console.info("[Upgoose] Test event sent. Check your project page.")}captureMessage(e,t="error"){this.config&&this.send({message:e,level:t})}setUser(e){this.user=e}setTag(e,t){this.tags[e]=t}addBreadcrumb(e){let t=this.config?.maxBreadcrumbs??50;this.breadcrumbs.push(e),this.breadcrumbs.length>t&&(this.breadcrumbs=this.breadcrumbs.slice(-t))}destroy(){g(),this.removeGlobalHandlers(),this.config=null,this.breadcrumbs=[],this.user=null,this.tags={},this.initialized=!1}setupGlobalHandlers(){window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onUnhandledRejection)}removeGlobalHandlers(){window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onUnhandledRejection)}send(e){let t=this.config;if(t&&!this.sending){this.sending=!0;try{if(typeof t.sampleRate=="number"&&Math.random()>t.sampleRate)return;let s={message:e.message.slice(0,2e3),stack:e.stack?.slice(0,1e4),level:e.level,environment:t.environment,release:t.release,tags:{...this.tags,...e.tags},context:{breadcrumbs:this.breadcrumbs.slice(-30),...this.user?{user:this.user}:{},page:{url:P(location.href),title:document.title},browser:D()},fingerprint:e.fingerprint},n=s;if(t.beforeSend)try{n=t.beforeSend(s)}catch{return}if(!n)return;f({apiKey:t.apiKey,apiUrl:t.apiUrl},n)}finally{this.sending=!1}}}};function B(r){return r instanceof Error?{message:r.message,stack:r.stack}:typeof r=="string"?{message:r}:r&&typeof r=="object"&&"message"in r?{message:String(r.message),stack:"stack"in r?String(r.stack):void 0}:{message:String(r)}}function P(r){try{let e=new URL(r),t=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let s of[...e.searchParams.keys()])t.some(n=>s.toLowerCase().includes(n))&&e.searchParams.set(s,"[REDACTED]");return e.hash="",e.toString()}catch{return r}}function D(){let r={};try{r.userAgent=navigator.userAgent,r.language=navigator.language,r.viewport=`${window.innerWidth}x${window.innerHeight}`,r.screen=`${screen.width}x${screen.height}`}catch{}return r}var m=new l,H=m;0&&(module.exports={Upgoose,upgoose});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/breadcrumbs.ts","../src/transport.ts"],"sourcesContent":["import type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n} from \"./types\";\nimport { setupBreadcrumbs, teardownBreadcrumbs } from \"./breadcrumbs\";\nimport { sendEvent } from \"./transport\";\n\nexport type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n};\n\nclass Upgoose {\n private config: UpgooseConfig | null = null;\n private breadcrumbs: Breadcrumb[] = [];\n private user: UserContext | null = null;\n private tags: Record<string, string> = {};\n private initialized = false;\n private sending = false; // re-entrancy guard\n\n /**\n * Initialize the Upgoose SDK. Call this once, as early as possible.\n *\n * ```ts\n * import upgoose from '@upgoose/browser';\n * upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });\n * ```\n */\n init(config: UpgooseConfig): void {\n if (this.initialized) {\n console.warn(\"[Upgoose] Already initialized. Call destroy() first to re-initialize.\");\n return;\n }\n\n if (!config.apiKey || !config.apiUrl) {\n console.error(\"[Upgoose] apiKey and apiUrl are required.\");\n return;\n }\n\n this.config = {\n sampleRate: 1,\n maxBreadcrumbs: 50,\n captureConsole: true,\n captureClicks: true,\n captureNavigation: true,\n captureNetwork: true,\n captureUnhandled: true,\n ...config,\n };\n\n this.breadcrumbs = [];\n this.initialized = true;\n\n // Set up breadcrumb collection\n setupBreadcrumbs(this.config, (crumb) => this.addBreadcrumb(crumb));\n\n // Set up global error handlers\n if (this.config.captureUnhandled) {\n this.setupGlobalHandlers();\n }\n }\n\n /**\n * Capture an exception manually.\n *\n * ```ts\n * try {\n * riskyOperation();\n * } catch (err) {\n * upgoose.captureException(err);\n * }\n * ```\n */\n captureException(\n error: unknown,\n extra?: { tags?: Record<string, string>; fingerprint?: string; level?: ErrorPayload[\"level\"] }\n ): void {\n if (!this.config) return;\n\n const { message, stack } = normalizeError(error);\n this.send({\n message,\n stack,\n level: extra?.level || \"error\",\n tags: extra?.tags,\n fingerprint: extra?.fingerprint,\n });\n }\n\n /**\n * Capture a message as an error event.\n *\n * ```ts\n * upgoose.captureMessage(\"User hit rate limit\", \"warning\");\n * ```\n */\n captureMessage(\n message: string,\n level: ErrorPayload[\"level\"] = \"error\"\n ): void {\n if (!this.config) return;\n this.send({ message, level });\n }\n\n /**\n * Set user context. Attached to all subsequent events.\n *\n * ```ts\n * upgoose.setUser({ id: '123', email: 'user@example.com' });\n * ```\n */\n setUser(user: UserContext | null): void {\n this.user = user;\n }\n\n /**\n * Set a global tag. Attached to all subsequent events.\n */\n setTag(key: string, value: string): void {\n this.tags[key] = value;\n }\n\n /**\n * Add a breadcrumb manually.\n */\n addBreadcrumb(crumb: Breadcrumb): void {\n const max = this.config?.maxBreadcrumbs ?? 50;\n this.breadcrumbs.push(crumb);\n if (this.breadcrumbs.length > max) {\n this.breadcrumbs = this.breadcrumbs.slice(-max);\n }\n }\n\n /**\n * Tear down the SDK — removes global handlers and breadcrumb instrumentation.\n */\n destroy(): void {\n teardownBreadcrumbs();\n this.removeGlobalHandlers();\n this.config = null;\n this.breadcrumbs = [];\n this.user = null;\n this.tags = {};\n this.initialized = false;\n }\n\n // -- Private --\n\n private onError = (event: ErrorEvent): void => {\n const error = event.error || new Error(event.message || \"Unknown error\");\n this.captureException(error);\n };\n\n private onUnhandledRejection = (event: PromiseRejectionEvent): void => {\n const error = event.reason;\n this.captureException(error);\n };\n\n private setupGlobalHandlers(): void {\n window.addEventListener(\"error\", this.onError);\n window.addEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private removeGlobalHandlers(): void {\n window.removeEventListener(\"error\", this.onError);\n window.removeEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private send(partial: {\n message: string;\n stack?: string;\n level: ErrorPayload[\"level\"];\n tags?: Record<string, string>;\n fingerprint?: string;\n }): void {\n const config = this.config;\n if (!config) return;\n\n // Re-entrancy guard: prevent infinite loops if send() itself throws\n // or if beforeSend throws and triggers the global error handler\n if (this.sending) return;\n this.sending = true;\n\n try {\n // Sample rate check\n if (typeof config.sampleRate === \"number\" && Math.random() > config.sampleRate) {\n return;\n }\n\n const payload: ErrorPayload = {\n message: partial.message.slice(0, 2000),\n stack: partial.stack?.slice(0, 10000),\n level: partial.level,\n environment: config.environment,\n release: config.release,\n tags: { ...this.tags, ...partial.tags },\n context: {\n breadcrumbs: this.breadcrumbs.slice(-30),\n ...(this.user ? { user: this.user } : {}),\n page: {\n url: stripUrlSensitiveParams(location.href),\n title: document.title,\n },\n browser: getBrowserContext(),\n },\n fingerprint: partial.fingerprint,\n };\n\n // beforeSend hook (wrapped to prevent throws from looping)\n let processed: ErrorPayload | null = payload;\n if (config.beforeSend) {\n try {\n processed = config.beforeSend(payload);\n } catch {\n // Drop event if beforeSend throws\n return;\n }\n }\n if (!processed) return;\n\n sendEvent(\n { apiKey: config.apiKey, apiUrl: config.apiUrl },\n processed\n );\n } finally {\n this.sending = false;\n }\n }\n}\n\nfunction normalizeError(error: unknown): { message: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, stack: error.stack };\n }\n if (typeof error === \"string\") {\n return { message: error };\n }\n if (error && typeof error === \"object\" && \"message\" in error) {\n return {\n message: String((error as { message: unknown }).message),\n stack: \"stack\" in error ? String((error as { stack: unknown }).stack) : undefined,\n };\n }\n return { message: String(error) };\n}\n\n/** Strip common sensitive query/hash params from URLs */\nfunction stripUrlSensitiveParams(url: string): string {\n try {\n const u = new URL(url);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n // Strip hash (often contains tokens in SPAs)\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getBrowserContext(): Record<string, string> {\n const ctx: Record<string, string> = {};\n try {\n ctx.userAgent = navigator.userAgent;\n ctx.language = navigator.language;\n ctx.viewport = `${window.innerWidth}x${window.innerHeight}`;\n ctx.screen = `${screen.width}x${screen.height}`;\n } catch {\n // Ignore\n }\n return ctx;\n}\n\n// Singleton instance\nconst upgoose = new Upgoose();\nexport default upgoose;\nexport { upgoose, Upgoose };\n","import type { Breadcrumb, UpgooseConfig } from \"./types\";\n\ntype BreadcrumbCallback = (crumb: Breadcrumb) => void;\n\nconst teardowns: Array<() => void> = [];\n\nexport function setupBreadcrumbs(\n config: UpgooseConfig,\n addBreadcrumb: BreadcrumbCallback\n): void {\n if (config.captureConsole !== false) {\n setupConsole(addBreadcrumb);\n }\n if (config.captureClicks !== false) {\n setupClicks(addBreadcrumb);\n }\n if (config.captureNavigation !== false) {\n setupNavigation(addBreadcrumb);\n }\n if (config.captureNetwork !== false) {\n setupFetch(addBreadcrumb);\n setupXHR(addBreadcrumb);\n }\n}\n\nexport function teardownBreadcrumbs(): void {\n for (const fn of teardowns) {\n try {\n fn();\n } catch {\n // Best effort cleanup\n }\n }\n teardowns.length = 0;\n}\n\nfunction setupConsole(add: BreadcrumbCallback): void {\n const original = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n };\n\n for (const level of [\"log\", \"warn\", \"error\"] as const) {\n const orig = original[level];\n console[level] = (...args: unknown[]) => {\n add({\n timestamp: Date.now(),\n category: \"console\",\n message: args.map(String).join(\" \").slice(0, 500),\n level: level === \"error\" ? \"error\" : level === \"warn\" ? \"warning\" : \"info\",\n });\n orig.apply(console, args);\n };\n }\n\n teardowns.push(() => {\n console.log = original.log;\n console.warn = original.warn;\n console.error = original.error;\n });\n}\n\nfunction setupClicks(add: BreadcrumbCallback): void {\n const handler = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (!target) return;\n\n const tag = target.tagName?.toLowerCase();\n const text = target.textContent?.trim().slice(0, 100) || \"\";\n const selector = getSelector(target);\n\n add({\n timestamp: Date.now(),\n category: \"ui\",\n message: `click on ${tag}${text ? `: \"${text}\"` : \"\"}`,\n data: { selector },\n });\n };\n\n document.addEventListener(\"click\", handler, { capture: true, passive: true });\n teardowns.push(() => document.removeEventListener(\"click\", handler, { capture: true }));\n}\n\nfunction setupNavigation(add: BreadcrumbCallback): void {\n // pushState\n const origPush = history.pushState;\n history.pushState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origPush.apply(this, args);\n };\n\n // replaceState\n const origReplace = history.replaceState;\n history.replaceState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origReplace.apply(this, args);\n };\n\n // popstate (back/forward)\n const popHandler = () => {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: location.href,\n });\n };\n window.addEventListener(\"popstate\", popHandler);\n\n teardowns.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener(\"popstate\", popHandler);\n });\n}\n\nfunction setupFetch(add: BreadcrumbCallback): void {\n const origFetch = window.fetch;\n\n window.fetch = async function (input, init?) {\n const rawUrl = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = init?.method || \"GET\";\n const start = Date.now();\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origFetch.call(window, input, init);\n }\n\n const url = scrubUrl(rawUrl);\n\n try {\n const response = await origFetch.call(window, input, init);\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: response.ok ? \"info\" : \"error\",\n data: {\n status: response.status,\n duration: Date.now() - start,\n },\n });\n return response;\n } catch (err) {\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: \"error\",\n data: {\n error: String(err),\n duration: Date.now() - start,\n },\n });\n throw err;\n }\n };\n\n teardowns.push(() => {\n window.fetch = origFetch;\n });\n}\n\nfunction setupXHR(add: BreadcrumbCallback): void {\n const origOpen = XMLHttpRequest.prototype.open;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL) {\n (this as any).__ug_method = method;\n (this as any).__ug_url = String(url);\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as any);\n };\n\n XMLHttpRequest.prototype.send = function () {\n const start = Date.now();\n const method = (this as any).__ug_method || \"GET\";\n const rawUrl: string = (this as any).__ug_url || \"\";\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origSend.apply(this, arguments as any);\n }\n\n const url = scrubUrl(rawUrl);\n\n this.addEventListener(\"loadend\", function () {\n add({\n timestamp: Date.now(),\n category: \"xhr\",\n message: `${method} ${url}`,\n level: this.status >= 400 ? \"error\" : \"info\",\n data: {\n status: this.status,\n duration: Date.now() - start,\n },\n });\n });\n\n // eslint-disable-next-line prefer-rest-params\n return origSend.apply(this, arguments as any);\n };\n\n teardowns.push(() => {\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.send = origSend;\n });\n}\n\n/** Strip sensitive query params from URLs in breadcrumbs */\nfunction scrubUrl(url: string): string {\n try {\n const u = new URL(url, location.origin);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getSelector(el: HTMLElement): string {\n const parts: string[] = [];\n let current: HTMLElement | null = el;\n\n for (let i = 0; i < 3 && current && current !== document.body; i++) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n part += `#${current.id}`;\n } else if (current.className && typeof current.className === \"string\") {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join(\".\");\n if (cls) part += `.${cls}`;\n }\n parts.unshift(part);\n current = current.parentElement;\n }\n\n return parts.join(\" > \");\n}\n","import type { ErrorPayload } from \"./types\";\n\n// Capture the original fetch before any instrumentation wraps it.\n// This prevents SDK requests from appearing in breadcrumbs and\n// avoids recursive loops if the fetch wrapper itself errors.\nconst originalFetch: typeof fetch =\n typeof window !== \"undefined\" ? window.fetch.bind(window) : ((() => {}) as any);\n\nexport interface TransportConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n/**\n * Must be called BEFORE setupBreadcrumbs() to capture the\n * un-instrumented fetch reference.\n */\nexport function captureOriginalFetch(): void {\n // Already captured at module load time above.\n // This function exists as a documentation marker.\n}\n\nexport function sendEvent(\n config: TransportConfig,\n payload: ErrorPayload\n): void {\n const url = `${config.apiUrl}/public/errors`;\n const body = JSON.stringify(payload);\n\n try {\n originalFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": config.apiKey,\n },\n body,\n keepalive: true, // Survives page unload\n }).catch(() => {\n // Silently fail — never impact user's app\n });\n } catch {\n // Silently fail\n }\n}\n"],"mappings":"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","names":["index_exports","__export","Upgoose","index_default","upgoose","__toCommonJS","teardowns","setupBreadcrumbs","config","addBreadcrumb","setupConsole","setupClicks","setupNavigation","setupFetch","setupXHR","teardownBreadcrumbs","fn","add","original","level","orig","args","handler","event","target","tag","text","selector","getSelector","origPush","origReplace","popHandler","origFetch","input","init","rawUrl","method","start","url","scrubUrl","response","err","origOpen","origSend","u","sensitive","param","s","el","parts","current","i","part","cls","originalFetch","sendEvent","config","payload","url","body","originalFetch","Upgoose","event","error","config","setupBreadcrumbs","crumb","extra","message","stack","normalizeError","level","user","key","value","max","teardownBreadcrumbs","partial","payload","stripUrlSensitiveParams","getBrowserContext","processed","sendEvent","url","u","sensitive","param","s","ctx","upgoose","index_default"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/breadcrumbs.ts","../src/transport.ts"],"sourcesContent":["import type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n} from \"./types\";\nimport { setupBreadcrumbs, teardownBreadcrumbs } from \"./breadcrumbs\";\nimport { sendEvent } from \"./transport\";\n\nexport type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n};\n\nclass Upgoose {\n private config: UpgooseConfig | null = null;\n private breadcrumbs: Breadcrumb[] = [];\n private user: UserContext | null = null;\n private tags: Record<string, string> = {};\n private initialized = false;\n private sending = false; // re-entrancy guard\n\n /**\n * Initialize the Upgoose SDK. Call this once, as early as possible.\n *\n * ```ts\n * import upgoose from '@upgoose/browser';\n * upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });\n * ```\n */\n init(config: UpgooseConfig): void {\n if (this.initialized) {\n console.warn(\"[Upgoose] Already initialized. Call destroy() first to re-initialize.\");\n return;\n }\n\n if (!config.apiKey || !config.apiUrl) {\n console.error(\"[Upgoose] apiKey and apiUrl are required.\");\n return;\n }\n\n this.config = {\n sampleRate: 1,\n maxBreadcrumbs: 50,\n captureConsole: true,\n captureClicks: true,\n captureNavigation: true,\n captureNetwork: true,\n captureUnhandled: true,\n ...config,\n };\n\n this.breadcrumbs = [];\n this.initialized = true;\n\n // Set up breadcrumb collection\n setupBreadcrumbs(this.config, (crumb) => this.addBreadcrumb(crumb));\n\n // Set up global error handlers\n if (this.config.captureUnhandled) {\n this.setupGlobalHandlers();\n }\n }\n\n /**\n * Capture an exception manually.\n *\n * ```ts\n * try {\n * riskyOperation();\n * } catch (err) {\n * upgoose.captureException(err);\n * }\n * ```\n */\n captureException(\n error: unknown,\n extra?: { tags?: Record<string, string>; fingerprint?: string; level?: ErrorPayload[\"level\"] }\n ): void {\n if (!this.config) return;\n\n const { message, stack } = normalizeError(error);\n this.send({\n message,\n stack,\n level: extra?.level || \"error\",\n tags: extra?.tags,\n fingerprint: extra?.fingerprint,\n });\n }\n\n /**\n * Send a test event to verify your integration is working.\n * Check your project page — the status should flip to \"CONNECTED\" within seconds.\n *\n * ```ts\n * upgoose.test();\n * ```\n */\n test(): void {\n if (!this.config) {\n console.warn(\"[Upgoose] Not initialized. Call init() first.\");\n return;\n }\n this.send({\n message: \"Upgoose test event — integration verified\",\n level: \"warning\",\n tags: { upgoose_test: \"true\" },\n fingerprint: \"upgoose-test-event\",\n });\n console.info(\"[Upgoose] Test event sent. Check your project page.\");\n }\n\n /**\n * Capture a message as an error event.\n *\n * ```ts\n * upgoose.captureMessage(\"User hit rate limit\", \"warning\");\n * ```\n */\n captureMessage(\n message: string,\n level: ErrorPayload[\"level\"] = \"error\"\n ): void {\n if (!this.config) return;\n this.send({ message, level });\n }\n\n /**\n * Set user context. Attached to all subsequent events.\n *\n * ```ts\n * upgoose.setUser({ id: '123', email: 'user@example.com' });\n * ```\n */\n setUser(user: UserContext | null): void {\n this.user = user;\n }\n\n /**\n * Set a global tag. Attached to all subsequent events.\n */\n setTag(key: string, value: string): void {\n this.tags[key] = value;\n }\n\n /**\n * Add a breadcrumb manually.\n */\n addBreadcrumb(crumb: Breadcrumb): void {\n const max = this.config?.maxBreadcrumbs ?? 50;\n this.breadcrumbs.push(crumb);\n if (this.breadcrumbs.length > max) {\n this.breadcrumbs = this.breadcrumbs.slice(-max);\n }\n }\n\n /**\n * Tear down the SDK — removes global handlers and breadcrumb instrumentation.\n */\n destroy(): void {\n teardownBreadcrumbs();\n this.removeGlobalHandlers();\n this.config = null;\n this.breadcrumbs = [];\n this.user = null;\n this.tags = {};\n this.initialized = false;\n }\n\n // -- Private --\n\n private onError = (event: ErrorEvent): void => {\n const error = event.error || new Error(event.message || \"Unknown error\");\n this.captureException(error);\n };\n\n private onUnhandledRejection = (event: PromiseRejectionEvent): void => {\n const error = event.reason;\n this.captureException(error);\n };\n\n private setupGlobalHandlers(): void {\n window.addEventListener(\"error\", this.onError);\n window.addEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private removeGlobalHandlers(): void {\n window.removeEventListener(\"error\", this.onError);\n window.removeEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private send(partial: {\n message: string;\n stack?: string;\n level: ErrorPayload[\"level\"];\n tags?: Record<string, string>;\n fingerprint?: string;\n }): void {\n const config = this.config;\n if (!config) return;\n\n // Re-entrancy guard: prevent infinite loops if send() itself throws\n // or if beforeSend throws and triggers the global error handler\n if (this.sending) return;\n this.sending = true;\n\n try {\n // Sample rate check\n if (typeof config.sampleRate === \"number\" && Math.random() > config.sampleRate) {\n return;\n }\n\n const payload: ErrorPayload = {\n message: partial.message.slice(0, 2000),\n stack: partial.stack?.slice(0, 10000),\n level: partial.level,\n environment: config.environment,\n release: config.release,\n tags: { ...this.tags, ...partial.tags },\n context: {\n breadcrumbs: this.breadcrumbs.slice(-30),\n ...(this.user ? { user: this.user } : {}),\n page: {\n url: stripUrlSensitiveParams(location.href),\n title: document.title,\n },\n browser: getBrowserContext(),\n },\n fingerprint: partial.fingerprint,\n };\n\n // beforeSend hook (wrapped to prevent throws from looping)\n let processed: ErrorPayload | null = payload;\n if (config.beforeSend) {\n try {\n processed = config.beforeSend(payload);\n } catch {\n // Drop event if beforeSend throws\n return;\n }\n }\n if (!processed) return;\n\n sendEvent(\n { apiKey: config.apiKey, apiUrl: config.apiUrl },\n processed\n );\n } finally {\n this.sending = false;\n }\n }\n}\n\nfunction normalizeError(error: unknown): { message: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, stack: error.stack };\n }\n if (typeof error === \"string\") {\n return { message: error };\n }\n if (error && typeof error === \"object\" && \"message\" in error) {\n return {\n message: String((error as { message: unknown }).message),\n stack: \"stack\" in error ? String((error as { stack: unknown }).stack) : undefined,\n };\n }\n return { message: String(error) };\n}\n\n/** Strip common sensitive query/hash params from URLs */\nfunction stripUrlSensitiveParams(url: string): string {\n try {\n const u = new URL(url);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n // Strip hash (often contains tokens in SPAs)\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getBrowserContext(): Record<string, string> {\n const ctx: Record<string, string> = {};\n try {\n ctx.userAgent = navigator.userAgent;\n ctx.language = navigator.language;\n ctx.viewport = `${window.innerWidth}x${window.innerHeight}`;\n ctx.screen = `${screen.width}x${screen.height}`;\n } catch {\n // Ignore\n }\n return ctx;\n}\n\n// Singleton instance\nconst upgoose = new Upgoose();\nexport default upgoose;\nexport { upgoose, Upgoose };\n","import type { Breadcrumb, UpgooseConfig } from \"./types\";\n\ntype BreadcrumbCallback = (crumb: Breadcrumb) => void;\n\nconst teardowns: Array<() => void> = [];\n\nexport function setupBreadcrumbs(\n config: UpgooseConfig,\n addBreadcrumb: BreadcrumbCallback\n): void {\n if (config.captureConsole !== false) {\n setupConsole(addBreadcrumb);\n }\n if (config.captureClicks !== false) {\n setupClicks(addBreadcrumb);\n }\n if (config.captureNavigation !== false) {\n setupNavigation(addBreadcrumb);\n }\n if (config.captureNetwork !== false) {\n setupFetch(addBreadcrumb);\n setupXHR(addBreadcrumb);\n }\n}\n\nexport function teardownBreadcrumbs(): void {\n for (const fn of teardowns) {\n try {\n fn();\n } catch {\n // Best effort cleanup\n }\n }\n teardowns.length = 0;\n}\n\nfunction setupConsole(add: BreadcrumbCallback): void {\n const original = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n };\n\n for (const level of [\"log\", \"warn\", \"error\"] as const) {\n const orig = original[level];\n console[level] = (...args: unknown[]) => {\n add({\n timestamp: Date.now(),\n category: \"console\",\n message: args.map(String).join(\" \").slice(0, 500),\n level: level === \"error\" ? \"error\" : level === \"warn\" ? \"warning\" : \"info\",\n });\n orig.apply(console, args);\n };\n }\n\n teardowns.push(() => {\n console.log = original.log;\n console.warn = original.warn;\n console.error = original.error;\n });\n}\n\nfunction setupClicks(add: BreadcrumbCallback): void {\n const handler = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (!target) return;\n\n const tag = target.tagName?.toLowerCase();\n const text = target.textContent?.trim().slice(0, 100) || \"\";\n const selector = getSelector(target);\n\n add({\n timestamp: Date.now(),\n category: \"ui\",\n message: `click on ${tag}${text ? `: \"${text}\"` : \"\"}`,\n data: { selector },\n });\n };\n\n document.addEventListener(\"click\", handler, { capture: true, passive: true });\n teardowns.push(() => document.removeEventListener(\"click\", handler, { capture: true }));\n}\n\nfunction setupNavigation(add: BreadcrumbCallback): void {\n // pushState\n const origPush = history.pushState;\n history.pushState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origPush.apply(this, args);\n };\n\n // replaceState\n const origReplace = history.replaceState;\n history.replaceState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origReplace.apply(this, args);\n };\n\n // popstate (back/forward)\n const popHandler = () => {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: location.href,\n });\n };\n window.addEventListener(\"popstate\", popHandler);\n\n teardowns.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener(\"popstate\", popHandler);\n });\n}\n\nfunction setupFetch(add: BreadcrumbCallback): void {\n const origFetch = window.fetch;\n\n window.fetch = async function (input, init?) {\n const rawUrl = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = init?.method || \"GET\";\n const start = Date.now();\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origFetch.call(window, input, init);\n }\n\n const url = scrubUrl(rawUrl);\n\n try {\n const response = await origFetch.call(window, input, init);\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: response.ok ? \"info\" : \"error\",\n data: {\n status: response.status,\n duration: Date.now() - start,\n },\n });\n return response;\n } catch (err) {\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: \"error\",\n data: {\n error: String(err),\n duration: Date.now() - start,\n },\n });\n throw err;\n }\n };\n\n teardowns.push(() => {\n window.fetch = origFetch;\n });\n}\n\nfunction setupXHR(add: BreadcrumbCallback): void {\n const origOpen = XMLHttpRequest.prototype.open;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL) {\n (this as any).__ug_method = method;\n (this as any).__ug_url = String(url);\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as any);\n };\n\n XMLHttpRequest.prototype.send = function () {\n const start = Date.now();\n const method = (this as any).__ug_method || \"GET\";\n const rawUrl: string = (this as any).__ug_url || \"\";\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origSend.apply(this, arguments as any);\n }\n\n const url = scrubUrl(rawUrl);\n\n this.addEventListener(\"loadend\", function () {\n add({\n timestamp: Date.now(),\n category: \"xhr\",\n message: `${method} ${url}`,\n level: this.status >= 400 ? \"error\" : \"info\",\n data: {\n status: this.status,\n duration: Date.now() - start,\n },\n });\n });\n\n // eslint-disable-next-line prefer-rest-params\n return origSend.apply(this, arguments as any);\n };\n\n teardowns.push(() => {\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.send = origSend;\n });\n}\n\n/** Strip sensitive query params from URLs in breadcrumbs */\nfunction scrubUrl(url: string): string {\n try {\n const u = new URL(url, location.origin);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getSelector(el: HTMLElement): string {\n const parts: string[] = [];\n let current: HTMLElement | null = el;\n\n for (let i = 0; i < 3 && current && current !== document.body; i++) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n part += `#${current.id}`;\n } else if (current.className && typeof current.className === \"string\") {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join(\".\");\n if (cls) part += `.${cls}`;\n }\n parts.unshift(part);\n current = current.parentElement;\n }\n\n return parts.join(\" > \");\n}\n","import type { ErrorPayload } from \"./types\";\n\n// Capture the original fetch before any instrumentation wraps it.\n// This prevents SDK requests from appearing in breadcrumbs and\n// avoids recursive loops if the fetch wrapper itself errors.\nconst originalFetch: typeof fetch =\n typeof window !== \"undefined\" ? window.fetch.bind(window) : ((() => {}) as any);\n\nexport interface TransportConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n/**\n * Must be called BEFORE setupBreadcrumbs() to capture the\n * un-instrumented fetch reference.\n */\nexport function captureOriginalFetch(): void {\n // Already captured at module load time above.\n // This function exists as a documentation marker.\n}\n\nexport function sendEvent(\n config: TransportConfig,\n payload: ErrorPayload\n): void {\n const url = `${config.apiUrl}/public/errors`;\n const body = JSON.stringify(payload);\n\n try {\n originalFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": config.apiKey,\n },\n body,\n keepalive: true, // Survives page unload\n }).catch(() => {\n // Silently fail — never impact user's app\n });\n } catch {\n // Silently fail\n }\n}\n"],"mappings":"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","names":["index_exports","__export","Upgoose","index_default","upgoose","__toCommonJS","teardowns","setupBreadcrumbs","config","addBreadcrumb","setupConsole","setupClicks","setupNavigation","setupFetch","setupXHR","teardownBreadcrumbs","fn","add","original","level","orig","args","handler","event","target","tag","text","selector","getSelector","origPush","origReplace","popHandler","origFetch","input","init","rawUrl","method","start","url","scrubUrl","response","err","origOpen","origSend","u","sensitive","param","s","el","parts","current","i","part","cls","originalFetch","sendEvent","config","payload","url","body","originalFetch","Upgoose","event","error","config","setupBreadcrumbs","crumb","extra","message","stack","normalizeError","level","user","key","value","max","teardownBreadcrumbs","partial","payload","stripUrlSensitiveParams","getBrowserContext","processed","sendEvent","url","u","sensitive","param","s","ctx","upgoose","index_default"]}
package/dist/index.d.cts CHANGED
@@ -79,6 +79,15 @@ declare class Upgoose {
79
79
  fingerprint?: string;
80
80
  level?: ErrorPayload["level"];
81
81
  }): void;
82
+ /**
83
+ * Send a test event to verify your integration is working.
84
+ * Check your project page — the status should flip to "CONNECTED" within seconds.
85
+ *
86
+ * ```ts
87
+ * upgoose.test();
88
+ * ```
89
+ */
90
+ test(): void;
82
91
  /**
83
92
  * Capture a message as an error event.
84
93
  *
package/dist/index.d.ts CHANGED
@@ -79,6 +79,15 @@ declare class Upgoose {
79
79
  fingerprint?: string;
80
80
  level?: ErrorPayload["level"];
81
81
  }): void;
82
+ /**
83
+ * Send a test event to verify your integration is working.
84
+ * Check your project page — the status should flip to "CONNECTED" within seconds.
85
+ *
86
+ * ```ts
87
+ * upgoose.test();
88
+ * ```
89
+ */
90
+ test(): void;
82
91
  /**
83
92
  * Capture a message as an error event.
84
93
  *
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var a=[];function p(r,t){r.captureConsole!==!1&&m(t),r.captureClicks!==!1&&f(t),r.captureNavigation!==!1&&y(t),r.captureNetwork!==!1&&(w(t),v(t))}function d(){for(let r of a)try{r()}catch{}a.length=0}function m(r){let t={log:console.log,warn:console.warn,error:console.error};for(let e of["log","warn","error"]){let n=t[e];console[e]=(...s)=>{r({timestamp:Date.now(),category:"console",message:s.map(String).join(" ").slice(0,500),level:e==="error"?"error":e==="warn"?"warning":"info"}),n.apply(console,s)}}a.push(()=>{console.log=t.log,console.warn=t.warn,console.error=t.error})}function f(r){let t=e=>{let n=e.target;if(!n)return;let s=n.tagName?.toLowerCase(),o=n.textContent?.trim().slice(0,100)||"",i=b(n);r({timestamp:Date.now(),category:"ui",message:`click on ${s}${o?`: "${o}"`:""}`,data:{selector:i}})};document.addEventListener("click",t,{capture:!0,passive:!0}),a.push(()=>document.removeEventListener("click",t,{capture:!0}))}function y(r){let t=history.pushState;history.pushState=function(...s){return r({timestamp:Date.now(),category:"navigation",message:String(s[2]||""),data:{from:location.href}}),t.apply(this,s)};let e=history.replaceState;history.replaceState=function(...s){return r({timestamp:Date.now(),category:"navigation",message:String(s[2]||""),data:{from:location.href}}),e.apply(this,s)};let n=()=>{r({timestamp:Date.now(),category:"navigation",message:location.href})};window.addEventListener("popstate",n),a.push(()=>{history.pushState=t,history.replaceState=e,window.removeEventListener("popstate",n)})}function w(r){let t=window.fetch;window.fetch=async function(e,n){let s=typeof e=="string"?e:e instanceof URL?e.href:e.url,o=n?.method||"GET",i=Date.now();if(s.includes("/public/errors"))return t.call(window,e,n);let u=g(s);try{let c=await t.call(window,e,n);return r({timestamp:Date.now(),category:"fetch",message:`${o} ${u}`,level:c.ok?"info":"error",data:{status:c.status,duration:Date.now()-i}}),c}catch(c){throw r({timestamp:Date.now(),category:"fetch",message:`${o} ${u}`,level:"error",data:{error:String(c),duration:Date.now()-i}}),c}},a.push(()=>{window.fetch=t})}function v(r){let t=XMLHttpRequest.prototype.open,e=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(n,s){return this.__ug_method=n,this.__ug_url=String(s),t.apply(this,arguments)},XMLHttpRequest.prototype.send=function(){let n=Date.now(),s=this.__ug_method||"GET",o=this.__ug_url||"";if(o.includes("/public/errors"))return e.apply(this,arguments);let i=g(o);return this.addEventListener("loadend",function(){r({timestamp:Date.now(),category:"xhr",message:`${s} ${i}`,level:this.status>=400?"error":"info",data:{status:this.status,duration:Date.now()-n}})}),e.apply(this,arguments)},a.push(()=>{XMLHttpRequest.prototype.open=t,XMLHttpRequest.prototype.send=e})}function g(r){try{let t=new URL(r,location.origin),e=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let n of[...t.searchParams.keys()])e.some(s=>n.toLowerCase().includes(s))&&t.searchParams.set(n,"[REDACTED]");return t.hash="",t.toString()}catch{return r}}function b(r){let t=[],e=r;for(let n=0;n<3&&e&&e!==document.body;n++){let s=e.tagName.toLowerCase();if(e.id)s+=`#${e.id}`;else if(e.className&&typeof e.className=="string"){let o=e.className.trim().split(/\s+/).slice(0,2).join(".");o&&(s+=`.${o}`)}t.unshift(s),e=e.parentElement}return t.join(" > ")}var k=typeof window<"u"?window.fetch.bind(window):(()=>{});function h(r,t){let e=`${r.apiUrl}/public/errors`,n=JSON.stringify(t);try{k(e,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":r.apiKey},body:n,keepalive:!0}).catch(()=>{})}catch{}}var l=class{constructor(){this.config=null;this.breadcrumbs=[];this.user=null;this.tags={};this.initialized=!1;this.sending=!1;this.onError=t=>{let e=t.error||new Error(t.message||"Unknown error");this.captureException(e)};this.onUnhandledRejection=t=>{let e=t.reason;this.captureException(e)}}init(t){if(this.initialized){console.warn("[Upgoose] Already initialized. Call destroy() first to re-initialize.");return}if(!t.apiKey||!t.apiUrl){console.error("[Upgoose] apiKey and apiUrl are required.");return}this.config={sampleRate:1,maxBreadcrumbs:50,captureConsole:!0,captureClicks:!0,captureNavigation:!0,captureNetwork:!0,captureUnhandled:!0,...t},this.breadcrumbs=[],this.initialized=!0,p(this.config,e=>this.addBreadcrumb(e)),this.config.captureUnhandled&&this.setupGlobalHandlers()}captureException(t,e){if(!this.config)return;let{message:n,stack:s}=E(t);this.send({message:n,stack:s,level:e?.level||"error",tags:e?.tags,fingerprint:e?.fingerprint})}captureMessage(t,e="error"){this.config&&this.send({message:t,level:e})}setUser(t){this.user=t}setTag(t,e){this.tags[t]=e}addBreadcrumb(t){let e=this.config?.maxBreadcrumbs??50;this.breadcrumbs.push(t),this.breadcrumbs.length>e&&(this.breadcrumbs=this.breadcrumbs.slice(-e))}destroy(){d(),this.removeGlobalHandlers(),this.config=null,this.breadcrumbs=[],this.user=null,this.tags={},this.initialized=!1}setupGlobalHandlers(){window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onUnhandledRejection)}removeGlobalHandlers(){window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onUnhandledRejection)}send(t){let e=this.config;if(e&&!this.sending){this.sending=!0;try{if(typeof e.sampleRate=="number"&&Math.random()>e.sampleRate)return;let n={message:t.message.slice(0,2e3),stack:t.stack?.slice(0,1e4),level:t.level,environment:e.environment,release:e.release,tags:{...this.tags,...t.tags},context:{breadcrumbs:this.breadcrumbs.slice(-30),...this.user?{user:this.user}:{},page:{url:C(location.href),title:document.title},browser:U()},fingerprint:t.fingerprint},s=n;if(e.beforeSend)try{s=e.beforeSend(n)}catch{return}if(!s)return;h({apiKey:e.apiKey,apiUrl:e.apiUrl},s)}finally{this.sending=!1}}}};function E(r){return r instanceof Error?{message:r.message,stack:r.stack}:typeof r=="string"?{message:r}:r&&typeof r=="object"&&"message"in r?{message:String(r.message),stack:"stack"in r?String(r.stack):void 0}:{message:String(r)}}function C(r){try{let t=new URL(r),e=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let n of[...t.searchParams.keys()])e.some(s=>n.toLowerCase().includes(s))&&t.searchParams.set(n,"[REDACTED]");return t.hash="",t.toString()}catch{return r}}function U(){let r={};try{r.userAgent=navigator.userAgent,r.language=navigator.language,r.viewport=`${window.innerWidth}x${window.innerHeight}`,r.screen=`${screen.width}x${screen.height}`}catch{}return r}var L=new l,P=L;export{l as Upgoose,P as default,L as upgoose};
1
+ var i=[];function p(r,t){r.captureConsole!==!1&&f(t),r.captureClicks!==!1&&m(t),r.captureNavigation!==!1&&y(t),r.captureNetwork!==!1&&(v(t),w(t))}function d(){for(let r of i)try{r()}catch{}i.length=0}function f(r){let t={log:console.log,warn:console.warn,error:console.error};for(let e of["log","warn","error"]){let n=t[e];console[e]=(...s)=>{r({timestamp:Date.now(),category:"console",message:s.map(String).join(" ").slice(0,500),level:e==="error"?"error":e==="warn"?"warning":"info"}),n.apply(console,s)}}i.push(()=>{console.log=t.log,console.warn=t.warn,console.error=t.error})}function m(r){let t=e=>{let n=e.target;if(!n)return;let s=n.tagName?.toLowerCase(),o=n.textContent?.trim().slice(0,100)||"",a=b(n);r({timestamp:Date.now(),category:"ui",message:`click on ${s}${o?`: "${o}"`:""}`,data:{selector:a}})};document.addEventListener("click",t,{capture:!0,passive:!0}),i.push(()=>document.removeEventListener("click",t,{capture:!0}))}function y(r){let t=history.pushState;history.pushState=function(...s){return r({timestamp:Date.now(),category:"navigation",message:String(s[2]||""),data:{from:location.href}}),t.apply(this,s)};let e=history.replaceState;history.replaceState=function(...s){return r({timestamp:Date.now(),category:"navigation",message:String(s[2]||""),data:{from:location.href}}),e.apply(this,s)};let n=()=>{r({timestamp:Date.now(),category:"navigation",message:location.href})};window.addEventListener("popstate",n),i.push(()=>{history.pushState=t,history.replaceState=e,window.removeEventListener("popstate",n)})}function v(r){let t=window.fetch;window.fetch=async function(e,n){let s=typeof e=="string"?e:e instanceof URL?e.href:e.url,o=n?.method||"GET",a=Date.now();if(s.includes("/public/errors"))return t.call(window,e,n);let u=g(s);try{let c=await t.call(window,e,n);return r({timestamp:Date.now(),category:"fetch",message:`${o} ${u}`,level:c.ok?"info":"error",data:{status:c.status,duration:Date.now()-a}}),c}catch(c){throw r({timestamp:Date.now(),category:"fetch",message:`${o} ${u}`,level:"error",data:{error:String(c),duration:Date.now()-a}}),c}},i.push(()=>{window.fetch=t})}function w(r){let t=XMLHttpRequest.prototype.open,e=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(n,s){return this.__ug_method=n,this.__ug_url=String(s),t.apply(this,arguments)},XMLHttpRequest.prototype.send=function(){let n=Date.now(),s=this.__ug_method||"GET",o=this.__ug_url||"";if(o.includes("/public/errors"))return e.apply(this,arguments);let a=g(o);return this.addEventListener("loadend",function(){r({timestamp:Date.now(),category:"xhr",message:`${s} ${a}`,level:this.status>=400?"error":"info",data:{status:this.status,duration:Date.now()-n}})}),e.apply(this,arguments)},i.push(()=>{XMLHttpRequest.prototype.open=t,XMLHttpRequest.prototype.send=e})}function g(r){try{let t=new URL(r,location.origin),e=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let n of[...t.searchParams.keys()])e.some(s=>n.toLowerCase().includes(s))&&t.searchParams.set(n,"[REDACTED]");return t.hash="",t.toString()}catch{return r}}function b(r){let t=[],e=r;for(let n=0;n<3&&e&&e!==document.body;n++){let s=e.tagName.toLowerCase();if(e.id)s+=`#${e.id}`;else if(e.className&&typeof e.className=="string"){let o=e.className.trim().split(/\s+/).slice(0,2).join(".");o&&(s+=`.${o}`)}t.unshift(s),e=e.parentElement}return t.join(" > ")}var k=typeof window<"u"?window.fetch.bind(window):(()=>{});function h(r,t){let e=`${r.apiUrl}/public/errors`,n=JSON.stringify(t);try{k(e,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":r.apiKey},body:n,keepalive:!0}).catch(()=>{})}catch{}}var l=class{constructor(){this.config=null;this.breadcrumbs=[];this.user=null;this.tags={};this.initialized=!1;this.sending=!1;this.onError=t=>{let e=t.error||new Error(t.message||"Unknown error");this.captureException(e)};this.onUnhandledRejection=t=>{let e=t.reason;this.captureException(e)}}init(t){if(this.initialized){console.warn("[Upgoose] Already initialized. Call destroy() first to re-initialize.");return}if(!t.apiKey||!t.apiUrl){console.error("[Upgoose] apiKey and apiUrl are required.");return}this.config={sampleRate:1,maxBreadcrumbs:50,captureConsole:!0,captureClicks:!0,captureNavigation:!0,captureNetwork:!0,captureUnhandled:!0,...t},this.breadcrumbs=[],this.initialized=!0,p(this.config,e=>this.addBreadcrumb(e)),this.config.captureUnhandled&&this.setupGlobalHandlers()}captureException(t,e){if(!this.config)return;let{message:n,stack:s}=E(t);this.send({message:n,stack:s,level:e?.level||"error",tags:e?.tags,fingerprint:e?.fingerprint})}test(){if(!this.config){console.warn("[Upgoose] Not initialized. Call init() first.");return}this.send({message:"Upgoose test event \u2014 integration verified",level:"warning",tags:{upgoose_test:"true"},fingerprint:"upgoose-test-event"}),console.info("[Upgoose] Test event sent. Check your project page.")}captureMessage(t,e="error"){this.config&&this.send({message:t,level:e})}setUser(t){this.user=t}setTag(t,e){this.tags[t]=e}addBreadcrumb(t){let e=this.config?.maxBreadcrumbs??50;this.breadcrumbs.push(t),this.breadcrumbs.length>e&&(this.breadcrumbs=this.breadcrumbs.slice(-e))}destroy(){d(),this.removeGlobalHandlers(),this.config=null,this.breadcrumbs=[],this.user=null,this.tags={},this.initialized=!1}setupGlobalHandlers(){window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onUnhandledRejection)}removeGlobalHandlers(){window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onUnhandledRejection)}send(t){let e=this.config;if(e&&!this.sending){this.sending=!0;try{if(typeof e.sampleRate=="number"&&Math.random()>e.sampleRate)return;let n={message:t.message.slice(0,2e3),stack:t.stack?.slice(0,1e4),level:t.level,environment:e.environment,release:e.release,tags:{...this.tags,...t.tags},context:{breadcrumbs:this.breadcrumbs.slice(-30),...this.user?{user:this.user}:{},page:{url:C(location.href),title:document.title},browser:U()},fingerprint:t.fingerprint},s=n;if(e.beforeSend)try{s=e.beforeSend(n)}catch{return}if(!s)return;h({apiKey:e.apiKey,apiUrl:e.apiUrl},s)}finally{this.sending=!1}}}};function E(r){return r instanceof Error?{message:r.message,stack:r.stack}:typeof r=="string"?{message:r}:r&&typeof r=="object"&&"message"in r?{message:String(r.message),stack:"stack"in r?String(r.stack):void 0}:{message:String(r)}}function C(r){try{let t=new URL(r),e=["token","key","secret","password","auth","code","session","jwt","access_token","refresh_token","api_key","apikey"];for(let n of[...t.searchParams.keys()])e.some(s=>n.toLowerCase().includes(s))&&t.searchParams.set(n,"[REDACTED]");return t.hash="",t.toString()}catch{return r}}function U(){let r={};try{r.userAgent=navigator.userAgent,r.language=navigator.language,r.viewport=`${window.innerWidth}x${window.innerHeight}`,r.screen=`${screen.width}x${screen.height}`}catch{}return r}var L=new l,B=L;export{l as Upgoose,B as default,L as upgoose};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/breadcrumbs.ts","../src/transport.ts","../src/index.ts"],"sourcesContent":["import type { Breadcrumb, UpgooseConfig } from \"./types\";\n\ntype BreadcrumbCallback = (crumb: Breadcrumb) => void;\n\nconst teardowns: Array<() => void> = [];\n\nexport function setupBreadcrumbs(\n config: UpgooseConfig,\n addBreadcrumb: BreadcrumbCallback\n): void {\n if (config.captureConsole !== false) {\n setupConsole(addBreadcrumb);\n }\n if (config.captureClicks !== false) {\n setupClicks(addBreadcrumb);\n }\n if (config.captureNavigation !== false) {\n setupNavigation(addBreadcrumb);\n }\n if (config.captureNetwork !== false) {\n setupFetch(addBreadcrumb);\n setupXHR(addBreadcrumb);\n }\n}\n\nexport function teardownBreadcrumbs(): void {\n for (const fn of teardowns) {\n try {\n fn();\n } catch {\n // Best effort cleanup\n }\n }\n teardowns.length = 0;\n}\n\nfunction setupConsole(add: BreadcrumbCallback): void {\n const original = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n };\n\n for (const level of [\"log\", \"warn\", \"error\"] as const) {\n const orig = original[level];\n console[level] = (...args: unknown[]) => {\n add({\n timestamp: Date.now(),\n category: \"console\",\n message: args.map(String).join(\" \").slice(0, 500),\n level: level === \"error\" ? \"error\" : level === \"warn\" ? \"warning\" : \"info\",\n });\n orig.apply(console, args);\n };\n }\n\n teardowns.push(() => {\n console.log = original.log;\n console.warn = original.warn;\n console.error = original.error;\n });\n}\n\nfunction setupClicks(add: BreadcrumbCallback): void {\n const handler = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (!target) return;\n\n const tag = target.tagName?.toLowerCase();\n const text = target.textContent?.trim().slice(0, 100) || \"\";\n const selector = getSelector(target);\n\n add({\n timestamp: Date.now(),\n category: \"ui\",\n message: `click on ${tag}${text ? `: \"${text}\"` : \"\"}`,\n data: { selector },\n });\n };\n\n document.addEventListener(\"click\", handler, { capture: true, passive: true });\n teardowns.push(() => document.removeEventListener(\"click\", handler, { capture: true }));\n}\n\nfunction setupNavigation(add: BreadcrumbCallback): void {\n // pushState\n const origPush = history.pushState;\n history.pushState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origPush.apply(this, args);\n };\n\n // replaceState\n const origReplace = history.replaceState;\n history.replaceState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origReplace.apply(this, args);\n };\n\n // popstate (back/forward)\n const popHandler = () => {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: location.href,\n });\n };\n window.addEventListener(\"popstate\", popHandler);\n\n teardowns.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener(\"popstate\", popHandler);\n });\n}\n\nfunction setupFetch(add: BreadcrumbCallback): void {\n const origFetch = window.fetch;\n\n window.fetch = async function (input, init?) {\n const rawUrl = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = init?.method || \"GET\";\n const start = Date.now();\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origFetch.call(window, input, init);\n }\n\n const url = scrubUrl(rawUrl);\n\n try {\n const response = await origFetch.call(window, input, init);\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: response.ok ? \"info\" : \"error\",\n data: {\n status: response.status,\n duration: Date.now() - start,\n },\n });\n return response;\n } catch (err) {\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: \"error\",\n data: {\n error: String(err),\n duration: Date.now() - start,\n },\n });\n throw err;\n }\n };\n\n teardowns.push(() => {\n window.fetch = origFetch;\n });\n}\n\nfunction setupXHR(add: BreadcrumbCallback): void {\n const origOpen = XMLHttpRequest.prototype.open;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL) {\n (this as any).__ug_method = method;\n (this as any).__ug_url = String(url);\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as any);\n };\n\n XMLHttpRequest.prototype.send = function () {\n const start = Date.now();\n const method = (this as any).__ug_method || \"GET\";\n const rawUrl: string = (this as any).__ug_url || \"\";\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origSend.apply(this, arguments as any);\n }\n\n const url = scrubUrl(rawUrl);\n\n this.addEventListener(\"loadend\", function () {\n add({\n timestamp: Date.now(),\n category: \"xhr\",\n message: `${method} ${url}`,\n level: this.status >= 400 ? \"error\" : \"info\",\n data: {\n status: this.status,\n duration: Date.now() - start,\n },\n });\n });\n\n // eslint-disable-next-line prefer-rest-params\n return origSend.apply(this, arguments as any);\n };\n\n teardowns.push(() => {\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.send = origSend;\n });\n}\n\n/** Strip sensitive query params from URLs in breadcrumbs */\nfunction scrubUrl(url: string): string {\n try {\n const u = new URL(url, location.origin);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getSelector(el: HTMLElement): string {\n const parts: string[] = [];\n let current: HTMLElement | null = el;\n\n for (let i = 0; i < 3 && current && current !== document.body; i++) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n part += `#${current.id}`;\n } else if (current.className && typeof current.className === \"string\") {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join(\".\");\n if (cls) part += `.${cls}`;\n }\n parts.unshift(part);\n current = current.parentElement;\n }\n\n return parts.join(\" > \");\n}\n","import type { ErrorPayload } from \"./types\";\n\n// Capture the original fetch before any instrumentation wraps it.\n// This prevents SDK requests from appearing in breadcrumbs and\n// avoids recursive loops if the fetch wrapper itself errors.\nconst originalFetch: typeof fetch =\n typeof window !== \"undefined\" ? window.fetch.bind(window) : ((() => {}) as any);\n\nexport interface TransportConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n/**\n * Must be called BEFORE setupBreadcrumbs() to capture the\n * un-instrumented fetch reference.\n */\nexport function captureOriginalFetch(): void {\n // Already captured at module load time above.\n // This function exists as a documentation marker.\n}\n\nexport function sendEvent(\n config: TransportConfig,\n payload: ErrorPayload\n): void {\n const url = `${config.apiUrl}/public/errors`;\n const body = JSON.stringify(payload);\n\n try {\n originalFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": config.apiKey,\n },\n body,\n keepalive: true, // Survives page unload\n }).catch(() => {\n // Silently fail — never impact user's app\n });\n } catch {\n // Silently fail\n }\n}\n","import type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n} from \"./types\";\nimport { setupBreadcrumbs, teardownBreadcrumbs } from \"./breadcrumbs\";\nimport { sendEvent } from \"./transport\";\n\nexport type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n};\n\nclass Upgoose {\n private config: UpgooseConfig | null = null;\n private breadcrumbs: Breadcrumb[] = [];\n private user: UserContext | null = null;\n private tags: Record<string, string> = {};\n private initialized = false;\n private sending = false; // re-entrancy guard\n\n /**\n * Initialize the Upgoose SDK. Call this once, as early as possible.\n *\n * ```ts\n * import upgoose from '@upgoose/browser';\n * upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });\n * ```\n */\n init(config: UpgooseConfig): void {\n if (this.initialized) {\n console.warn(\"[Upgoose] Already initialized. Call destroy() first to re-initialize.\");\n return;\n }\n\n if (!config.apiKey || !config.apiUrl) {\n console.error(\"[Upgoose] apiKey and apiUrl are required.\");\n return;\n }\n\n this.config = {\n sampleRate: 1,\n maxBreadcrumbs: 50,\n captureConsole: true,\n captureClicks: true,\n captureNavigation: true,\n captureNetwork: true,\n captureUnhandled: true,\n ...config,\n };\n\n this.breadcrumbs = [];\n this.initialized = true;\n\n // Set up breadcrumb collection\n setupBreadcrumbs(this.config, (crumb) => this.addBreadcrumb(crumb));\n\n // Set up global error handlers\n if (this.config.captureUnhandled) {\n this.setupGlobalHandlers();\n }\n }\n\n /**\n * Capture an exception manually.\n *\n * ```ts\n * try {\n * riskyOperation();\n * } catch (err) {\n * upgoose.captureException(err);\n * }\n * ```\n */\n captureException(\n error: unknown,\n extra?: { tags?: Record<string, string>; fingerprint?: string; level?: ErrorPayload[\"level\"] }\n ): void {\n if (!this.config) return;\n\n const { message, stack } = normalizeError(error);\n this.send({\n message,\n stack,\n level: extra?.level || \"error\",\n tags: extra?.tags,\n fingerprint: extra?.fingerprint,\n });\n }\n\n /**\n * Capture a message as an error event.\n *\n * ```ts\n * upgoose.captureMessage(\"User hit rate limit\", \"warning\");\n * ```\n */\n captureMessage(\n message: string,\n level: ErrorPayload[\"level\"] = \"error\"\n ): void {\n if (!this.config) return;\n this.send({ message, level });\n }\n\n /**\n * Set user context. Attached to all subsequent events.\n *\n * ```ts\n * upgoose.setUser({ id: '123', email: 'user@example.com' });\n * ```\n */\n setUser(user: UserContext | null): void {\n this.user = user;\n }\n\n /**\n * Set a global tag. Attached to all subsequent events.\n */\n setTag(key: string, value: string): void {\n this.tags[key] = value;\n }\n\n /**\n * Add a breadcrumb manually.\n */\n addBreadcrumb(crumb: Breadcrumb): void {\n const max = this.config?.maxBreadcrumbs ?? 50;\n this.breadcrumbs.push(crumb);\n if (this.breadcrumbs.length > max) {\n this.breadcrumbs = this.breadcrumbs.slice(-max);\n }\n }\n\n /**\n * Tear down the SDK — removes global handlers and breadcrumb instrumentation.\n */\n destroy(): void {\n teardownBreadcrumbs();\n this.removeGlobalHandlers();\n this.config = null;\n this.breadcrumbs = [];\n this.user = null;\n this.tags = {};\n this.initialized = false;\n }\n\n // -- Private --\n\n private onError = (event: ErrorEvent): void => {\n const error = event.error || new Error(event.message || \"Unknown error\");\n this.captureException(error);\n };\n\n private onUnhandledRejection = (event: PromiseRejectionEvent): void => {\n const error = event.reason;\n this.captureException(error);\n };\n\n private setupGlobalHandlers(): void {\n window.addEventListener(\"error\", this.onError);\n window.addEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private removeGlobalHandlers(): void {\n window.removeEventListener(\"error\", this.onError);\n window.removeEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private send(partial: {\n message: string;\n stack?: string;\n level: ErrorPayload[\"level\"];\n tags?: Record<string, string>;\n fingerprint?: string;\n }): void {\n const config = this.config;\n if (!config) return;\n\n // Re-entrancy guard: prevent infinite loops if send() itself throws\n // or if beforeSend throws and triggers the global error handler\n if (this.sending) return;\n this.sending = true;\n\n try {\n // Sample rate check\n if (typeof config.sampleRate === \"number\" && Math.random() > config.sampleRate) {\n return;\n }\n\n const payload: ErrorPayload = {\n message: partial.message.slice(0, 2000),\n stack: partial.stack?.slice(0, 10000),\n level: partial.level,\n environment: config.environment,\n release: config.release,\n tags: { ...this.tags, ...partial.tags },\n context: {\n breadcrumbs: this.breadcrumbs.slice(-30),\n ...(this.user ? { user: this.user } : {}),\n page: {\n url: stripUrlSensitiveParams(location.href),\n title: document.title,\n },\n browser: getBrowserContext(),\n },\n fingerprint: partial.fingerprint,\n };\n\n // beforeSend hook (wrapped to prevent throws from looping)\n let processed: ErrorPayload | null = payload;\n if (config.beforeSend) {\n try {\n processed = config.beforeSend(payload);\n } catch {\n // Drop event if beforeSend throws\n return;\n }\n }\n if (!processed) return;\n\n sendEvent(\n { apiKey: config.apiKey, apiUrl: config.apiUrl },\n processed\n );\n } finally {\n this.sending = false;\n }\n }\n}\n\nfunction normalizeError(error: unknown): { message: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, stack: error.stack };\n }\n if (typeof error === \"string\") {\n return { message: error };\n }\n if (error && typeof error === \"object\" && \"message\" in error) {\n return {\n message: String((error as { message: unknown }).message),\n stack: \"stack\" in error ? String((error as { stack: unknown }).stack) : undefined,\n };\n }\n return { message: String(error) };\n}\n\n/** Strip common sensitive query/hash params from URLs */\nfunction stripUrlSensitiveParams(url: string): string {\n try {\n const u = new URL(url);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n // Strip hash (often contains tokens in SPAs)\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getBrowserContext(): Record<string, string> {\n const ctx: Record<string, string> = {};\n try {\n ctx.userAgent = navigator.userAgent;\n ctx.language = navigator.language;\n ctx.viewport = `${window.innerWidth}x${window.innerHeight}`;\n ctx.screen = `${screen.width}x${screen.height}`;\n } catch {\n // Ignore\n }\n return ctx;\n}\n\n// Singleton instance\nconst upgoose = new Upgoose();\nexport default upgoose;\nexport { upgoose, Upgoose };\n"],"mappings":"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","names":["teardowns","setupBreadcrumbs","config","addBreadcrumb","setupConsole","setupClicks","setupNavigation","setupFetch","setupXHR","teardownBreadcrumbs","fn","add","original","level","orig","args","handler","event","target","tag","text","selector","getSelector","origPush","origReplace","popHandler","origFetch","input","init","rawUrl","method","start","url","scrubUrl","response","err","origOpen","origSend","u","sensitive","param","el","parts","current","i","part","cls","originalFetch","sendEvent","config","payload","url","body","originalFetch","Upgoose","event","error","config","setupBreadcrumbs","crumb","extra","message","stack","normalizeError","level","user","key","value","max","teardownBreadcrumbs","partial","payload","stripUrlSensitiveParams","getBrowserContext","processed","sendEvent","url","u","sensitive","param","ctx","upgoose","index_default"]}
1
+ {"version":3,"sources":["../src/breadcrumbs.ts","../src/transport.ts","../src/index.ts"],"sourcesContent":["import type { Breadcrumb, UpgooseConfig } from \"./types\";\n\ntype BreadcrumbCallback = (crumb: Breadcrumb) => void;\n\nconst teardowns: Array<() => void> = [];\n\nexport function setupBreadcrumbs(\n config: UpgooseConfig,\n addBreadcrumb: BreadcrumbCallback\n): void {\n if (config.captureConsole !== false) {\n setupConsole(addBreadcrumb);\n }\n if (config.captureClicks !== false) {\n setupClicks(addBreadcrumb);\n }\n if (config.captureNavigation !== false) {\n setupNavigation(addBreadcrumb);\n }\n if (config.captureNetwork !== false) {\n setupFetch(addBreadcrumb);\n setupXHR(addBreadcrumb);\n }\n}\n\nexport function teardownBreadcrumbs(): void {\n for (const fn of teardowns) {\n try {\n fn();\n } catch {\n // Best effort cleanup\n }\n }\n teardowns.length = 0;\n}\n\nfunction setupConsole(add: BreadcrumbCallback): void {\n const original = {\n log: console.log,\n warn: console.warn,\n error: console.error,\n };\n\n for (const level of [\"log\", \"warn\", \"error\"] as const) {\n const orig = original[level];\n console[level] = (...args: unknown[]) => {\n add({\n timestamp: Date.now(),\n category: \"console\",\n message: args.map(String).join(\" \").slice(0, 500),\n level: level === \"error\" ? \"error\" : level === \"warn\" ? \"warning\" : \"info\",\n });\n orig.apply(console, args);\n };\n }\n\n teardowns.push(() => {\n console.log = original.log;\n console.warn = original.warn;\n console.error = original.error;\n });\n}\n\nfunction setupClicks(add: BreadcrumbCallback): void {\n const handler = (event: MouseEvent) => {\n const target = event.target as HTMLElement | null;\n if (!target) return;\n\n const tag = target.tagName?.toLowerCase();\n const text = target.textContent?.trim().slice(0, 100) || \"\";\n const selector = getSelector(target);\n\n add({\n timestamp: Date.now(),\n category: \"ui\",\n message: `click on ${tag}${text ? `: \"${text}\"` : \"\"}`,\n data: { selector },\n });\n };\n\n document.addEventListener(\"click\", handler, { capture: true, passive: true });\n teardowns.push(() => document.removeEventListener(\"click\", handler, { capture: true }));\n}\n\nfunction setupNavigation(add: BreadcrumbCallback): void {\n // pushState\n const origPush = history.pushState;\n history.pushState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origPush.apply(this, args);\n };\n\n // replaceState\n const origReplace = history.replaceState;\n history.replaceState = function (...args) {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: String(args[2] || \"\"),\n data: { from: location.href },\n });\n return origReplace.apply(this, args);\n };\n\n // popstate (back/forward)\n const popHandler = () => {\n add({\n timestamp: Date.now(),\n category: \"navigation\",\n message: location.href,\n });\n };\n window.addEventListener(\"popstate\", popHandler);\n\n teardowns.push(() => {\n history.pushState = origPush;\n history.replaceState = origReplace;\n window.removeEventListener(\"popstate\", popHandler);\n });\n}\n\nfunction setupFetch(add: BreadcrumbCallback): void {\n const origFetch = window.fetch;\n\n window.fetch = async function (input, init?) {\n const rawUrl = typeof input === \"string\" ? input : input instanceof URL ? input.href : input.url;\n const method = init?.method || \"GET\";\n const start = Date.now();\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origFetch.call(window, input, init);\n }\n\n const url = scrubUrl(rawUrl);\n\n try {\n const response = await origFetch.call(window, input, init);\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: response.ok ? \"info\" : \"error\",\n data: {\n status: response.status,\n duration: Date.now() - start,\n },\n });\n return response;\n } catch (err) {\n add({\n timestamp: Date.now(),\n category: \"fetch\",\n message: `${method} ${url}`,\n level: \"error\",\n data: {\n error: String(err),\n duration: Date.now() - start,\n },\n });\n throw err;\n }\n };\n\n teardowns.push(() => {\n window.fetch = origFetch;\n });\n}\n\nfunction setupXHR(add: BreadcrumbCallback): void {\n const origOpen = XMLHttpRequest.prototype.open;\n const origSend = XMLHttpRequest.prototype.send;\n\n XMLHttpRequest.prototype.open = function (method: string, url: string | URL) {\n (this as any).__ug_method = method;\n (this as any).__ug_url = String(url);\n // eslint-disable-next-line prefer-rest-params\n return origOpen.apply(this, arguments as any);\n };\n\n XMLHttpRequest.prototype.send = function () {\n const start = Date.now();\n const method = (this as any).__ug_method || \"GET\";\n const rawUrl: string = (this as any).__ug_url || \"\";\n\n // Skip breadcrumbs for SDK's own requests\n if (rawUrl.includes(\"/public/errors\")) {\n return origSend.apply(this, arguments as any);\n }\n\n const url = scrubUrl(rawUrl);\n\n this.addEventListener(\"loadend\", function () {\n add({\n timestamp: Date.now(),\n category: \"xhr\",\n message: `${method} ${url}`,\n level: this.status >= 400 ? \"error\" : \"info\",\n data: {\n status: this.status,\n duration: Date.now() - start,\n },\n });\n });\n\n // eslint-disable-next-line prefer-rest-params\n return origSend.apply(this, arguments as any);\n };\n\n teardowns.push(() => {\n XMLHttpRequest.prototype.open = origOpen;\n XMLHttpRequest.prototype.send = origSend;\n });\n}\n\n/** Strip sensitive query params from URLs in breadcrumbs */\nfunction scrubUrl(url: string): string {\n try {\n const u = new URL(url, location.origin);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getSelector(el: HTMLElement): string {\n const parts: string[] = [];\n let current: HTMLElement | null = el;\n\n for (let i = 0; i < 3 && current && current !== document.body; i++) {\n let part = current.tagName.toLowerCase();\n if (current.id) {\n part += `#${current.id}`;\n } else if (current.className && typeof current.className === \"string\") {\n const cls = current.className.trim().split(/\\s+/).slice(0, 2).join(\".\");\n if (cls) part += `.${cls}`;\n }\n parts.unshift(part);\n current = current.parentElement;\n }\n\n return parts.join(\" > \");\n}\n","import type { ErrorPayload } from \"./types\";\n\n// Capture the original fetch before any instrumentation wraps it.\n// This prevents SDK requests from appearing in breadcrumbs and\n// avoids recursive loops if the fetch wrapper itself errors.\nconst originalFetch: typeof fetch =\n typeof window !== \"undefined\" ? window.fetch.bind(window) : ((() => {}) as any);\n\nexport interface TransportConfig {\n apiKey: string;\n apiUrl: string;\n}\n\n/**\n * Must be called BEFORE setupBreadcrumbs() to capture the\n * un-instrumented fetch reference.\n */\nexport function captureOriginalFetch(): void {\n // Already captured at module load time above.\n // This function exists as a documentation marker.\n}\n\nexport function sendEvent(\n config: TransportConfig,\n payload: ErrorPayload\n): void {\n const url = `${config.apiUrl}/public/errors`;\n const body = JSON.stringify(payload);\n\n try {\n originalFetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-API-Key\": config.apiKey,\n },\n body,\n keepalive: true, // Survives page unload\n }).catch(() => {\n // Silently fail — never impact user's app\n });\n } catch {\n // Silently fail\n }\n}\n","import type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n} from \"./types\";\nimport { setupBreadcrumbs, teardownBreadcrumbs } from \"./breadcrumbs\";\nimport { sendEvent } from \"./transport\";\n\nexport type {\n UpgooseConfig,\n Breadcrumb,\n UserContext,\n ErrorPayload,\n};\n\nclass Upgoose {\n private config: UpgooseConfig | null = null;\n private breadcrumbs: Breadcrumb[] = [];\n private user: UserContext | null = null;\n private tags: Record<string, string> = {};\n private initialized = false;\n private sending = false; // re-entrancy guard\n\n /**\n * Initialize the Upgoose SDK. Call this once, as early as possible.\n *\n * ```ts\n * import upgoose from '@upgoose/browser';\n * upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });\n * ```\n */\n init(config: UpgooseConfig): void {\n if (this.initialized) {\n console.warn(\"[Upgoose] Already initialized. Call destroy() first to re-initialize.\");\n return;\n }\n\n if (!config.apiKey || !config.apiUrl) {\n console.error(\"[Upgoose] apiKey and apiUrl are required.\");\n return;\n }\n\n this.config = {\n sampleRate: 1,\n maxBreadcrumbs: 50,\n captureConsole: true,\n captureClicks: true,\n captureNavigation: true,\n captureNetwork: true,\n captureUnhandled: true,\n ...config,\n };\n\n this.breadcrumbs = [];\n this.initialized = true;\n\n // Set up breadcrumb collection\n setupBreadcrumbs(this.config, (crumb) => this.addBreadcrumb(crumb));\n\n // Set up global error handlers\n if (this.config.captureUnhandled) {\n this.setupGlobalHandlers();\n }\n }\n\n /**\n * Capture an exception manually.\n *\n * ```ts\n * try {\n * riskyOperation();\n * } catch (err) {\n * upgoose.captureException(err);\n * }\n * ```\n */\n captureException(\n error: unknown,\n extra?: { tags?: Record<string, string>; fingerprint?: string; level?: ErrorPayload[\"level\"] }\n ): void {\n if (!this.config) return;\n\n const { message, stack } = normalizeError(error);\n this.send({\n message,\n stack,\n level: extra?.level || \"error\",\n tags: extra?.tags,\n fingerprint: extra?.fingerprint,\n });\n }\n\n /**\n * Send a test event to verify your integration is working.\n * Check your project page — the status should flip to \"CONNECTED\" within seconds.\n *\n * ```ts\n * upgoose.test();\n * ```\n */\n test(): void {\n if (!this.config) {\n console.warn(\"[Upgoose] Not initialized. Call init() first.\");\n return;\n }\n this.send({\n message: \"Upgoose test event — integration verified\",\n level: \"warning\",\n tags: { upgoose_test: \"true\" },\n fingerprint: \"upgoose-test-event\",\n });\n console.info(\"[Upgoose] Test event sent. Check your project page.\");\n }\n\n /**\n * Capture a message as an error event.\n *\n * ```ts\n * upgoose.captureMessage(\"User hit rate limit\", \"warning\");\n * ```\n */\n captureMessage(\n message: string,\n level: ErrorPayload[\"level\"] = \"error\"\n ): void {\n if (!this.config) return;\n this.send({ message, level });\n }\n\n /**\n * Set user context. Attached to all subsequent events.\n *\n * ```ts\n * upgoose.setUser({ id: '123', email: 'user@example.com' });\n * ```\n */\n setUser(user: UserContext | null): void {\n this.user = user;\n }\n\n /**\n * Set a global tag. Attached to all subsequent events.\n */\n setTag(key: string, value: string): void {\n this.tags[key] = value;\n }\n\n /**\n * Add a breadcrumb manually.\n */\n addBreadcrumb(crumb: Breadcrumb): void {\n const max = this.config?.maxBreadcrumbs ?? 50;\n this.breadcrumbs.push(crumb);\n if (this.breadcrumbs.length > max) {\n this.breadcrumbs = this.breadcrumbs.slice(-max);\n }\n }\n\n /**\n * Tear down the SDK — removes global handlers and breadcrumb instrumentation.\n */\n destroy(): void {\n teardownBreadcrumbs();\n this.removeGlobalHandlers();\n this.config = null;\n this.breadcrumbs = [];\n this.user = null;\n this.tags = {};\n this.initialized = false;\n }\n\n // -- Private --\n\n private onError = (event: ErrorEvent): void => {\n const error = event.error || new Error(event.message || \"Unknown error\");\n this.captureException(error);\n };\n\n private onUnhandledRejection = (event: PromiseRejectionEvent): void => {\n const error = event.reason;\n this.captureException(error);\n };\n\n private setupGlobalHandlers(): void {\n window.addEventListener(\"error\", this.onError);\n window.addEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private removeGlobalHandlers(): void {\n window.removeEventListener(\"error\", this.onError);\n window.removeEventListener(\"unhandledrejection\", this.onUnhandledRejection);\n }\n\n private send(partial: {\n message: string;\n stack?: string;\n level: ErrorPayload[\"level\"];\n tags?: Record<string, string>;\n fingerprint?: string;\n }): void {\n const config = this.config;\n if (!config) return;\n\n // Re-entrancy guard: prevent infinite loops if send() itself throws\n // or if beforeSend throws and triggers the global error handler\n if (this.sending) return;\n this.sending = true;\n\n try {\n // Sample rate check\n if (typeof config.sampleRate === \"number\" && Math.random() > config.sampleRate) {\n return;\n }\n\n const payload: ErrorPayload = {\n message: partial.message.slice(0, 2000),\n stack: partial.stack?.slice(0, 10000),\n level: partial.level,\n environment: config.environment,\n release: config.release,\n tags: { ...this.tags, ...partial.tags },\n context: {\n breadcrumbs: this.breadcrumbs.slice(-30),\n ...(this.user ? { user: this.user } : {}),\n page: {\n url: stripUrlSensitiveParams(location.href),\n title: document.title,\n },\n browser: getBrowserContext(),\n },\n fingerprint: partial.fingerprint,\n };\n\n // beforeSend hook (wrapped to prevent throws from looping)\n let processed: ErrorPayload | null = payload;\n if (config.beforeSend) {\n try {\n processed = config.beforeSend(payload);\n } catch {\n // Drop event if beforeSend throws\n return;\n }\n }\n if (!processed) return;\n\n sendEvent(\n { apiKey: config.apiKey, apiUrl: config.apiUrl },\n processed\n );\n } finally {\n this.sending = false;\n }\n }\n}\n\nfunction normalizeError(error: unknown): { message: string; stack?: string } {\n if (error instanceof Error) {\n return { message: error.message, stack: error.stack };\n }\n if (typeof error === \"string\") {\n return { message: error };\n }\n if (error && typeof error === \"object\" && \"message\" in error) {\n return {\n message: String((error as { message: unknown }).message),\n stack: \"stack\" in error ? String((error as { stack: unknown }).stack) : undefined,\n };\n }\n return { message: String(error) };\n}\n\n/** Strip common sensitive query/hash params from URLs */\nfunction stripUrlSensitiveParams(url: string): string {\n try {\n const u = new URL(url);\n const sensitive = [\"token\", \"key\", \"secret\", \"password\", \"auth\", \"code\", \"session\", \"jwt\", \"access_token\", \"refresh_token\", \"api_key\", \"apikey\"];\n for (const param of [...u.searchParams.keys()]) {\n if (sensitive.some((s) => param.toLowerCase().includes(s))) {\n u.searchParams.set(param, \"[REDACTED]\");\n }\n }\n // Strip hash (often contains tokens in SPAs)\n u.hash = \"\";\n return u.toString();\n } catch {\n return url;\n }\n}\n\nfunction getBrowserContext(): Record<string, string> {\n const ctx: Record<string, string> = {};\n try {\n ctx.userAgent = navigator.userAgent;\n ctx.language = navigator.language;\n ctx.viewport = `${window.innerWidth}x${window.innerHeight}`;\n ctx.screen = `${screen.width}x${screen.height}`;\n } catch {\n // Ignore\n }\n return ctx;\n}\n\n// Singleton instance\nconst upgoose = new Upgoose();\nexport default upgoose;\nexport { upgoose, Upgoose };\n"],"mappings":"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","names":["teardowns","setupBreadcrumbs","config","addBreadcrumb","setupConsole","setupClicks","setupNavigation","setupFetch","setupXHR","teardownBreadcrumbs","fn","add","original","level","orig","args","handler","event","target","tag","text","selector","getSelector","origPush","origReplace","popHandler","origFetch","input","init","rawUrl","method","start","url","scrubUrl","response","err","origOpen","origSend","u","sensitive","param","el","parts","current","i","part","cls","originalFetch","sendEvent","config","payload","url","body","originalFetch","Upgoose","event","error","config","setupBreadcrumbs","crumb","extra","message","stack","normalizeError","level","user","key","value","max","teardownBreadcrumbs","partial","payload","stripUrlSensitiveParams","getBrowserContext","processed","sendEvent","url","u","sensitive","param","ctx","upgoose","index_default"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upgoose/browser",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Upgoose error tracking SDK for browsers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",