@upgoose/browser 0.1.0
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/LICENSE +28 -0
- package/README.md +112 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +118 -0
- package/dist/index.d.ts +118 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Leyline Digital
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @upgoose/browser
|
|
2
|
+
|
|
3
|
+
Lightweight error tracking SDK for browser applications. Captures unhandled exceptions, promise rejections, and manual error reports — with breadcrumbs, PII scrubbing, and zero dependencies.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @upgoose/browser
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import upgoose from '@upgoose/browser';
|
|
15
|
+
|
|
16
|
+
upgoose.init({
|
|
17
|
+
apiKey: 'ug_...', // from your project's DSN
|
|
18
|
+
apiUrl: 'https://api.upgoose.app',
|
|
19
|
+
environment: 'production',
|
|
20
|
+
release: '1.2.3', // optional, enables release tracking
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Errors are captured automatically. That's it.
|
|
25
|
+
|
|
26
|
+
## Manual capture
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// Capture an exception
|
|
30
|
+
try {
|
|
31
|
+
riskyOperation();
|
|
32
|
+
} catch (err) {
|
|
33
|
+
upgoose.captureException(err);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Capture a message
|
|
37
|
+
upgoose.captureMessage("Payment flow failed", "warning");
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## User context
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
upgoose.setUser({ id: '123', email: 'user@example.com' });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Tags
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
upgoose.setTag('team', 'checkout');
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
| Option | Type | Default | Description |
|
|
55
|
+
|--------|------|---------|-------------|
|
|
56
|
+
| `apiKey` | `string` | required | API key from your project DSN |
|
|
57
|
+
| `apiUrl` | `string` | required | Upgoose API base URL |
|
|
58
|
+
| `environment` | `string` | — | e.g. `'production'`, `'staging'` |
|
|
59
|
+
| `release` | `string` | — | App version or git SHA |
|
|
60
|
+
| `sampleRate` | `number` | `1` | 0–1, fraction of events to capture |
|
|
61
|
+
| `maxBreadcrumbs` | `number` | `50` | Max breadcrumbs retained |
|
|
62
|
+
| `beforeSend` | `function` | — | Transform or drop events before send |
|
|
63
|
+
| `captureConsole` | `boolean` | `true` | Log console.error/warn as breadcrumbs |
|
|
64
|
+
| `captureClicks` | `boolean` | `true` | Log click events as breadcrumbs |
|
|
65
|
+
| `captureNavigation` | `boolean` | `true` | Log route changes as breadcrumbs |
|
|
66
|
+
| `captureNetwork` | `boolean` | `true` | Log fetch/XHR as breadcrumbs |
|
|
67
|
+
| `captureUnhandled` | `boolean` | `true` | Global error + rejection handlers |
|
|
68
|
+
|
|
69
|
+
## What gets captured
|
|
70
|
+
|
|
71
|
+
- Unhandled exceptions (`window.onerror`)
|
|
72
|
+
- Unhandled promise rejections
|
|
73
|
+
- Breadcrumbs: clicks, navigation, console, fetch/XHR (with timing)
|
|
74
|
+
- Browser context: user agent, viewport, language
|
|
75
|
+
- Page URL (sensitive query params automatically redacted)
|
|
76
|
+
|
|
77
|
+
## What doesn't get sent
|
|
78
|
+
|
|
79
|
+
- No cookies, localStorage, or session data
|
|
80
|
+
- No DOM snapshots or session replay
|
|
81
|
+
- No performance traces
|
|
82
|
+
- Sensitive URL params (`token`, `key`, `secret`, `password`, `auth`, `session`, `jwt`) are redacted before leaving the browser
|
|
83
|
+
|
|
84
|
+
## Filtering events
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
upgoose.init({
|
|
88
|
+
apiKey: '...',
|
|
89
|
+
apiUrl: '...',
|
|
90
|
+
beforeSend(event) {
|
|
91
|
+
// Drop events from browser extensions
|
|
92
|
+
if (event.stack?.includes('chrome-extension://')) return null;
|
|
93
|
+
// Redact custom fields
|
|
94
|
+
delete event.context?.sensitiveField;
|
|
95
|
+
return event;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Bundle size
|
|
101
|
+
|
|
102
|
+
~3 KB gzipped. Zero dependencies.
|
|
103
|
+
|
|
104
|
+
## Documentation
|
|
105
|
+
|
|
106
|
+
Full integration guide: [upgoose.app/docs](https://upgoose.app/docs#error-tracking)
|
|
107
|
+
|
|
108
|
+
Already using a Sentry SDK? You can also point it at Upgoose with a one-line DSN swap: [Migration guide](https://upgoose.app/docs#migrate-from-sentry)
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
BSD-3-Clause
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +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});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
interface UpgooseConfig {
|
|
2
|
+
/** API key from your Upgoose error project */
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** Upgoose API base URL */
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
/** App release/version (e.g. "1.2.3") */
|
|
7
|
+
release?: string;
|
|
8
|
+
/** Environment (e.g. "production", "staging") */
|
|
9
|
+
environment?: string;
|
|
10
|
+
/** Sample rate 0-1 (1 = capture everything, 0.5 = 50%) */
|
|
11
|
+
sampleRate?: number;
|
|
12
|
+
/** Max breadcrumbs to keep (default: 50) */
|
|
13
|
+
maxBreadcrumbs?: number;
|
|
14
|
+
/** Transform or filter events before sending. Return null to drop. */
|
|
15
|
+
beforeSend?: (event: ErrorPayload) => ErrorPayload | null;
|
|
16
|
+
/** Enable console breadcrumbs (default: true) */
|
|
17
|
+
captureConsole?: boolean;
|
|
18
|
+
/** Enable click breadcrumbs (default: true) */
|
|
19
|
+
captureClicks?: boolean;
|
|
20
|
+
/** Enable navigation breadcrumbs (default: true) */
|
|
21
|
+
captureNavigation?: boolean;
|
|
22
|
+
/** Enable fetch/XHR breadcrumbs (default: true) */
|
|
23
|
+
captureNetwork?: boolean;
|
|
24
|
+
/** Enable global error handlers (default: true) */
|
|
25
|
+
captureUnhandled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
interface Breadcrumb {
|
|
28
|
+
timestamp: number;
|
|
29
|
+
category: "ui" | "navigation" | "console" | "fetch" | "xhr";
|
|
30
|
+
message: string;
|
|
31
|
+
level?: "info" | "warning" | "error";
|
|
32
|
+
data?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
interface UserContext {
|
|
35
|
+
id?: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
username?: string;
|
|
38
|
+
}
|
|
39
|
+
interface ErrorPayload {
|
|
40
|
+
message: string;
|
|
41
|
+
stack?: string;
|
|
42
|
+
level: "error" | "warning" | "fatal";
|
|
43
|
+
environment?: string;
|
|
44
|
+
release?: string;
|
|
45
|
+
tags?: Record<string, string>;
|
|
46
|
+
context?: Record<string, unknown>;
|
|
47
|
+
fingerprint?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare class Upgoose {
|
|
51
|
+
private config;
|
|
52
|
+
private breadcrumbs;
|
|
53
|
+
private user;
|
|
54
|
+
private tags;
|
|
55
|
+
private initialized;
|
|
56
|
+
private sending;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the Upgoose SDK. Call this once, as early as possible.
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* import upgoose from '@upgoose/browser';
|
|
62
|
+
* upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
init(config: UpgooseConfig): void;
|
|
66
|
+
/**
|
|
67
|
+
* Capture an exception manually.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* try {
|
|
71
|
+
* riskyOperation();
|
|
72
|
+
* } catch (err) {
|
|
73
|
+
* upgoose.captureException(err);
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
captureException(error: unknown, extra?: {
|
|
78
|
+
tags?: Record<string, string>;
|
|
79
|
+
fingerprint?: string;
|
|
80
|
+
level?: ErrorPayload["level"];
|
|
81
|
+
}): void;
|
|
82
|
+
/**
|
|
83
|
+
* Capture a message as an error event.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* upgoose.captureMessage("User hit rate limit", "warning");
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
captureMessage(message: string, level?: ErrorPayload["level"]): void;
|
|
90
|
+
/**
|
|
91
|
+
* Set user context. Attached to all subsequent events.
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* upgoose.setUser({ id: '123', email: 'user@example.com' });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
setUser(user: UserContext | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Set a global tag. Attached to all subsequent events.
|
|
100
|
+
*/
|
|
101
|
+
setTag(key: string, value: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Add a breadcrumb manually.
|
|
104
|
+
*/
|
|
105
|
+
addBreadcrumb(crumb: Breadcrumb): void;
|
|
106
|
+
/**
|
|
107
|
+
* Tear down the SDK — removes global handlers and breadcrumb instrumentation.
|
|
108
|
+
*/
|
|
109
|
+
destroy(): void;
|
|
110
|
+
private onError;
|
|
111
|
+
private onUnhandledRejection;
|
|
112
|
+
private setupGlobalHandlers;
|
|
113
|
+
private removeGlobalHandlers;
|
|
114
|
+
private send;
|
|
115
|
+
}
|
|
116
|
+
declare const upgoose: Upgoose;
|
|
117
|
+
|
|
118
|
+
export { type Breadcrumb, type ErrorPayload, Upgoose, type UpgooseConfig, type UserContext, upgoose as default, upgoose };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
interface UpgooseConfig {
|
|
2
|
+
/** API key from your Upgoose error project */
|
|
3
|
+
apiKey: string;
|
|
4
|
+
/** Upgoose API base URL */
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
/** App release/version (e.g. "1.2.3") */
|
|
7
|
+
release?: string;
|
|
8
|
+
/** Environment (e.g. "production", "staging") */
|
|
9
|
+
environment?: string;
|
|
10
|
+
/** Sample rate 0-1 (1 = capture everything, 0.5 = 50%) */
|
|
11
|
+
sampleRate?: number;
|
|
12
|
+
/** Max breadcrumbs to keep (default: 50) */
|
|
13
|
+
maxBreadcrumbs?: number;
|
|
14
|
+
/** Transform or filter events before sending. Return null to drop. */
|
|
15
|
+
beforeSend?: (event: ErrorPayload) => ErrorPayload | null;
|
|
16
|
+
/** Enable console breadcrumbs (default: true) */
|
|
17
|
+
captureConsole?: boolean;
|
|
18
|
+
/** Enable click breadcrumbs (default: true) */
|
|
19
|
+
captureClicks?: boolean;
|
|
20
|
+
/** Enable navigation breadcrumbs (default: true) */
|
|
21
|
+
captureNavigation?: boolean;
|
|
22
|
+
/** Enable fetch/XHR breadcrumbs (default: true) */
|
|
23
|
+
captureNetwork?: boolean;
|
|
24
|
+
/** Enable global error handlers (default: true) */
|
|
25
|
+
captureUnhandled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
interface Breadcrumb {
|
|
28
|
+
timestamp: number;
|
|
29
|
+
category: "ui" | "navigation" | "console" | "fetch" | "xhr";
|
|
30
|
+
message: string;
|
|
31
|
+
level?: "info" | "warning" | "error";
|
|
32
|
+
data?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
interface UserContext {
|
|
35
|
+
id?: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
username?: string;
|
|
38
|
+
}
|
|
39
|
+
interface ErrorPayload {
|
|
40
|
+
message: string;
|
|
41
|
+
stack?: string;
|
|
42
|
+
level: "error" | "warning" | "fatal";
|
|
43
|
+
environment?: string;
|
|
44
|
+
release?: string;
|
|
45
|
+
tags?: Record<string, string>;
|
|
46
|
+
context?: Record<string, unknown>;
|
|
47
|
+
fingerprint?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
declare class Upgoose {
|
|
51
|
+
private config;
|
|
52
|
+
private breadcrumbs;
|
|
53
|
+
private user;
|
|
54
|
+
private tags;
|
|
55
|
+
private initialized;
|
|
56
|
+
private sending;
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the Upgoose SDK. Call this once, as early as possible.
|
|
59
|
+
*
|
|
60
|
+
* ```ts
|
|
61
|
+
* import upgoose from '@upgoose/browser';
|
|
62
|
+
* upgoose.init({ apiKey: 'ug_...', apiUrl: 'https://api.upgoose.app' });
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
init(config: UpgooseConfig): void;
|
|
66
|
+
/**
|
|
67
|
+
* Capture an exception manually.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* try {
|
|
71
|
+
* riskyOperation();
|
|
72
|
+
* } catch (err) {
|
|
73
|
+
* upgoose.captureException(err);
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
captureException(error: unknown, extra?: {
|
|
78
|
+
tags?: Record<string, string>;
|
|
79
|
+
fingerprint?: string;
|
|
80
|
+
level?: ErrorPayload["level"];
|
|
81
|
+
}): void;
|
|
82
|
+
/**
|
|
83
|
+
* Capture a message as an error event.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* upgoose.captureMessage("User hit rate limit", "warning");
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
captureMessage(message: string, level?: ErrorPayload["level"]): void;
|
|
90
|
+
/**
|
|
91
|
+
* Set user context. Attached to all subsequent events.
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* upgoose.setUser({ id: '123', email: 'user@example.com' });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
setUser(user: UserContext | null): void;
|
|
98
|
+
/**
|
|
99
|
+
* Set a global tag. Attached to all subsequent events.
|
|
100
|
+
*/
|
|
101
|
+
setTag(key: string, value: string): void;
|
|
102
|
+
/**
|
|
103
|
+
* Add a breadcrumb manually.
|
|
104
|
+
*/
|
|
105
|
+
addBreadcrumb(crumb: Breadcrumb): void;
|
|
106
|
+
/**
|
|
107
|
+
* Tear down the SDK — removes global handlers and breadcrumb instrumentation.
|
|
108
|
+
*/
|
|
109
|
+
destroy(): void;
|
|
110
|
+
private onError;
|
|
111
|
+
private onUnhandledRejection;
|
|
112
|
+
private setupGlobalHandlers;
|
|
113
|
+
private removeGlobalHandlers;
|
|
114
|
+
private send;
|
|
115
|
+
}
|
|
116
|
+
declare const upgoose: Upgoose;
|
|
117
|
+
|
|
118
|
+
export { type Breadcrumb, type ErrorPayload, Upgoose, type UpgooseConfig, type UserContext, upgoose as default, upgoose };
|
package/dist/index.js
ADDED
|
@@ -0,0 +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};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@upgoose/browser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Upgoose error tracking SDK for browsers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"tsup": "^8.0.0",
|
|
26
|
+
"typescript": "^5.7.2"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"error-tracking",
|
|
30
|
+
"monitoring",
|
|
31
|
+
"upgoose",
|
|
32
|
+
"browser",
|
|
33
|
+
"sdk"
|
|
34
|
+
],
|
|
35
|
+
"license": "BSD-3-Clause",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/randell/upgoose",
|
|
39
|
+
"directory": "packages/sdk-browser"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://upgoose.app/docs#error-tracking",
|
|
42
|
+
"bugs": "https://github.com/randell/upgoose/issues",
|
|
43
|
+
"sideEffects": false
|
|
44
|
+
}
|