browser-error-log 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +98 -0
- package/README.md +3 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.ko.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# browser-error-log
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/hyeonse-swing/browser-error-log/blob/main/packages/browser/README.md) | 한국어
|
|
4
|
+
|
|
5
|
+
직접 관리하는 수집기에 연결하는 작은 브라우저 오류 SDK입니다. 자체 백엔드 또는 저장소에 포함된 SQLite·회전 JSONL 파일 기반 Node.js 수집기를 사용할 수 있습니다.
|
|
6
|
+
|
|
7
|
+
## 사용 이유
|
|
8
|
+
|
|
9
|
+
- **이벤트 목적지를 직접 선택:** 내장 호스팅 endpoint나 별도 분석 목적지는 없습니다. `endpoint` 또는 사용자 지정 `transport`를 지정합니다.
|
|
10
|
+
- **작은 브라우저 의존성:** 런타임 프레임워크 의존성이 없고 ESM, CommonJS, TypeScript 선언을 제공합니다. 저장소는 뷰어·선택적 React 어댑터를 제외한 기본 SDK에 gzip 20 KiB 예산을 적용합니다.
|
|
11
|
+
- **전송 전 제어:** `beforeSend`로 이벤트를 수정·제외하고 `sanitizeUrl`로 경로 식별자를 제거하며 `shouldCaptureHttp`로 필요한 HTTP 응답을 선택합니다.
|
|
12
|
+
- **오류 맥락:** JavaScript 예외, 미처리 Promise, fetch/XHR 실패, 수동 오류에 프로젝트·릴리스·페이지·발생 시각을 기록합니다. Console 수집은 선택 사항입니다.
|
|
13
|
+
- **옮길 수 있는 저장 방식:** SQLite 수집기와 실시간 뷰어, JSONL 로그와 로컬 내보내기, 또는 같은 버전 계약을 따르는 자체 백엔드를 선택합니다.
|
|
14
|
+
|
|
15
|
+
## 설치
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install browser-error-log
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`browser-error-log-protocol`은 npm 의존성으로 설치됩니다. 게시 패키지 대신 로컬 소스를 시험하려면 Node.js 24에서 `npm ci`와 `npm run check:package`를 실행하고 [SDK 가이드](https://github.com/hyeonse-swing/browser-error-log/blob/main/docs/ko/personal-sdk.md)에 따라 프로토콜·브라우저 tarball을 함께 설치하세요.
|
|
22
|
+
|
|
23
|
+
## 세 단계로 연결
|
|
24
|
+
|
|
25
|
+
1. 소스 체크아웃에서 앱의 정확한 Origin을 허용해 수집기를 시작합니다.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
MICRO_PROJECTS=my-web MICRO_ALLOWED_ORIGINS=http://localhost:5173 npm run micro
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. 앱의 브라우저 진입점에서 한 번 초기화합니다.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { init } from 'browser-error-log';
|
|
35
|
+
|
|
36
|
+
export const errorLog = init({
|
|
37
|
+
project: 'my-web',
|
|
38
|
+
endpoint: 'http://127.0.0.1:4319/api/events',
|
|
39
|
+
environment: 'development',
|
|
40
|
+
release: 'my-release',
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
3. 진단 오류를 보내고 `http://127.0.0.1:4319/`에서 확인합니다.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
errorLog.captureException(new Error('Connection check'));
|
|
48
|
+
await errorLog.flush();
|
|
49
|
+
console.log(errorLog.getStats()); // 전달 성공 시 sent: 1
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
앱 수명 동안 클라이언트를 유지하세요. 테스트 종료나 앱 해제 때 `destroy()`는 계측을 제거하고 대기 이벤트를 버립니다. `flush()`는 SDK 전송 시도가 끝났다는 뜻이지 저장 보장이 아닙니다. `getStats()`와 수집기에서 확인하세요. 초기화 이전 오류는 수집하지 못합니다.
|
|
53
|
+
|
|
54
|
+
외부 호스팅은 [자체 호스팅 가이드](https://github.com/hyeonse-swing/browser-error-log/blob/main/docs/ko/micro-server.md)를 참고하세요. 운영 앱에는 HTTPS 수집기, 정확한 허용 Origin, 영속 저장, 뷰어 인증이 필요합니다. `localhost`와 `127.0.0.1`은 서로 다른 Origin입니다. 뷰어 암호는 서버에만 두고 SDK 설정에 넣지 마세요.
|
|
55
|
+
|
|
56
|
+
## 처리한 오류 기록
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
try {
|
|
60
|
+
await loadDashboard();
|
|
61
|
+
} catch (error) {
|
|
62
|
+
errorLog.captureException(error, { context: { action: 'load-dashboard' } });
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
선택적인 `browser-error-log-react` 패키지는 Error Boundary를 제공합니다. 기본 SDK에는 React가 필요하지 않습니다. SSR에서 import·초기화할 수 있지만 서버 오류는 수집하지 않으므로 프레임워크의 브라우저/클라이언트 진입점에서 초기화하세요.
|
|
67
|
+
|
|
68
|
+
## 수집 제어
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
const errorLog = init({
|
|
72
|
+
project: 'my-web',
|
|
73
|
+
endpoint: '/api/events', // 자체 수집기 또는 같은 Origin의 프록시
|
|
74
|
+
captureConsole: false,
|
|
75
|
+
beforeSend(event) {
|
|
76
|
+
if (event.message.includes('Expected diagnostic')) return null;
|
|
77
|
+
return event;
|
|
78
|
+
},
|
|
79
|
+
sanitizeUrl(url) {
|
|
80
|
+
return url.replace(/\/users\/[^/?#]+/g, '/users/:id');
|
|
81
|
+
},
|
|
82
|
+
shouldCaptureHttp(status) {
|
|
83
|
+
return status >= 500;
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
기본 HTTP 수집은 5xx와 네트워크 실패를 포함하고 의도적인 취소와 수집기 endpoint를 제외합니다. SDK는 URL 쿼리·해시·자격 정보와 흔한 이메일/토큰 패턴을 제거합니다. 요청/응답 본문, 쿠키, DOM, 폼 입력, 세션 녹화는 수집하지 않습니다. 오류 메시지, URL 경로, 스택, 사용자 지정 context에는 민감정보가 남을 수 있으므로 앱별 필터를 설정하세요. `beforeSend`나 `sanitizeUrl`이 실패하면 이벤트를 버립니다.
|
|
89
|
+
|
|
90
|
+
사용자 지정 `transport(batch, { signal })`에서는 요청에 `signal`을 전달하고 전송 실패 시 reject하세요. 자동 네트워크 수집을 원하면 자기 전송을 제외할 `endpoint`를 지정해야 합니다. 없으면 사용자 지정 transport에서 네트워크 수집을 끕니다. 기본 전송은 credentials를 보내지 않습니다.
|
|
91
|
+
|
|
92
|
+
## 전달 제한
|
|
93
|
+
|
|
94
|
+
기본 큐는 50건, flush 주기는 10초입니다. 배치는 최대 20건 또는 48,000바이트입니다. 각 시도는 5초 제한이고 재시도 가능한 실패는 최대 두 번 재시도합니다. `maxQueueSize`, `flushIntervalMs`, `transportTimeoutMs`는 정해진 범위 안에서 조정할 수 있습니다. 오프라인, 탭 종료, 버퍼 초과, 재시도 소진으로 이벤트가 유실될 수 있습니다. 영속 오프라인 큐나 정확히 한 번의 전달 보장은 없습니다.
|
|
95
|
+
|
|
96
|
+
## 호스팅과 비용
|
|
97
|
+
|
|
98
|
+
이 프로젝트는 관리형 모니터링 서비스가 아닌 소프트웨어를 제공합니다. 인프라는 포함되지 않습니다. 호스팅, 서버, 데이터베이스, 스토리지, 대역폭, 도메인, 백업 및 제3자 서비스는 각 운영자가 선택하고 비용을 부담합니다. 요금은 해당 제공업체가 이 프로젝트 및 유지관리자와 독립적으로 청구합니다. 자체 호스팅이 무료 인프라를 뜻하지는 않습니다.
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# browser-error-log
|
|
2
2
|
|
|
3
|
+
English | [한국어](https://github.com/hyeonse-swing/browser-error-log/blob/main/packages/browser/README.ko.md)
|
|
4
|
+
|
|
3
5
|
A small browser error SDK for a collector you control. Use your own backend or the repository's self-hosted Node.js collector, which stores events in SQLite or rotating JSONL files.
|
|
4
6
|
|
|
5
7
|
## Why use it?
|
|
@@ -16,7 +18,7 @@ A small browser error SDK for a collector you control. Use your own backend or t
|
|
|
16
18
|
npm install browser-error-log
|
|
17
19
|
```
|
|
18
20
|
|
|
19
|
-
The `browser-error-log-protocol` dependency resolves from npm. For
|
|
21
|
+
The `browser-error-log-protocol` dependency resolves from npm. For local source-checkout development, use Node.js 24, `npm ci`, and `npm run check:package`, then install the protocol and browser tarballs together as shown in the [SDK guide](https://github.com/hyeonse-swing/browser-error-log/blob/main/docs/personal-sdk.md).
|
|
20
22
|
|
|
21
23
|
## Connect in three steps
|
|
22
24
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var S=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var H=(r,e,t)=>e in r?S(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var X=(r,e)=>{for(var t in e)S(r,t,{get:e[t],enumerable:!0})},D=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of P(e))!z.call(r,o)&&o!==t&&S(r,o,{get:()=>e[o],enumerable:!(n=O(e,o))||n.enumerable});return r};var V=r=>D(S({},"__esModule",{value:!0}),r);var s=(r,e,t)=>H(r,typeof e!="symbol"?e+"":e,t);var ne={};X(ne,{init:()=>te});module.exports=V(ne);var q=[250,500],F=/(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i,M=new Set(["javascript","promise","react","console","http","network","manual"]),_=new Set(["browser","webview-ios","webview-android","unknown"]),Y=0;function c(r,e){try{return r==null?void 0:Reflect.get(Object(r),e)}catch{return}}function m(r){try{return String(r)}catch{return"[unavailable]"}}function W(r,e){return m(r).slice(0,e)}function w(){try{return performance.now()}catch{return Date.now()}}function N(){try{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch{}return`${Date.now().toString(36)}-${(++Y).toString(36)}-${Math.random().toString(36).slice(2,10)}`}function x(r,e){try{if(e?.aborted)return!0}catch{}return c(r,"name")==="AbortError"||c(r,"code")===20}function U(r){if(r==null)return{message:"Unknown error"};let e=c(r,"message"),t=c(r,"name"),n=c(r,"stack");return{message:e==null?m(r):m(e),...t==null?{}:{name:m(t)},...n==null?{}:{stack:m(n)}}}function C(r){return new TextEncoder().encode(r).length}function b(r,e,t,n){return typeof r=="number"&&Number.isFinite(r)?Math.min(t,Math.max(e,Math.floor(r))):n}var A=class extends Error{constructor(t){super(`Collector responded ${t}`);s(this,"status",t)}},k=class extends Error{},B=class{constructor(e){s(this,"active");s(this,"endpoint");s(this,"options");s(this,"networkCaptureEnabled");s(this,"transportTimeoutMs");s(this,"startedAt",w());s(this,"viewId",N());s(this,"seenErrors",new WeakSet);s(this,"queue",[]);s(this,"maxQueueSize");s(this,"cancelWaits",new Set);s(this,"controllers",new Set);s(this,"destroySignal");s(this,"resolveDestroy");s(this,"interval");s(this,"flushing");s(this,"sequence",0);s(this,"inFlight",0);s(this,"sent",0);s(this,"dropped",0);s(this,"destroyed",!1);s(this,"onError",e=>{this.captureException(e.error??e.message??"Script error",{type:"javascript"})});s(this,"onRejection",e=>{this.captureException(e.reason,{type:"promise"})});s(this,"onVisibility",()=>{document.visibilityState==="hidden"&&this.tryBeacon()});if(this.options=e,this.active=e.enabled!==!1&&typeof window<"u"&&typeof document<"u",this.networkCaptureEnabled=e.captureNetwork!==!1&&(!e.transport||!!e.endpoint),this.maxQueueSize=b(e.maxQueueSize,1,100,50),this.transportTimeoutMs=b(e.transportTimeoutMs,250,6e4,5e3),this.destroySignal=new Promise(t=>{this.resolveDestroy=t}),!!this.active){if(!e.project||typeof e.project!="string")throw new Error("project is required");if(!e.transport&&!e.endpoint)throw new Error("endpoint or transport is required");this.endpoint=e.endpoint?this.absoluteUrl(e.endpoint):void 0,window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onRejection),document.addEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&Q(this),e.captureConsole===!0&&Z(this),this.interval=setInterval(()=>{this.flush()},b(e.flushIntervalMs,10,6e4,1e4))}}captureException(e,t={}){if(!(!this.active||this.destroyed||x(e)))try{if(e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.has(e))return;let n=U(e),o=M.has(t.type)?t.type:"manual",i=this.makeEvent(o,n.message,{name:n.name,stack:n.stack,componentStack:t.componentStack,context:t.context});this.enqueue(i)&&e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.add(e)}catch(n){n instanceof k&&this.dropped++}}reportNetwork(e,t,n,o,i){if(!(this.destroyed||!this.active||this.isCollectorUrl(t)||x(i)))try{if(n!==void 0&&!(this.options.shouldCaptureHttp?this.options.shouldCaptureHttp(n,t,e):n>=500))return;let a={method:W(e||"GET",16).toUpperCase(),url:t,status:n,durationMs:o},d=i===void 0?void 0:U(i),l=this.makeEvent(n===void 0?"network":"http",n===void 0?d?.message||"Network request failed":`HTTP ${n}`,{name:d?.name,network:a});this.enqueue(l)}catch{this.dropped++}}captureConsole(e){if(!(!this.active||this.destroyed))try{this.enqueue(this.makeEvent("console",e.map(m).join(" ")))}catch{this.dropped++}}flush(){return!this.active||this.destroyed?Promise.resolve():this.flushing?this.flushing:(this.flushing=this.drain().finally(()=>{this.flushing=void 0}),this.flushing)}destroy(){if(!this.destroyed){this.destroyed=!0,this.interval&&clearInterval(this.interval),this.active&&(window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onRejection),document.removeEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&$(this),this.options.captureConsole===!0&&K(this)),this.dropped+=this.queue.length+this.inFlight,this.queue.length=0;for(let e of this.cancelWaits)e();for(let e of this.controllers)e.abort();this.resolveDestroy(I)}}getStats(){return{queued:this.queue.length,sent:this.sent,dropped:this.dropped}}absoluteUrl(e){try{return new URL(e,window.location.href).href}catch{return e}}isCollectorUrl(e){if(!this.endpoint)return!1;try{let t=new URL(e,window.location.href),n=new URL(this.endpoint,window.location.href);return t.origin===n.origin&&t.pathname===n.pathname}catch{return e===this.endpoint}}cleanUrl(e){let t=m(e);if(this.options.sanitizeUrl){try{t=this.options.sanitizeUrl(t)}catch{throw new k("URL sanitizer failed")}if(typeof t!="string"||!t)throw new k("URL sanitizer returned no URL")}try{let n=new URL(t,window.location.href);return n.protocol!=="http:"&&n.protocol!=="https:"?"[unsupported URL]":(n.username="",n.password="",n.search="",n.hash="",this.cleanText(n.href,1024,!1))}catch{return this.cleanText(t,1024,!1).split(/[?#]/,1)[0]}}cleanText(e,t,n=!0){let o=m(e);return n&&(o=o.replace(/https?:\/\/[^\s<>"'()[\]]+/gi,i=>this.cleanUrl(i))),o.replace(/\bBearer\s+[A-Za-z0-9._~+\/-]+/gi,"Bearer [REDACTED]").replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,"[REDACTED_EMAIL]").slice(0,t)}cleanContext(e){if(!e||typeof e!="object")return;let t={},n;try{n=Object.keys(e)}catch{return}for(let o of n){if(Object.keys(t).length>=12)break;if(F.test(o))continue;let i=c(e,o),a=this.cleanText(o,80);typeof i=="string"?t[a]=this.cleanText(i,500):(typeof i=="number"&&Number.isFinite(i)||typeof i=="boolean"||i===null)&&(t[a]=i)}return Object.keys(t).length?t:void 0}cleanNetwork(e){if(!e||typeof e!="object")return;let t=c(e,"status"),n=typeof t=="number"&&Number.isInteger(t)&&t>=100&&t<=599?t:void 0;return{method:this.cleanText(c(e,"method")??"GET",16).toUpperCase(),url:this.cleanUrl(c(e,"url")??""),...n===void 0?{}:{status:n},durationMs:b(c(e,"durationMs"),0,36e5,0)}}makeEvent(e,t,n={}){let o=c(navigator,"userAgent"),i=this.cleanContext(n.context),a=this.cleanNetwork(n.network);return{schemaVersion:1,eventId:N(),project:this.cleanText(this.options.project,80),environment:this.cleanText(this.options.environment??"development",80),release:this.cleanText(this.options.release??"unknown",120),sdkVersion:"0.1.0",occurredAt:new Date().toISOString(),viewId:this.viewId,sequence:++this.sequence,elapsedMs:b(w()-this.startedAt,0,36e5,0),type:e,message:this.cleanText(t,1024),...n.name===void 0?{}:{name:this.cleanText(n.name,100)},...n.stack===void 0?{}:{stack:this.cleanText(n.stack,4096)},...n.componentStack===void 0?{}:{componentStack:this.cleanText(n.componentStack,2048)},page:this.cleanUrl(window.location.href),runtime:_.has(this.options.runtime)?this.options.runtime:"browser",...typeof o=="string"?{browser:this.cleanText(o,300)}:{},...i?{context:i}:{},...a?{network:a}:{}}}sanitizeReplacement(e,t){let n=c(e,"type"),o=c(e,"runtime"),i=c(e,"name"),a=c(e,"stack"),d=c(e,"componentStack"),l=c(e,"browser"),u=this.cleanContext(c(e,"context")),p=this.cleanNetwork(c(e,"network"));return{...t,type:M.has(n)?n:t.type,message:this.cleanText(c(e,"message")??t.message,1024),...i===void 0?{name:t.name}:{name:this.cleanText(i,100)},...a===void 0?{stack:t.stack}:{stack:this.cleanText(a,4096)},...d===void 0?{componentStack:t.componentStack}:{componentStack:this.cleanText(d,2048)},page:this.cleanUrl(c(e,"page")??t.page),runtime:_.has(o)?o:t.runtime,...l===void 0?{browser:t.browser}:{browser:this.cleanText(l,300)},...u?{context:u}:{context:void 0},...p?{network:p}:{network:void 0}}}enqueue(e){if(this.destroyed)return!1;let t=e;if(this.options.beforeSend)try{let n={...e,context:e.context&&{...e.context},network:e.network&&{...e.network}},o=this.options.beforeSend(n);if(o===!1||o===null)return!1;t=this.sanitizeReplacement(o===void 0?n:o,e)}catch{return this.dropped++,!1}return C(JSON.stringify(t))>48e3?(this.dropped++,!1):(this.queue.length>=this.maxQueueSize&&(this.queue.shift(),this.dropped++),this.queue.push(t),this.queue.length>=this.maxQueueSize&&this.flush(),!0)}takeBatch(e=20,t=48e3){let n=[],o=C('{"schemaVersion":1,"events":[]}');for(;this.queue.length&&n.length<e;){let i=this.queue[0],a=C(JSON.stringify(i))+(n.length?1:0);if(n.length&&o+a>t)break;if(!n.length&&o+a>t){this.queue.shift(),this.dropped++;continue}n.push(this.queue.shift()),o+=a}return{schemaVersion:1,events:n}}async drain(){for(;!this.destroyed&&this.queue.length;){let e=this.takeBatch();if(e.events.length){this.inFlight+=e.events.length;try{await this.sendWithRetry(e)}finally{this.inFlight-=e.events.length}}}}async sendWithRetry(e){for(let t=0;t<=q.length&&!this.destroyed;t++){let n=new AbortController;this.controllers.add(n);let o,i=new Promise((d,l)=>{o=setTimeout(()=>{n.abort(),l(new Error("Collector transport timed out"))},this.transportTimeoutMs)}),a;try{if(await Promise.race([this.send(e,n.signal),i,this.destroySignal])===I||this.destroyed)return;this.sent+=e.events.length;return}catch(d){if(this.destroyed)return;let l=c(d,"status");if(!(typeof l!="number"||l===429||l>=500)||t===q.length)break;a=q[t]}finally{o&&clearTimeout(o),this.controllers.delete(n)}a!==void 0&&await this.wait(a)}this.destroyed||(this.dropped+=e.events.length)}async send(e,t){if(this.options.transport){await this.options.transport(e,{signal:t});return}let n=await window.fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"omit",signal:t});if(!n.ok)throw new A(n.status)}wait(e){return new Promise(t=>{let n=()=>{clearTimeout(o),this.cancelWaits.delete(n),t()},o=setTimeout(n,e);this.cancelWaits.add(n)})}tryBeacon(){if(!(this.destroyed||this.options.transport||!this.endpoint||!this.queue.length))try{let e=new URL(this.endpoint);if(e.origin!==window.location.origin||e.search||e.hash||e.username||e.password||typeof navigator.sendBeacon!="function")return;let t=this.queue.slice(0,10);for(;t.length>0;){let o=JSON.stringify({schemaVersion:1,events:t});if(C(o)<=24e3){navigator.sendBeacon(this.endpoint,new Blob([o],{type:"application/json"}))&&this.queue.splice(0,t.length);return}t.pop()}}catch{}}},I=Symbol("destroyed"),T,R;function Q(r){T||(T=J()),T.clients.add(r)}function $(r){let e=T;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),T=void 0))}function G(r,e){let t=typeof Request<"u"&&r instanceof Request?r:void 0;return{url:t?t.url:m(r),method:m(e?.method??t?.method??"GET"),signal:e?.signal??t?.signal}}function J(){let r=new Set,e=window.fetch,t;typeof e=="function"&&(t=function(...u){if(!r.size)return Reflect.apply(e,this,u);let p=w(),f;try{f=G(u[0],u[1])}catch{f={url:"",method:"GET"}}let g;try{g=Reflect.apply(e,this,u)}catch(h){if(!x(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h}return g.then(h=>{try{let v=h.status;for(let y of r)y.reportNetwork(f.method,f.url,v,w()-p)}catch{}return h},h=>{if(!x(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h})},window.fetch=t);let n=typeof XMLHttpRequest>"u"?void 0:XMLHttpRequest.prototype,o=n?.open,i=n?.send,a=new WeakMap,d,l;return n&&o&&i&&(d=(function(...u){let p=Reflect.apply(o,this,u);try{a.set(this,{method:m(u[0]),url:m(u[1])})}catch{}return p}),l=function(...u){if(!r.size)return Reflect.apply(i,this,u);let p=a.get(this);if(!p)return Reflect.apply(i,this,u);let f=w(),g=!1,h=!1,v=()=>{g=!0},y=()=>{h=!0},L=()=>{if(this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",L),!g)try{let E=this.status;if(E===0&&!h)return;for(let j of r)j.reportNetwork(p.method,p.url,E===0?void 0:E,w()-f)}catch{}};try{this.addEventListener("abort",v),this.addEventListener("error",y),this.addEventListener("timeout",y),this.addEventListener("loadend",L)}catch{return Reflect.apply(i,this,u)}try{return Reflect.apply(i,this,u)}catch(E){throw this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",L),E}},n.open=d,n.send=l),{clients:r,cleanup(){t&&window.fetch===t&&(window.fetch=e),n&&d&&n.open===d&&(n.open=o),n&&l&&n.send===l&&(n.send=i)}}}function Z(r){R||(R=ee()),R.clients.add(r)}function K(r){let e=R;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),R=void 0))}function ee(){let r=new Set,e=console.error,t=function(...n){try{return Reflect.apply(e,this,n)}finally{for(let o of r)o.captureConsole(n)}};return console.error=t,{clients:r,cleanup(){console.error===t&&(console.error=e)}}}function te(r){return new B(r)}
|
|
1
|
+
"use strict";var S=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var H=(r,e,t)=>e in r?S(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var X=(r,e)=>{for(var t in e)S(r,t,{get:e[t],enumerable:!0})},D=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of P(e))!z.call(r,o)&&o!==t&&S(r,o,{get:()=>e[o],enumerable:!(n=O(e,o))||n.enumerable});return r};var V=r=>D(S({},"__esModule",{value:!0}),r);var s=(r,e,t)=>H(r,typeof e!="symbol"?e+"":e,t);var ne={};X(ne,{init:()=>te});module.exports=V(ne);var q=[250,500],F=/(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i,M=new Set(["javascript","promise","react","console","http","network","manual"]),_=new Set(["browser","webview-ios","webview-android","unknown"]),Y=0;function c(r,e){try{return r==null?void 0:Reflect.get(Object(r),e)}catch{return}}function m(r){try{return String(r)}catch{return"[unavailable]"}}function W(r,e){return m(r).slice(0,e)}function w(){try{return performance.now()}catch{return Date.now()}}function N(){try{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch{}return`${Date.now().toString(36)}-${(++Y).toString(36)}-${Math.random().toString(36).slice(2,10)}`}function x(r,e){try{if(e?.aborted)return!0}catch{}return c(r,"name")==="AbortError"||c(r,"code")===20}function U(r){if(r==null)return{message:"Unknown error"};let e=c(r,"message"),t=c(r,"name"),n=c(r,"stack");return{message:e==null?m(r):m(e),...t==null?{}:{name:m(t)},...n==null?{}:{stack:m(n)}}}function C(r){return new TextEncoder().encode(r).length}function b(r,e,t,n){return typeof r=="number"&&Number.isFinite(r)?Math.min(t,Math.max(e,Math.floor(r))):n}var A=class extends Error{constructor(t){super(`Collector responded ${t}`);s(this,"status",t)}},k=class extends Error{},B=class{constructor(e){s(this,"active");s(this,"endpoint");s(this,"options");s(this,"networkCaptureEnabled");s(this,"transportTimeoutMs");s(this,"startedAt",w());s(this,"viewId",N());s(this,"seenErrors",new WeakSet);s(this,"queue",[]);s(this,"maxQueueSize");s(this,"cancelWaits",new Set);s(this,"controllers",new Set);s(this,"destroySignal");s(this,"resolveDestroy");s(this,"interval");s(this,"flushing");s(this,"sequence",0);s(this,"inFlight",0);s(this,"sent",0);s(this,"dropped",0);s(this,"destroyed",!1);s(this,"onError",e=>{this.captureException(e.error??e.message??"Script error",{type:"javascript"})});s(this,"onRejection",e=>{this.captureException(e.reason,{type:"promise"})});s(this,"onVisibility",()=>{document.visibilityState==="hidden"&&this.tryBeacon()});if(this.options=e,this.active=e.enabled!==!1&&typeof window<"u"&&typeof document<"u",this.networkCaptureEnabled=e.captureNetwork!==!1&&(!e.transport||!!e.endpoint),this.maxQueueSize=b(e.maxQueueSize,1,100,50),this.transportTimeoutMs=b(e.transportTimeoutMs,250,6e4,5e3),this.destroySignal=new Promise(t=>{this.resolveDestroy=t}),!!this.active){if(!e.project||typeof e.project!="string")throw new Error("project is required");if(!e.transport&&!e.endpoint)throw new Error("endpoint or transport is required");this.endpoint=e.endpoint?this.absoluteUrl(e.endpoint):void 0,window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onRejection),document.addEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&Q(this),e.captureConsole===!0&&Z(this),this.interval=setInterval(()=>{this.flush()},b(e.flushIntervalMs,10,6e4,1e4))}}captureException(e,t={}){if(!(!this.active||this.destroyed||x(e)))try{if(e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.has(e))return;let n=U(e),o=M.has(t.type)?t.type:"manual",i=this.makeEvent(o,n.message,{name:n.name,stack:n.stack,componentStack:t.componentStack,context:t.context});this.enqueue(i)&&e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.add(e)}catch(n){n instanceof k&&this.dropped++}}reportNetwork(e,t,n,o,i){if(!(this.destroyed||!this.active||this.isCollectorUrl(t)||x(i)))try{if(n!==void 0&&!(this.options.shouldCaptureHttp?this.options.shouldCaptureHttp(n,t,e):n>=500))return;let a={method:W(e||"GET",16).toUpperCase(),url:t,status:n,durationMs:o},d=i===void 0?void 0:U(i),l=this.makeEvent(n===void 0?"network":"http",n===void 0?d?.message||"Network request failed":`HTTP ${n}`,{name:d?.name,network:a});this.enqueue(l)}catch{this.dropped++}}captureConsole(e){if(!(!this.active||this.destroyed))try{this.enqueue(this.makeEvent("console",e.map(m).join(" ")))}catch{this.dropped++}}flush(){return!this.active||this.destroyed?Promise.resolve():this.flushing?this.flushing:(this.flushing=this.drain().finally(()=>{this.flushing=void 0}),this.flushing)}destroy(){if(!this.destroyed){this.destroyed=!0,this.interval&&clearInterval(this.interval),this.active&&(window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onRejection),document.removeEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&$(this),this.options.captureConsole===!0&&K(this)),this.dropped+=this.queue.length+this.inFlight,this.queue.length=0;for(let e of this.cancelWaits)e();for(let e of this.controllers)e.abort();this.resolveDestroy(I)}}getStats(){return{queued:this.queue.length,sent:this.sent,dropped:this.dropped}}absoluteUrl(e){try{return new URL(e,window.location.href).href}catch{return e}}isCollectorUrl(e){if(!this.endpoint)return!1;try{let t=new URL(e,window.location.href),n=new URL(this.endpoint,window.location.href);return t.origin===n.origin&&t.pathname===n.pathname}catch{return e===this.endpoint}}cleanUrl(e){let t=m(e);if(this.options.sanitizeUrl){try{t=this.options.sanitizeUrl(t)}catch{throw new k("URL sanitizer failed")}if(typeof t!="string"||!t)throw new k("URL sanitizer returned no URL")}try{let n=new URL(t,window.location.href);return n.protocol!=="http:"&&n.protocol!=="https:"?"[unsupported URL]":(n.username="",n.password="",n.search="",n.hash="",this.cleanText(n.href,1024,!1))}catch{return this.cleanText(t,1024,!1).split(/[?#]/,1)[0]}}cleanText(e,t,n=!0){let o=m(e);return n&&(o=o.replace(/https?:\/\/[^\s<>"'()[\]]+/gi,i=>this.cleanUrl(i))),o.replace(/\bBearer\s+[A-Za-z0-9._~+\/-]+/gi,"Bearer [REDACTED]").replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,"[REDACTED_EMAIL]").slice(0,t)}cleanContext(e){if(!e||typeof e!="object")return;let t={},n;try{n=Object.keys(e)}catch{return}for(let o of n){if(Object.keys(t).length>=12)break;if(F.test(o))continue;let i=c(e,o),a=this.cleanText(o,80);typeof i=="string"?t[a]=this.cleanText(i,500):(typeof i=="number"&&Number.isFinite(i)||typeof i=="boolean"||i===null)&&(t[a]=i)}return Object.keys(t).length?t:void 0}cleanNetwork(e){if(!e||typeof e!="object")return;let t=c(e,"status"),n=typeof t=="number"&&Number.isInteger(t)&&t>=100&&t<=599?t:void 0;return{method:this.cleanText(c(e,"method")??"GET",16).toUpperCase(),url:this.cleanUrl(c(e,"url")??""),...n===void 0?{}:{status:n},durationMs:b(c(e,"durationMs"),0,36e5,0)}}makeEvent(e,t,n={}){let o=c(navigator,"userAgent"),i=this.cleanContext(n.context),a=this.cleanNetwork(n.network);return{schemaVersion:1,eventId:N(),project:this.cleanText(this.options.project,80),environment:this.cleanText(this.options.environment??"development",80),release:this.cleanText(this.options.release??"unknown",120),sdkVersion:"0.1.1",occurredAt:new Date().toISOString(),viewId:this.viewId,sequence:++this.sequence,elapsedMs:b(w()-this.startedAt,0,36e5,0),type:e,message:this.cleanText(t,1024),...n.name===void 0?{}:{name:this.cleanText(n.name,100)},...n.stack===void 0?{}:{stack:this.cleanText(n.stack,4096)},...n.componentStack===void 0?{}:{componentStack:this.cleanText(n.componentStack,2048)},page:this.cleanUrl(window.location.href),runtime:_.has(this.options.runtime)?this.options.runtime:"browser",...typeof o=="string"?{browser:this.cleanText(o,300)}:{},...i?{context:i}:{},...a?{network:a}:{}}}sanitizeReplacement(e,t){let n=c(e,"type"),o=c(e,"runtime"),i=c(e,"name"),a=c(e,"stack"),d=c(e,"componentStack"),l=c(e,"browser"),u=this.cleanContext(c(e,"context")),p=this.cleanNetwork(c(e,"network"));return{...t,type:M.has(n)?n:t.type,message:this.cleanText(c(e,"message")??t.message,1024),...i===void 0?{name:t.name}:{name:this.cleanText(i,100)},...a===void 0?{stack:t.stack}:{stack:this.cleanText(a,4096)},...d===void 0?{componentStack:t.componentStack}:{componentStack:this.cleanText(d,2048)},page:this.cleanUrl(c(e,"page")??t.page),runtime:_.has(o)?o:t.runtime,...l===void 0?{browser:t.browser}:{browser:this.cleanText(l,300)},...u?{context:u}:{context:void 0},...p?{network:p}:{network:void 0}}}enqueue(e){if(this.destroyed)return!1;let t=e;if(this.options.beforeSend)try{let n={...e,context:e.context&&{...e.context},network:e.network&&{...e.network}},o=this.options.beforeSend(n);if(o===!1||o===null)return!1;t=this.sanitizeReplacement(o===void 0?n:o,e)}catch{return this.dropped++,!1}return C(JSON.stringify(t))>48e3?(this.dropped++,!1):(this.queue.length>=this.maxQueueSize&&(this.queue.shift(),this.dropped++),this.queue.push(t),this.queue.length>=this.maxQueueSize&&this.flush(),!0)}takeBatch(e=20,t=48e3){let n=[],o=C('{"schemaVersion":1,"events":[]}');for(;this.queue.length&&n.length<e;){let i=this.queue[0],a=C(JSON.stringify(i))+(n.length?1:0);if(n.length&&o+a>t)break;if(!n.length&&o+a>t){this.queue.shift(),this.dropped++;continue}n.push(this.queue.shift()),o+=a}return{schemaVersion:1,events:n}}async drain(){for(;!this.destroyed&&this.queue.length;){let e=this.takeBatch();if(e.events.length){this.inFlight+=e.events.length;try{await this.sendWithRetry(e)}finally{this.inFlight-=e.events.length}}}}async sendWithRetry(e){for(let t=0;t<=q.length&&!this.destroyed;t++){let n=new AbortController;this.controllers.add(n);let o,i=new Promise((d,l)=>{o=setTimeout(()=>{n.abort(),l(new Error("Collector transport timed out"))},this.transportTimeoutMs)}),a;try{if(await Promise.race([this.send(e,n.signal),i,this.destroySignal])===I||this.destroyed)return;this.sent+=e.events.length;return}catch(d){if(this.destroyed)return;let l=c(d,"status");if(!(typeof l!="number"||l===429||l>=500)||t===q.length)break;a=q[t]}finally{o&&clearTimeout(o),this.controllers.delete(n)}a!==void 0&&await this.wait(a)}this.destroyed||(this.dropped+=e.events.length)}async send(e,t){if(this.options.transport){await this.options.transport(e,{signal:t});return}let n=await window.fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"omit",signal:t});if(!n.ok)throw new A(n.status)}wait(e){return new Promise(t=>{let n=()=>{clearTimeout(o),this.cancelWaits.delete(n),t()},o=setTimeout(n,e);this.cancelWaits.add(n)})}tryBeacon(){if(!(this.destroyed||this.options.transport||!this.endpoint||!this.queue.length))try{let e=new URL(this.endpoint);if(e.origin!==window.location.origin||e.search||e.hash||e.username||e.password||typeof navigator.sendBeacon!="function")return;let t=this.queue.slice(0,10);for(;t.length>0;){let o=JSON.stringify({schemaVersion:1,events:t});if(C(o)<=24e3){navigator.sendBeacon(this.endpoint,new Blob([o],{type:"application/json"}))&&this.queue.splice(0,t.length);return}t.pop()}}catch{}}},I=Symbol("destroyed"),T,R;function Q(r){T||(T=J()),T.clients.add(r)}function $(r){let e=T;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),T=void 0))}function G(r,e){let t=typeof Request<"u"&&r instanceof Request?r:void 0;return{url:t?t.url:m(r),method:m(e?.method??t?.method??"GET"),signal:e?.signal??t?.signal}}function J(){let r=new Set,e=window.fetch,t;typeof e=="function"&&(t=function(...u){if(!r.size)return Reflect.apply(e,this,u);let p=w(),f;try{f=G(u[0],u[1])}catch{f={url:"",method:"GET"}}let g;try{g=Reflect.apply(e,this,u)}catch(h){if(!x(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h}return g.then(h=>{try{let v=h.status;for(let y of r)y.reportNetwork(f.method,f.url,v,w()-p)}catch{}return h},h=>{if(!x(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h})},window.fetch=t);let n=typeof XMLHttpRequest>"u"?void 0:XMLHttpRequest.prototype,o=n?.open,i=n?.send,a=new WeakMap,d,l;return n&&o&&i&&(d=(function(...u){let p=Reflect.apply(o,this,u);try{a.set(this,{method:m(u[0]),url:m(u[1])})}catch{}return p}),l=function(...u){if(!r.size)return Reflect.apply(i,this,u);let p=a.get(this);if(!p)return Reflect.apply(i,this,u);let f=w(),g=!1,h=!1,v=()=>{g=!0},y=()=>{h=!0},L=()=>{if(this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",L),!g)try{let E=this.status;if(E===0&&!h)return;for(let j of r)j.reportNetwork(p.method,p.url,E===0?void 0:E,w()-f)}catch{}};try{this.addEventListener("abort",v),this.addEventListener("error",y),this.addEventListener("timeout",y),this.addEventListener("loadend",L)}catch{return Reflect.apply(i,this,u)}try{return Reflect.apply(i,this,u)}catch(E){throw this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",L),E}},n.open=d,n.send=l),{clients:r,cleanup(){t&&window.fetch===t&&(window.fetch=e),n&&d&&n.open===d&&(n.open=o),n&&l&&n.send===l&&(n.send=i)}}}function Z(r){R||(R=ee()),R.clients.add(r)}function K(r){let e=R;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),R=void 0))}function ee(){let r=new Set,e=console.error,t=function(...n){try{return Reflect.apply(e,this,n)}finally{for(let o of r)o.captureConsole(n)}};return console.error=t,{clients:r,cleanup(){console.error===t&&(console.error=e)}}}function te(r){return new B(r)}
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ErrorEventRecord, EventBatch, EventType, Runtime } from 'browser-error-log-protocol';\n\nexport type { ErrorEventRecord, EventBatch } from 'browser-error-log-protocol';\n\nexport interface CaptureExtra {\n type?: EventType;\n context?: Record<string, unknown>;\n componentStack?: string;\n}\n\nexport interface TransportContext { signal: AbortSignal }\n\nexport interface BrowserErrorLogOptions {\n project: string;\n environment?: string;\n release?: string;\n endpoint?: string;\n transport?: (batch: EventBatch, context: TransportContext) => void | Promise<void>;\n runtime?: Runtime;\n enabled?: boolean;\n captureConsole?: boolean;\n /** Defaults to true. With a custom transport, automatic network capture requires endpoint for collector exclusion. */\n captureNetwork?: boolean;\n beforeSend?: (event: ErrorEventRecord) => ErrorEventRecord | false | null | void;\n sanitizeUrl?: (url: string) => string;\n shouldCaptureHttp?: (status: number, url: string, method: string) => boolean;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n /** Per-attempt deadline, clamped to 250\u201360000 ms; defaults to 5000 ms. */\n transportTimeoutMs?: number;\n}\n\nexport interface BrowserErrorLogClient {\n captureException(error: unknown, extra?: CaptureExtra): void;\n flush(): Promise<void>;\n destroy(): void;\n getStats(): { queued: number; sent: number; dropped: number };\n}\n\nconst MAX_BATCH_EVENTS = 20;\nconst MAX_BATCH_BYTES = 48_000;\nconst MAX_BEACON_EVENTS = 10;\nconst MAX_BEACON_BYTES = 24_000;\nconst RETRY_DELAYS_MS = [250, 500];\nconst SENSITIVE_KEY = /(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i;\nconst EVENT_TYPES = new Set<EventType>(['javascript', 'promise', 'react', 'console', 'http', 'network', 'manual']);\nconst RUNTIMES = new Set<Runtime>(['browser', 'webview-ios', 'webview-android', 'unknown']);\nlet fallbackId = 0;\n\nfunction read(object: unknown, key: PropertyKey): unknown {\n try { return object == null ? undefined : Reflect.get(Object(object), key); }\n catch { return undefined; }\n}\n\nfunction safeString(value: unknown): string {\n try { return String(value); }\n catch { return '[unavailable]'; }\n}\n\nfunction trim(value: unknown, length: number): string {\n return safeString(value).slice(0, length);\n}\n\nfunction now(): number {\n try { return performance.now(); }\n catch { return Date.now(); }\n}\n\nfunction newId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n } catch { /* fall through */ }\n return `${Date.now().toString(36)}-${(++fallbackId).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isAbort(error: unknown, signal?: AbortSignal | null): boolean {\n try { if (signal?.aborted) return true; }\n catch { /* ignore hostile signal */ }\n return read(error, 'name') === 'AbortError' || read(error, 'code') === 20;\n}\n\nfunction errorFields(error: unknown): { message: string; name?: string; stack?: string } {\n if (error === null || error === undefined) return { message: 'Unknown error' };\n const message = read(error, 'message');\n const name = read(error, 'name');\n const stack = read(error, 'stack');\n return {\n message: message == null ? safeString(error) : safeString(message),\n ...(name == null ? {} : { name: safeString(name) }),\n ...(stack == null ? {} : { stack: safeString(stack) }),\n };\n}\n\nfunction byteLength(value: string): number {\n return new TextEncoder().encode(value).length;\n}\n\nfunction boundedNumber(value: unknown, minimum: number, maximum: number, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(maximum, Math.max(minimum, Math.floor(value)))\n : fallback;\n}\n\nclass HttpDeliveryError extends Error {\n constructor(readonly status: number) { super(`Collector responded ${status}`); }\n}\n\nclass UrlSanitizationError extends Error {}\n\nclass Client implements BrowserErrorLogClient {\n private readonly active: boolean;\n private readonly endpoint?: string;\n private readonly options: BrowserErrorLogOptions;\n private readonly networkCaptureEnabled: boolean;\n private readonly transportTimeoutMs: number;\n private readonly startedAt = now();\n private readonly viewId = newId();\n private readonly seenErrors = new WeakSet<object>();\n private readonly queue: ErrorEventRecord[] = [];\n private readonly maxQueueSize: number;\n private readonly cancelWaits = new Set<() => void>();\n private readonly controllers = new Set<AbortController>();\n private readonly destroySignal: Promise<symbol>;\n private resolveDestroy!: (value: symbol) => void;\n private interval?: ReturnType<typeof setInterval>;\n private flushing?: Promise<void>;\n private sequence = 0;\n private inFlight = 0;\n private sent = 0;\n private dropped = 0;\n private destroyed = false;\n\n constructor(options: BrowserErrorLogOptions) {\n this.options = options;\n this.active = options.enabled !== false && typeof window !== 'undefined' && typeof document !== 'undefined';\n this.networkCaptureEnabled = options.captureNetwork !== false && (!options.transport || Boolean(options.endpoint));\n this.maxQueueSize = boundedNumber(options.maxQueueSize, 1, 100, 50);\n this.transportTimeoutMs = boundedNumber(options.transportTimeoutMs, 250, 60_000, 5_000);\n this.destroySignal = new Promise(resolve => { this.resolveDestroy = resolve; });\n if (!this.active) return;\n if (!options.project || typeof options.project !== 'string') throw new Error('project is required');\n if (!options.transport && !options.endpoint) throw new Error('endpoint or transport is required');\n this.endpoint = options.endpoint ? this.absoluteUrl(options.endpoint) : undefined;\n window.addEventListener('error', this.onError);\n window.addEventListener('unhandledrejection', this.onRejection);\n document.addEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) addNetworkClient(this);\n if (options.captureConsole === true) addConsoleClient(this);\n this.interval = setInterval(() => { void this.flush(); }, boundedNumber(options.flushIntervalMs, 10, 60_000, 10_000));\n }\n\n captureException(error: unknown, extra: CaptureExtra = {}): void {\n if (!this.active || this.destroyed || isAbort(error)) return;\n try {\n if (error !== null && (typeof error === 'object' || typeof error === 'function')) {\n if (this.seenErrors.has(error)) return;\n }\n const fields = errorFields(error);\n const type = EVENT_TYPES.has(extra.type as EventType) ? extra.type! : 'manual';\n const event = this.makeEvent(type, fields.message, {\n name: fields.name,\n stack: fields.stack,\n componentStack: extra.componentStack,\n context: extra.context,\n });\n if (this.enqueue(event) && error !== null && (typeof error === 'object' || typeof error === 'function')) {\n this.seenErrors.add(error);\n }\n } catch (error) {\n if (error instanceof UrlSanitizationError) this.dropped++;\n // Diagnostics must never affect the host app.\n }\n }\n\n reportNetwork(method: string, url: string, status: number | undefined, durationMs: number, failure?: unknown): void {\n if (this.destroyed || !this.active || this.isCollectorUrl(url) || isAbort(failure)) return;\n try {\n if (status !== undefined) {\n const include = this.options.shouldCaptureHttp\n ? this.options.shouldCaptureHttp(status, url, method)\n : status >= 500;\n if (!include) return;\n }\n const network = { method: trim(method || 'GET', 16).toUpperCase(), url, status, durationMs };\n const fields = failure === undefined ? undefined : errorFields(failure);\n const event = this.makeEvent(status === undefined ? 'network' : 'http',\n status === undefined ? (fields?.message || 'Network request failed') : `HTTP ${status}`,\n { name: fields?.name, network });\n this.enqueue(event);\n } catch { this.dropped++; /* a failing hook must not expose an unfiltered event */ }\n }\n\n captureConsole(values: unknown[]): void {\n if (!this.active || this.destroyed) return;\n try { this.enqueue(this.makeEvent('console', values.map(safeString).join(' '))); }\n catch { this.dropped++; /* console behavior is never changed by capture */ }\n }\n\n flush(): Promise<void> {\n if (!this.active || this.destroyed) return Promise.resolve();\n if (this.flushing) return this.flushing;\n this.flushing = this.drain().finally(() => { this.flushing = undefined; });\n return this.flushing;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.interval) clearInterval(this.interval);\n if (this.active) {\n window.removeEventListener('error', this.onError);\n window.removeEventListener('unhandledrejection', this.onRejection);\n document.removeEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) removeNetworkClient(this);\n if (this.options.captureConsole === true) removeConsoleClient(this);\n }\n this.dropped += this.queue.length + this.inFlight;\n this.queue.length = 0;\n for (const cancel of this.cancelWaits) cancel();\n for (const controller of this.controllers) controller.abort();\n this.resolveDestroy(DESTROYED);\n }\n\n getStats(): { queued: number; sent: number; dropped: number } {\n return { queued: this.queue.length, sent: this.sent, dropped: this.dropped };\n }\n\n private readonly onError = (event: ErrorEvent): void => {\n this.captureException(event.error ?? event.message ?? 'Script error', { type: 'javascript' });\n };\n\n private readonly onRejection = (event: PromiseRejectionEvent): void => {\n this.captureException(event.reason, { type: 'promise' });\n };\n\n private readonly onVisibility = (): void => {\n if (document.visibilityState === 'hidden') this.tryBeacon();\n };\n\n private absoluteUrl(url: string): string {\n try { return new URL(url, window.location.href).href; }\n catch { return url; }\n }\n\n private isCollectorUrl(url: string): boolean {\n if (!this.endpoint) return false;\n try {\n const request = new URL(url, window.location.href);\n const collector = new URL(this.endpoint, window.location.href);\n return request.origin === collector.origin && request.pathname === collector.pathname;\n } catch { return url === this.endpoint; }\n }\n\n private cleanUrl(input: unknown): string {\n let value = safeString(input);\n if (this.options.sanitizeUrl) {\n try { value = this.options.sanitizeUrl(value); }\n catch { throw new UrlSanitizationError('URL sanitizer failed'); }\n if (typeof value !== 'string' || !value) throw new UrlSanitizationError('URL sanitizer returned no URL');\n }\n try {\n const url = new URL(value, window.location.href);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '[unsupported URL]';\n url.username = '';\n url.password = '';\n url.search = '';\n url.hash = '';\n return this.cleanText(url.href, 1024, false);\n } catch { return this.cleanText(value, 1024, false).split(/[?#]/, 1)[0]; }\n }\n\n private cleanText(input: unknown, length: number, replaceUrls = true): string {\n let value = safeString(input);\n if (replaceUrls) {\n value = value.replace(/https?:\\/\\/[^\\s<>\"'()[\\]]+/gi, match => this.cleanUrl(match));\n }\n return value\n .replace(/\\bBearer\\s+[A-Za-z0-9._~+\\/-]+/gi, 'Bearer [REDACTED]')\n .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi, '[REDACTED_EMAIL]')\n .slice(0, length);\n }\n\n private cleanContext(input: unknown): ErrorEventRecord['context'] {\n if (!input || typeof input !== 'object') return undefined;\n const output: NonNullable<ErrorEventRecord['context']> = {};\n let keys: string[];\n try { keys = Object.keys(input); }\n catch { return undefined; }\n for (const key of keys) {\n if (Object.keys(output).length >= 12) break;\n if (SENSITIVE_KEY.test(key)) continue;\n const value = read(input, key);\n const safeKey = this.cleanText(key, 80);\n if (typeof value === 'string') output[safeKey] = this.cleanText(value, 500);\n else if (typeof value === 'number' && Number.isFinite(value)) output[safeKey] = value;\n else if (typeof value === 'boolean' || value === null) output[safeKey] = value;\n }\n return Object.keys(output).length ? output : undefined;\n }\n\n private cleanNetwork(input: unknown): ErrorEventRecord['network'] {\n if (!input || typeof input !== 'object') return undefined;\n const rawStatus = read(input, 'status');\n const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) && rawStatus >= 100 && rawStatus <= 599\n ? rawStatus : undefined;\n return {\n method: this.cleanText(read(input, 'method') ?? 'GET', 16).toUpperCase(),\n url: this.cleanUrl(read(input, 'url') ?? ''),\n ...(status === undefined ? {} : { status }),\n durationMs: boundedNumber(read(input, 'durationMs'), 0, 3_600_000, 0),\n };\n }\n\n private makeEvent(type: EventType, message: string, extra: {\n name?: string; stack?: string; componentStack?: string;\n context?: unknown; network?: unknown;\n } = {}): ErrorEventRecord {\n const userAgent = read(navigator, 'userAgent');\n const context = this.cleanContext(extra.context);\n const network = this.cleanNetwork(extra.network);\n return {\n schemaVersion: 1,\n eventId: newId(),\n project: this.cleanText(this.options.project, 80),\n environment: this.cleanText(this.options.environment ?? 'development', 80),\n release: this.cleanText(this.options.release ?? 'unknown', 120),\n sdkVersion: '0.1.0',\n occurredAt: new Date().toISOString(),\n viewId: this.viewId,\n sequence: ++this.sequence,\n elapsedMs: boundedNumber(now() - this.startedAt, 0, 3_600_000, 0),\n type,\n message: this.cleanText(message, 1024),\n ...(extra.name === undefined ? {} : { name: this.cleanText(extra.name, 100) }),\n ...(extra.stack === undefined ? {} : { stack: this.cleanText(extra.stack, 4096) }),\n ...(extra.componentStack === undefined ? {} : { componentStack: this.cleanText(extra.componentStack, 2048) }),\n page: this.cleanUrl(window.location.href),\n runtime: RUNTIMES.has(this.options.runtime as Runtime) ? this.options.runtime! : 'browser',\n ...(typeof userAgent === 'string' ? { browser: this.cleanText(userAgent, 300) } : {}),\n ...(context ? { context } : {}),\n ...(network ? { network } : {}),\n };\n }\n\n private sanitizeReplacement(candidate: unknown, base: ErrorEventRecord): ErrorEventRecord {\n const type = read(candidate, 'type');\n const runtime = read(candidate, 'runtime');\n const name = read(candidate, 'name');\n const stack = read(candidate, 'stack');\n const componentStack = read(candidate, 'componentStack');\n const browser = read(candidate, 'browser');\n const context = this.cleanContext(read(candidate, 'context'));\n const network = this.cleanNetwork(read(candidate, 'network'));\n return {\n ...base,\n type: EVENT_TYPES.has(type as EventType) ? type as EventType : base.type,\n message: this.cleanText(read(candidate, 'message') ?? base.message, 1024),\n ...(name === undefined ? { name: base.name } : { name: this.cleanText(name, 100) }),\n ...(stack === undefined ? { stack: base.stack } : { stack: this.cleanText(stack, 4096) }),\n ...(componentStack === undefined ? { componentStack: base.componentStack } : { componentStack: this.cleanText(componentStack, 2048) }),\n page: this.cleanUrl(read(candidate, 'page') ?? base.page),\n runtime: RUNTIMES.has(runtime as Runtime) ? runtime as Runtime : base.runtime,\n ...(browser === undefined ? { browser: base.browser } : { browser: this.cleanText(browser, 300) }),\n ...(context ? { context } : { context: undefined }),\n ...(network ? { network } : { network: undefined }),\n };\n }\n\n private enqueue(event: ErrorEventRecord): boolean {\n if (this.destroyed) return false;\n let finalEvent = event;\n if (this.options.beforeSend) {\n try {\n const copy = { ...event, context: event.context && { ...event.context }, network: event.network && { ...event.network } };\n const result = this.options.beforeSend(copy);\n if (result === false || result === null) return false;\n finalEvent = this.sanitizeReplacement(result === undefined ? copy : result, event);\n } catch { this.dropped++; return false; }\n }\n // A single event can never make a batch exceed its byte budget.\n if (byteLength(JSON.stringify(finalEvent)) > MAX_BATCH_BYTES) { this.dropped++; return false; }\n if (this.queue.length >= this.maxQueueSize) { this.queue.shift(); this.dropped++; }\n this.queue.push(finalEvent);\n if (this.queue.length >= this.maxQueueSize) void this.flush();\n return true;\n }\n\n private takeBatch(maxEvents = MAX_BATCH_EVENTS, maxBytes = MAX_BATCH_BYTES): EventBatch {\n const events: ErrorEventRecord[] = [];\n let bytes = byteLength('{\"schemaVersion\":1,\"events\":[]}');\n while (this.queue.length && events.length < maxEvents) {\n const next = this.queue[0];\n const size = byteLength(JSON.stringify(next)) + (events.length ? 1 : 0);\n if (events.length && bytes + size > maxBytes) break;\n if (!events.length && bytes + size > maxBytes) { this.queue.shift(); this.dropped++; continue; }\n events.push(this.queue.shift()!);\n bytes += size;\n }\n return { schemaVersion: 1, events };\n }\n\n private async drain(): Promise<void> {\n while (!this.destroyed && this.queue.length) {\n const batch = this.takeBatch();\n if (batch.events.length) {\n this.inFlight += batch.events.length;\n try { await this.sendWithRetry(batch); }\n finally { this.inFlight -= batch.events.length; }\n }\n }\n }\n\n private async sendWithRetry(batch: EventBatch): Promise<void> {\n for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length && !this.destroyed; attempt++) {\n const controller = new AbortController();\n this.controllers.add(controller);\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timeoutHandle = setTimeout(() => {\n controller.abort();\n reject(new Error('Collector transport timed out'));\n }, this.transportTimeoutMs);\n });\n let retryDelay: number | undefined;\n try {\n const result = await Promise.race([this.send(batch, controller.signal), timeout, this.destroySignal]);\n if (result === DESTROYED || this.destroyed) return;\n this.sent += batch.events.length;\n return;\n } catch (error) {\n if (this.destroyed) return;\n const status = read(error, 'status');\n const retryable = typeof status !== 'number' || status === 429 || status >= 500;\n if (!retryable || attempt === RETRY_DELAYS_MS.length) break;\n retryDelay = RETRY_DELAYS_MS[attempt];\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n this.controllers.delete(controller);\n }\n if (retryDelay !== undefined) await this.wait(retryDelay);\n }\n if (!this.destroyed) this.dropped += batch.events.length;\n }\n\n private async send(batch: EventBatch, signal: AbortSignal): Promise<void> {\n if (this.options.transport) { await this.options.transport(batch, { signal }); return; }\n const response = await window.fetch(this.endpoint!, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(batch),\n credentials: 'omit',\n signal,\n });\n if (!response.ok) throw new HttpDeliveryError(response.status);\n }\n\n private wait(milliseconds: number): Promise<void> {\n return new Promise(resolve => {\n const finish = () => { clearTimeout(timer); this.cancelWaits.delete(finish); resolve(); };\n const timer = setTimeout(finish, milliseconds);\n this.cancelWaits.add(finish);\n });\n }\n\n private tryBeacon(): void {\n if (this.destroyed || this.options.transport || !this.endpoint || !this.queue.length) return;\n try {\n const url = new URL(this.endpoint);\n if (url.origin !== window.location.origin || url.search || url.hash || url.username || url.password) return;\n if (typeof navigator.sendBeacon !== 'function') return;\n const candidates = this.queue.slice(0, MAX_BEACON_EVENTS);\n while (candidates.length > 0) {\n const batch: EventBatch = { schemaVersion: 1, events: candidates };\n const body = JSON.stringify(batch);\n if (byteLength(body) <= MAX_BEACON_BYTES) {\n if (navigator.sendBeacon(this.endpoint, new Blob([body], { type: 'application/json' }))) {\n this.queue.splice(0, candidates.length);\n }\n return;\n }\n candidates.pop();\n }\n } catch { /* best effort only */ }\n }\n}\n\nconst DESTROYED = Symbol('destroyed');\n\ninterface Instrumentation {\n clients: Set<Client>;\n cleanup(): void;\n}\n\nlet networkInstrumentation: Instrumentation | undefined;\nlet consoleInstrumentation: Instrumentation | undefined;\n\nfunction addNetworkClient(client: Client): void {\n if (!networkInstrumentation) networkInstrumentation = installNetwork();\n networkInstrumentation.clients.add(client);\n}\n\nfunction removeNetworkClient(client: Client): void {\n const instrumentation = networkInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n networkInstrumentation = undefined;\n }\n}\n\nfunction describeFetch(input: RequestInfo | URL, init?: RequestInit): { url: string; method: string; signal?: AbortSignal | null } {\n const request = typeof Request !== 'undefined' && input instanceof Request ? input : undefined;\n const url = request ? request.url : safeString(input);\n return { url, method: safeString(init?.method ?? request?.method ?? 'GET'), signal: init?.signal ?? request?.signal };\n}\n\nfunction installNetwork(): Instrumentation {\n const clients = new Set<Client>();\n const originalFetch = window.fetch;\n let wrappedFetch: typeof fetch | undefined;\n if (typeof originalFetch === 'function') {\n wrappedFetch = function (this: typeof window, ...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n if (!clients.size) return Reflect.apply(originalFetch, this, args);\n const started = now();\n let request: ReturnType<typeof describeFetch>;\n try { request = describeFetch(args[0], args[1]); }\n catch { request = { url: '', method: 'GET' }; }\n let promise: ReturnType<typeof fetch>;\n try { promise = Reflect.apply(originalFetch, this, args); }\n catch (error) {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n }\n return promise.then(\n response => {\n try {\n const status = response.status;\n for (const client of clients) client.reportNetwork(request.method, request.url, status, now() - started);\n } catch { /* telemetry cannot change the fetch result */ }\n return response;\n },\n error => {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n },\n );\n };\n window.fetch = wrappedFetch;\n }\n\n const prototype = typeof XMLHttpRequest === 'undefined' ? undefined : XMLHttpRequest.prototype;\n const originalOpen = prototype?.open;\n const originalSend = prototype?.send;\n const metadata = new WeakMap<XMLHttpRequest, { method: string; url: string }>();\n let wrappedOpen: typeof XMLHttpRequest.prototype.open | undefined;\n let wrappedSend: typeof XMLHttpRequest.prototype.send | undefined;\n if (prototype && originalOpen && originalSend) {\n wrappedOpen = (function (this: XMLHttpRequest, ...args: unknown[]): void {\n const result = Reflect.apply(originalOpen, this, args);\n try { metadata.set(this, { method: safeString(args[0]), url: safeString(args[1]) }); }\n catch { /* native open already succeeded */ }\n return result;\n }) as typeof XMLHttpRequest.prototype.open;\n wrappedSend = function (this: XMLHttpRequest, ...args: Parameters<XMLHttpRequest['send']>): void {\n if (!clients.size) return Reflect.apply(originalSend, this, args);\n const details = metadata.get(this);\n if (!details) return Reflect.apply(originalSend, this, args);\n const started = now();\n let aborted = false;\n let failed = false;\n const onAbort = () => { aborted = true; };\n const onFailure = () => { failed = true; };\n const onLoadEnd = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n if (aborted) return;\n try {\n const status = this.status;\n if (status === 0 && !failed) return;\n for (const client of clients) client.reportNetwork(details.method, details.url,\n status === 0 ? undefined : status, now() - started);\n } catch { /* XHR state may be unavailable */ }\n };\n try {\n this.addEventListener('abort', onAbort);\n this.addEventListener('error', onFailure);\n this.addEventListener('timeout', onFailure);\n this.addEventListener('loadend', onLoadEnd);\n } catch { return Reflect.apply(originalSend, this, args); }\n try { return Reflect.apply(originalSend, this, args); }\n catch (error) {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n throw error;\n }\n };\n prototype.open = wrappedOpen!;\n prototype.send = wrappedSend;\n }\n return {\n clients,\n cleanup() {\n if (wrappedFetch && window.fetch === wrappedFetch) window.fetch = originalFetch;\n if (prototype && wrappedOpen && prototype.open === wrappedOpen) prototype.open = originalOpen!;\n if (prototype && wrappedSend && prototype.send === wrappedSend) prototype.send = originalSend!;\n },\n };\n}\n\nfunction addConsoleClient(client: Client): void {\n if (!consoleInstrumentation) consoleInstrumentation = installConsole();\n consoleInstrumentation.clients.add(client);\n}\n\nfunction removeConsoleClient(client: Client): void {\n const instrumentation = consoleInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n consoleInstrumentation = undefined;\n }\n}\n\nfunction installConsole(): Instrumentation {\n const clients = new Set<Client>();\n const original = console.error;\n const wrapped: typeof console.error = function (this: Console, ...values: unknown[]): void {\n try { return Reflect.apply(original, this, values); }\n finally { for (const client of clients) client.captureConsole(values); }\n };\n console.error = wrapped;\n return { clients, cleanup() { if (console.error === wrapped) console.error = original; } };\n}\n\nexport function init(options: BrowserErrorLogOptions): BrowserErrorLogClient {\n return new Client(options);\n}\n"],
|
|
4
|
+
"sourcesContent": ["import type { ErrorEventRecord, EventBatch, EventType, Runtime } from 'browser-error-log-protocol';\n\nexport type { ErrorEventRecord, EventBatch } from 'browser-error-log-protocol';\n\nexport interface CaptureExtra {\n type?: EventType;\n context?: Record<string, unknown>;\n componentStack?: string;\n}\n\nexport interface TransportContext { signal: AbortSignal }\n\nexport interface BrowserErrorLogOptions {\n project: string;\n environment?: string;\n release?: string;\n endpoint?: string;\n transport?: (batch: EventBatch, context: TransportContext) => void | Promise<void>;\n runtime?: Runtime;\n enabled?: boolean;\n captureConsole?: boolean;\n /** Defaults to true. With a custom transport, automatic network capture requires endpoint for collector exclusion. */\n captureNetwork?: boolean;\n beforeSend?: (event: ErrorEventRecord) => ErrorEventRecord | false | null | void;\n sanitizeUrl?: (url: string) => string;\n shouldCaptureHttp?: (status: number, url: string, method: string) => boolean;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n /** Per-attempt deadline, clamped to 250\u201360000 ms; defaults to 5000 ms. */\n transportTimeoutMs?: number;\n}\n\nexport interface BrowserErrorLogClient {\n captureException(error: unknown, extra?: CaptureExtra): void;\n flush(): Promise<void>;\n destroy(): void;\n getStats(): { queued: number; sent: number; dropped: number };\n}\n\nconst MAX_BATCH_EVENTS = 20;\nconst MAX_BATCH_BYTES = 48_000;\nconst MAX_BEACON_EVENTS = 10;\nconst MAX_BEACON_BYTES = 24_000;\nconst RETRY_DELAYS_MS = [250, 500];\nconst SENSITIVE_KEY = /(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i;\nconst EVENT_TYPES = new Set<EventType>(['javascript', 'promise', 'react', 'console', 'http', 'network', 'manual']);\nconst RUNTIMES = new Set<Runtime>(['browser', 'webview-ios', 'webview-android', 'unknown']);\nlet fallbackId = 0;\n\nfunction read(object: unknown, key: PropertyKey): unknown {\n try { return object == null ? undefined : Reflect.get(Object(object), key); }\n catch { return undefined; }\n}\n\nfunction safeString(value: unknown): string {\n try { return String(value); }\n catch { return '[unavailable]'; }\n}\n\nfunction trim(value: unknown, length: number): string {\n return safeString(value).slice(0, length);\n}\n\nfunction now(): number {\n try { return performance.now(); }\n catch { return Date.now(); }\n}\n\nfunction newId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n } catch { /* fall through */ }\n return `${Date.now().toString(36)}-${(++fallbackId).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isAbort(error: unknown, signal?: AbortSignal | null): boolean {\n try { if (signal?.aborted) return true; }\n catch { /* ignore hostile signal */ }\n return read(error, 'name') === 'AbortError' || read(error, 'code') === 20;\n}\n\nfunction errorFields(error: unknown): { message: string; name?: string; stack?: string } {\n if (error === null || error === undefined) return { message: 'Unknown error' };\n const message = read(error, 'message');\n const name = read(error, 'name');\n const stack = read(error, 'stack');\n return {\n message: message == null ? safeString(error) : safeString(message),\n ...(name == null ? {} : { name: safeString(name) }),\n ...(stack == null ? {} : { stack: safeString(stack) }),\n };\n}\n\nfunction byteLength(value: string): number {\n return new TextEncoder().encode(value).length;\n}\n\nfunction boundedNumber(value: unknown, minimum: number, maximum: number, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(maximum, Math.max(minimum, Math.floor(value)))\n : fallback;\n}\n\nclass HttpDeliveryError extends Error {\n constructor(readonly status: number) { super(`Collector responded ${status}`); }\n}\n\nclass UrlSanitizationError extends Error {}\n\nclass Client implements BrowserErrorLogClient {\n private readonly active: boolean;\n private readonly endpoint?: string;\n private readonly options: BrowserErrorLogOptions;\n private readonly networkCaptureEnabled: boolean;\n private readonly transportTimeoutMs: number;\n private readonly startedAt = now();\n private readonly viewId = newId();\n private readonly seenErrors = new WeakSet<object>();\n private readonly queue: ErrorEventRecord[] = [];\n private readonly maxQueueSize: number;\n private readonly cancelWaits = new Set<() => void>();\n private readonly controllers = new Set<AbortController>();\n private readonly destroySignal: Promise<symbol>;\n private resolveDestroy!: (value: symbol) => void;\n private interval?: ReturnType<typeof setInterval>;\n private flushing?: Promise<void>;\n private sequence = 0;\n private inFlight = 0;\n private sent = 0;\n private dropped = 0;\n private destroyed = false;\n\n constructor(options: BrowserErrorLogOptions) {\n this.options = options;\n this.active = options.enabled !== false && typeof window !== 'undefined' && typeof document !== 'undefined';\n this.networkCaptureEnabled = options.captureNetwork !== false && (!options.transport || Boolean(options.endpoint));\n this.maxQueueSize = boundedNumber(options.maxQueueSize, 1, 100, 50);\n this.transportTimeoutMs = boundedNumber(options.transportTimeoutMs, 250, 60_000, 5_000);\n this.destroySignal = new Promise(resolve => { this.resolveDestroy = resolve; });\n if (!this.active) return;\n if (!options.project || typeof options.project !== 'string') throw new Error('project is required');\n if (!options.transport && !options.endpoint) throw new Error('endpoint or transport is required');\n this.endpoint = options.endpoint ? this.absoluteUrl(options.endpoint) : undefined;\n window.addEventListener('error', this.onError);\n window.addEventListener('unhandledrejection', this.onRejection);\n document.addEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) addNetworkClient(this);\n if (options.captureConsole === true) addConsoleClient(this);\n this.interval = setInterval(() => { void this.flush(); }, boundedNumber(options.flushIntervalMs, 10, 60_000, 10_000));\n }\n\n captureException(error: unknown, extra: CaptureExtra = {}): void {\n if (!this.active || this.destroyed || isAbort(error)) return;\n try {\n if (error !== null && (typeof error === 'object' || typeof error === 'function')) {\n if (this.seenErrors.has(error)) return;\n }\n const fields = errorFields(error);\n const type = EVENT_TYPES.has(extra.type as EventType) ? extra.type! : 'manual';\n const event = this.makeEvent(type, fields.message, {\n name: fields.name,\n stack: fields.stack,\n componentStack: extra.componentStack,\n context: extra.context,\n });\n if (this.enqueue(event) && error !== null && (typeof error === 'object' || typeof error === 'function')) {\n this.seenErrors.add(error);\n }\n } catch (error) {\n if (error instanceof UrlSanitizationError) this.dropped++;\n // Diagnostics must never affect the host app.\n }\n }\n\n reportNetwork(method: string, url: string, status: number | undefined, durationMs: number, failure?: unknown): void {\n if (this.destroyed || !this.active || this.isCollectorUrl(url) || isAbort(failure)) return;\n try {\n if (status !== undefined) {\n const include = this.options.shouldCaptureHttp\n ? this.options.shouldCaptureHttp(status, url, method)\n : status >= 500;\n if (!include) return;\n }\n const network = { method: trim(method || 'GET', 16).toUpperCase(), url, status, durationMs };\n const fields = failure === undefined ? undefined : errorFields(failure);\n const event = this.makeEvent(status === undefined ? 'network' : 'http',\n status === undefined ? (fields?.message || 'Network request failed') : `HTTP ${status}`,\n { name: fields?.name, network });\n this.enqueue(event);\n } catch { this.dropped++; /* a failing hook must not expose an unfiltered event */ }\n }\n\n captureConsole(values: unknown[]): void {\n if (!this.active || this.destroyed) return;\n try { this.enqueue(this.makeEvent('console', values.map(safeString).join(' '))); }\n catch { this.dropped++; /* console behavior is never changed by capture */ }\n }\n\n flush(): Promise<void> {\n if (!this.active || this.destroyed) return Promise.resolve();\n if (this.flushing) return this.flushing;\n this.flushing = this.drain().finally(() => { this.flushing = undefined; });\n return this.flushing;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.interval) clearInterval(this.interval);\n if (this.active) {\n window.removeEventListener('error', this.onError);\n window.removeEventListener('unhandledrejection', this.onRejection);\n document.removeEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) removeNetworkClient(this);\n if (this.options.captureConsole === true) removeConsoleClient(this);\n }\n this.dropped += this.queue.length + this.inFlight;\n this.queue.length = 0;\n for (const cancel of this.cancelWaits) cancel();\n for (const controller of this.controllers) controller.abort();\n this.resolveDestroy(DESTROYED);\n }\n\n getStats(): { queued: number; sent: number; dropped: number } {\n return { queued: this.queue.length, sent: this.sent, dropped: this.dropped };\n }\n\n private readonly onError = (event: ErrorEvent): void => {\n this.captureException(event.error ?? event.message ?? 'Script error', { type: 'javascript' });\n };\n\n private readonly onRejection = (event: PromiseRejectionEvent): void => {\n this.captureException(event.reason, { type: 'promise' });\n };\n\n private readonly onVisibility = (): void => {\n if (document.visibilityState === 'hidden') this.tryBeacon();\n };\n\n private absoluteUrl(url: string): string {\n try { return new URL(url, window.location.href).href; }\n catch { return url; }\n }\n\n private isCollectorUrl(url: string): boolean {\n if (!this.endpoint) return false;\n try {\n const request = new URL(url, window.location.href);\n const collector = new URL(this.endpoint, window.location.href);\n return request.origin === collector.origin && request.pathname === collector.pathname;\n } catch { return url === this.endpoint; }\n }\n\n private cleanUrl(input: unknown): string {\n let value = safeString(input);\n if (this.options.sanitizeUrl) {\n try { value = this.options.sanitizeUrl(value); }\n catch { throw new UrlSanitizationError('URL sanitizer failed'); }\n if (typeof value !== 'string' || !value) throw new UrlSanitizationError('URL sanitizer returned no URL');\n }\n try {\n const url = new URL(value, window.location.href);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '[unsupported URL]';\n url.username = '';\n url.password = '';\n url.search = '';\n url.hash = '';\n return this.cleanText(url.href, 1024, false);\n } catch { return this.cleanText(value, 1024, false).split(/[?#]/, 1)[0]; }\n }\n\n private cleanText(input: unknown, length: number, replaceUrls = true): string {\n let value = safeString(input);\n if (replaceUrls) {\n value = value.replace(/https?:\\/\\/[^\\s<>\"'()[\\]]+/gi, match => this.cleanUrl(match));\n }\n return value\n .replace(/\\bBearer\\s+[A-Za-z0-9._~+\\/-]+/gi, 'Bearer [REDACTED]')\n .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi, '[REDACTED_EMAIL]')\n .slice(0, length);\n }\n\n private cleanContext(input: unknown): ErrorEventRecord['context'] {\n if (!input || typeof input !== 'object') return undefined;\n const output: NonNullable<ErrorEventRecord['context']> = {};\n let keys: string[];\n try { keys = Object.keys(input); }\n catch { return undefined; }\n for (const key of keys) {\n if (Object.keys(output).length >= 12) break;\n if (SENSITIVE_KEY.test(key)) continue;\n const value = read(input, key);\n const safeKey = this.cleanText(key, 80);\n if (typeof value === 'string') output[safeKey] = this.cleanText(value, 500);\n else if (typeof value === 'number' && Number.isFinite(value)) output[safeKey] = value;\n else if (typeof value === 'boolean' || value === null) output[safeKey] = value;\n }\n return Object.keys(output).length ? output : undefined;\n }\n\n private cleanNetwork(input: unknown): ErrorEventRecord['network'] {\n if (!input || typeof input !== 'object') return undefined;\n const rawStatus = read(input, 'status');\n const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) && rawStatus >= 100 && rawStatus <= 599\n ? rawStatus : undefined;\n return {\n method: this.cleanText(read(input, 'method') ?? 'GET', 16).toUpperCase(),\n url: this.cleanUrl(read(input, 'url') ?? ''),\n ...(status === undefined ? {} : { status }),\n durationMs: boundedNumber(read(input, 'durationMs'), 0, 3_600_000, 0),\n };\n }\n\n private makeEvent(type: EventType, message: string, extra: {\n name?: string; stack?: string; componentStack?: string;\n context?: unknown; network?: unknown;\n } = {}): ErrorEventRecord {\n const userAgent = read(navigator, 'userAgent');\n const context = this.cleanContext(extra.context);\n const network = this.cleanNetwork(extra.network);\n return {\n schemaVersion: 1,\n eventId: newId(),\n project: this.cleanText(this.options.project, 80),\n environment: this.cleanText(this.options.environment ?? 'development', 80),\n release: this.cleanText(this.options.release ?? 'unknown', 120),\n sdkVersion: '0.1.1',\n occurredAt: new Date().toISOString(),\n viewId: this.viewId,\n sequence: ++this.sequence,\n elapsedMs: boundedNumber(now() - this.startedAt, 0, 3_600_000, 0),\n type,\n message: this.cleanText(message, 1024),\n ...(extra.name === undefined ? {} : { name: this.cleanText(extra.name, 100) }),\n ...(extra.stack === undefined ? {} : { stack: this.cleanText(extra.stack, 4096) }),\n ...(extra.componentStack === undefined ? {} : { componentStack: this.cleanText(extra.componentStack, 2048) }),\n page: this.cleanUrl(window.location.href),\n runtime: RUNTIMES.has(this.options.runtime as Runtime) ? this.options.runtime! : 'browser',\n ...(typeof userAgent === 'string' ? { browser: this.cleanText(userAgent, 300) } : {}),\n ...(context ? { context } : {}),\n ...(network ? { network } : {}),\n };\n }\n\n private sanitizeReplacement(candidate: unknown, base: ErrorEventRecord): ErrorEventRecord {\n const type = read(candidate, 'type');\n const runtime = read(candidate, 'runtime');\n const name = read(candidate, 'name');\n const stack = read(candidate, 'stack');\n const componentStack = read(candidate, 'componentStack');\n const browser = read(candidate, 'browser');\n const context = this.cleanContext(read(candidate, 'context'));\n const network = this.cleanNetwork(read(candidate, 'network'));\n return {\n ...base,\n type: EVENT_TYPES.has(type as EventType) ? type as EventType : base.type,\n message: this.cleanText(read(candidate, 'message') ?? base.message, 1024),\n ...(name === undefined ? { name: base.name } : { name: this.cleanText(name, 100) }),\n ...(stack === undefined ? { stack: base.stack } : { stack: this.cleanText(stack, 4096) }),\n ...(componentStack === undefined ? { componentStack: base.componentStack } : { componentStack: this.cleanText(componentStack, 2048) }),\n page: this.cleanUrl(read(candidate, 'page') ?? base.page),\n runtime: RUNTIMES.has(runtime as Runtime) ? runtime as Runtime : base.runtime,\n ...(browser === undefined ? { browser: base.browser } : { browser: this.cleanText(browser, 300) }),\n ...(context ? { context } : { context: undefined }),\n ...(network ? { network } : { network: undefined }),\n };\n }\n\n private enqueue(event: ErrorEventRecord): boolean {\n if (this.destroyed) return false;\n let finalEvent = event;\n if (this.options.beforeSend) {\n try {\n const copy = { ...event, context: event.context && { ...event.context }, network: event.network && { ...event.network } };\n const result = this.options.beforeSend(copy);\n if (result === false || result === null) return false;\n finalEvent = this.sanitizeReplacement(result === undefined ? copy : result, event);\n } catch { this.dropped++; return false; }\n }\n // A single event can never make a batch exceed its byte budget.\n if (byteLength(JSON.stringify(finalEvent)) > MAX_BATCH_BYTES) { this.dropped++; return false; }\n if (this.queue.length >= this.maxQueueSize) { this.queue.shift(); this.dropped++; }\n this.queue.push(finalEvent);\n if (this.queue.length >= this.maxQueueSize) void this.flush();\n return true;\n }\n\n private takeBatch(maxEvents = MAX_BATCH_EVENTS, maxBytes = MAX_BATCH_BYTES): EventBatch {\n const events: ErrorEventRecord[] = [];\n let bytes = byteLength('{\"schemaVersion\":1,\"events\":[]}');\n while (this.queue.length && events.length < maxEvents) {\n const next = this.queue[0];\n const size = byteLength(JSON.stringify(next)) + (events.length ? 1 : 0);\n if (events.length && bytes + size > maxBytes) break;\n if (!events.length && bytes + size > maxBytes) { this.queue.shift(); this.dropped++; continue; }\n events.push(this.queue.shift()!);\n bytes += size;\n }\n return { schemaVersion: 1, events };\n }\n\n private async drain(): Promise<void> {\n while (!this.destroyed && this.queue.length) {\n const batch = this.takeBatch();\n if (batch.events.length) {\n this.inFlight += batch.events.length;\n try { await this.sendWithRetry(batch); }\n finally { this.inFlight -= batch.events.length; }\n }\n }\n }\n\n private async sendWithRetry(batch: EventBatch): Promise<void> {\n for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length && !this.destroyed; attempt++) {\n const controller = new AbortController();\n this.controllers.add(controller);\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timeoutHandle = setTimeout(() => {\n controller.abort();\n reject(new Error('Collector transport timed out'));\n }, this.transportTimeoutMs);\n });\n let retryDelay: number | undefined;\n try {\n const result = await Promise.race([this.send(batch, controller.signal), timeout, this.destroySignal]);\n if (result === DESTROYED || this.destroyed) return;\n this.sent += batch.events.length;\n return;\n } catch (error) {\n if (this.destroyed) return;\n const status = read(error, 'status');\n const retryable = typeof status !== 'number' || status === 429 || status >= 500;\n if (!retryable || attempt === RETRY_DELAYS_MS.length) break;\n retryDelay = RETRY_DELAYS_MS[attempt];\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n this.controllers.delete(controller);\n }\n if (retryDelay !== undefined) await this.wait(retryDelay);\n }\n if (!this.destroyed) this.dropped += batch.events.length;\n }\n\n private async send(batch: EventBatch, signal: AbortSignal): Promise<void> {\n if (this.options.transport) { await this.options.transport(batch, { signal }); return; }\n const response = await window.fetch(this.endpoint!, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(batch),\n credentials: 'omit',\n signal,\n });\n if (!response.ok) throw new HttpDeliveryError(response.status);\n }\n\n private wait(milliseconds: number): Promise<void> {\n return new Promise(resolve => {\n const finish = () => { clearTimeout(timer); this.cancelWaits.delete(finish); resolve(); };\n const timer = setTimeout(finish, milliseconds);\n this.cancelWaits.add(finish);\n });\n }\n\n private tryBeacon(): void {\n if (this.destroyed || this.options.transport || !this.endpoint || !this.queue.length) return;\n try {\n const url = new URL(this.endpoint);\n if (url.origin !== window.location.origin || url.search || url.hash || url.username || url.password) return;\n if (typeof navigator.sendBeacon !== 'function') return;\n const candidates = this.queue.slice(0, MAX_BEACON_EVENTS);\n while (candidates.length > 0) {\n const batch: EventBatch = { schemaVersion: 1, events: candidates };\n const body = JSON.stringify(batch);\n if (byteLength(body) <= MAX_BEACON_BYTES) {\n if (navigator.sendBeacon(this.endpoint, new Blob([body], { type: 'application/json' }))) {\n this.queue.splice(0, candidates.length);\n }\n return;\n }\n candidates.pop();\n }\n } catch { /* best effort only */ }\n }\n}\n\nconst DESTROYED = Symbol('destroyed');\n\ninterface Instrumentation {\n clients: Set<Client>;\n cleanup(): void;\n}\n\nlet networkInstrumentation: Instrumentation | undefined;\nlet consoleInstrumentation: Instrumentation | undefined;\n\nfunction addNetworkClient(client: Client): void {\n if (!networkInstrumentation) networkInstrumentation = installNetwork();\n networkInstrumentation.clients.add(client);\n}\n\nfunction removeNetworkClient(client: Client): void {\n const instrumentation = networkInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n networkInstrumentation = undefined;\n }\n}\n\nfunction describeFetch(input: RequestInfo | URL, init?: RequestInit): { url: string; method: string; signal?: AbortSignal | null } {\n const request = typeof Request !== 'undefined' && input instanceof Request ? input : undefined;\n const url = request ? request.url : safeString(input);\n return { url, method: safeString(init?.method ?? request?.method ?? 'GET'), signal: init?.signal ?? request?.signal };\n}\n\nfunction installNetwork(): Instrumentation {\n const clients = new Set<Client>();\n const originalFetch = window.fetch;\n let wrappedFetch: typeof fetch | undefined;\n if (typeof originalFetch === 'function') {\n wrappedFetch = function (this: typeof window, ...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n if (!clients.size) return Reflect.apply(originalFetch, this, args);\n const started = now();\n let request: ReturnType<typeof describeFetch>;\n try { request = describeFetch(args[0], args[1]); }\n catch { request = { url: '', method: 'GET' }; }\n let promise: ReturnType<typeof fetch>;\n try { promise = Reflect.apply(originalFetch, this, args); }\n catch (error) {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n }\n return promise.then(\n response => {\n try {\n const status = response.status;\n for (const client of clients) client.reportNetwork(request.method, request.url, status, now() - started);\n } catch { /* telemetry cannot change the fetch result */ }\n return response;\n },\n error => {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n },\n );\n };\n window.fetch = wrappedFetch;\n }\n\n const prototype = typeof XMLHttpRequest === 'undefined' ? undefined : XMLHttpRequest.prototype;\n const originalOpen = prototype?.open;\n const originalSend = prototype?.send;\n const metadata = new WeakMap<XMLHttpRequest, { method: string; url: string }>();\n let wrappedOpen: typeof XMLHttpRequest.prototype.open | undefined;\n let wrappedSend: typeof XMLHttpRequest.prototype.send | undefined;\n if (prototype && originalOpen && originalSend) {\n wrappedOpen = (function (this: XMLHttpRequest, ...args: unknown[]): void {\n const result = Reflect.apply(originalOpen, this, args);\n try { metadata.set(this, { method: safeString(args[0]), url: safeString(args[1]) }); }\n catch { /* native open already succeeded */ }\n return result;\n }) as typeof XMLHttpRequest.prototype.open;\n wrappedSend = function (this: XMLHttpRequest, ...args: Parameters<XMLHttpRequest['send']>): void {\n if (!clients.size) return Reflect.apply(originalSend, this, args);\n const details = metadata.get(this);\n if (!details) return Reflect.apply(originalSend, this, args);\n const started = now();\n let aborted = false;\n let failed = false;\n const onAbort = () => { aborted = true; };\n const onFailure = () => { failed = true; };\n const onLoadEnd = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n if (aborted) return;\n try {\n const status = this.status;\n if (status === 0 && !failed) return;\n for (const client of clients) client.reportNetwork(details.method, details.url,\n status === 0 ? undefined : status, now() - started);\n } catch { /* XHR state may be unavailable */ }\n };\n try {\n this.addEventListener('abort', onAbort);\n this.addEventListener('error', onFailure);\n this.addEventListener('timeout', onFailure);\n this.addEventListener('loadend', onLoadEnd);\n } catch { return Reflect.apply(originalSend, this, args); }\n try { return Reflect.apply(originalSend, this, args); }\n catch (error) {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n throw error;\n }\n };\n prototype.open = wrappedOpen!;\n prototype.send = wrappedSend;\n }\n return {\n clients,\n cleanup() {\n if (wrappedFetch && window.fetch === wrappedFetch) window.fetch = originalFetch;\n if (prototype && wrappedOpen && prototype.open === wrappedOpen) prototype.open = originalOpen!;\n if (prototype && wrappedSend && prototype.send === wrappedSend) prototype.send = originalSend!;\n },\n };\n}\n\nfunction addConsoleClient(client: Client): void {\n if (!consoleInstrumentation) consoleInstrumentation = installConsole();\n consoleInstrumentation.clients.add(client);\n}\n\nfunction removeConsoleClient(client: Client): void {\n const instrumentation = consoleInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n consoleInstrumentation = undefined;\n }\n}\n\nfunction installConsole(): Instrumentation {\n const clients = new Set<Client>();\n const original = console.error;\n const wrapped: typeof console.error = function (this: Console, ...values: unknown[]): void {\n try { return Reflect.apply(original, this, values); }\n finally { for (const client of clients) client.captureConsole(values); }\n };\n console.error = wrapped;\n return { clients, cleanup() { if (console.error === wrapped) console.error = original; } };\n}\n\nexport function init(options: BrowserErrorLogOptions): BrowserErrorLogClient {\n return new Client(options);\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["index_exports", "__export", "init", "__toCommonJS", "RETRY_DELAYS_MS", "SENSITIVE_KEY", "EVENT_TYPES", "RUNTIMES", "fallbackId", "read", "object", "key", "safeString", "value", "trim", "length", "now", "newId", "isAbort", "error", "signal", "errorFields", "message", "name", "stack", "byteLength", "boundedNumber", "minimum", "maximum", "fallback", "HttpDeliveryError", "status", "__publicField", "UrlSanitizationError", "Client", "options", "event", "resolve", "addNetworkClient", "addConsoleClient", "extra", "fields", "type", "method", "url", "durationMs", "failure", "network", "values", "removeNetworkClient", "removeConsoleClient", "cancel", "controller", "DESTROYED", "request", "collector", "input", "replaceUrls", "match", "output", "keys", "safeKey", "rawStatus", "userAgent", "context", "candidate", "base", "runtime", "componentStack", "browser", "finalEvent", "copy", "result", "maxEvents", "maxBytes", "events", "bytes", "next", "size", "batch", "attempt", "timeoutHandle", "timeout", "_resolve", "reject", "retryDelay", "response", "milliseconds", "finish", "timer", "candidates", "body", "networkInstrumentation", "consoleInstrumentation", "client", "installNetwork", "instrumentation", "describeFetch", "init", "clients", "originalFetch", "wrappedFetch", "args", "started", "promise", "prototype", "originalOpen", "originalSend", "metadata", "wrappedOpen", "wrappedSend", "details", "aborted", "failed", "onAbort", "onFailure", "onLoadEnd", "installConsole", "original", "wrapped"]
|
|
7
7
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var j=Object.defineProperty;var O=(r,e,t)=>e in r?j(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var s=(r,e,t)=>O(r,typeof e!="symbol"?e+"":e,t);var L=[250,500],P=/(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i,B=new Set(["javascript","promise","react","console","http","network","manual"]),M=new Set(["browser","webview-ios","webview-android","unknown"]),z=0;function c(r,e){try{return r==null?void 0:Reflect.get(Object(r),e)}catch{return}}function m(r){try{return String(r)}catch{return"[unavailable]"}}function H(r,e){return m(r).slice(0,e)}function w(){try{return performance.now()}catch{return Date.now()}}function _(){try{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch{}return`${Date.now().toString(36)}-${(++z).toString(36)}-${Math.random().toString(36).slice(2,10)}`}function C(r,e){try{if(e?.aborted)return!0}catch{}return c(r,"name")==="AbortError"||c(r,"code")===20}function N(r){if(r==null)return{message:"Unknown error"};let e=c(r,"message"),t=c(r,"name"),n=c(r,"stack");return{message:e==null?m(r):m(e),...t==null?{}:{name:m(t)},...n==null?{}:{stack:m(n)}}}function S(r){return new TextEncoder().encode(r).length}function b(r,e,t,n){return typeof r=="number"&&Number.isFinite(r)?Math.min(t,Math.max(e,Math.floor(r))):n}var q=class extends Error{constructor(t){super(`Collector responded ${t}`);s(this,"status",t)}},k=class extends Error{},A=class{constructor(e){s(this,"active");s(this,"endpoint");s(this,"options");s(this,"networkCaptureEnabled");s(this,"transportTimeoutMs");s(this,"startedAt",w());s(this,"viewId",_());s(this,"seenErrors",new WeakSet);s(this,"queue",[]);s(this,"maxQueueSize");s(this,"cancelWaits",new Set);s(this,"controllers",new Set);s(this,"destroySignal");s(this,"resolveDestroy");s(this,"interval");s(this,"flushing");s(this,"sequence",0);s(this,"inFlight",0);s(this,"sent",0);s(this,"dropped",0);s(this,"destroyed",!1);s(this,"onError",e=>{this.captureException(e.error??e.message??"Script error",{type:"javascript"})});s(this,"onRejection",e=>{this.captureException(e.reason,{type:"promise"})});s(this,"onVisibility",()=>{document.visibilityState==="hidden"&&this.tryBeacon()});if(this.options=e,this.active=e.enabled!==!1&&typeof window<"u"&&typeof document<"u",this.networkCaptureEnabled=e.captureNetwork!==!1&&(!e.transport||!!e.endpoint),this.maxQueueSize=b(e.maxQueueSize,1,100,50),this.transportTimeoutMs=b(e.transportTimeoutMs,250,6e4,5e3),this.destroySignal=new Promise(t=>{this.resolveDestroy=t}),!!this.active){if(!e.project||typeof e.project!="string")throw new Error("project is required");if(!e.transport&&!e.endpoint)throw new Error("endpoint or transport is required");this.endpoint=e.endpoint?this.absoluteUrl(e.endpoint):void 0,window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onRejection),document.addEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&X(this),e.captureConsole===!0&&Y(this),this.interval=setInterval(()=>{this.flush()},b(e.flushIntervalMs,10,6e4,1e4))}}captureException(e,t={}){if(!(!this.active||this.destroyed||C(e)))try{if(e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.has(e))return;let n=N(e),o=B.has(t.type)?t.type:"manual",i=this.makeEvent(o,n.message,{name:n.name,stack:n.stack,componentStack:t.componentStack,context:t.context});this.enqueue(i)&&e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.add(e)}catch(n){n instanceof k&&this.dropped++}}reportNetwork(e,t,n,o,i){if(!(this.destroyed||!this.active||this.isCollectorUrl(t)||C(i)))try{if(n!==void 0&&!(this.options.shouldCaptureHttp?this.options.shouldCaptureHttp(n,t,e):n>=500))return;let a={method:H(e||"GET",16).toUpperCase(),url:t,status:n,durationMs:o},d=i===void 0?void 0:N(i),l=this.makeEvent(n===void 0?"network":"http",n===void 0?d?.message||"Network request failed":`HTTP ${n}`,{name:d?.name,network:a});this.enqueue(l)}catch{this.dropped++}}captureConsole(e){if(!(!this.active||this.destroyed))try{this.enqueue(this.makeEvent("console",e.map(m).join(" ")))}catch{this.dropped++}}flush(){return!this.active||this.destroyed?Promise.resolve():this.flushing?this.flushing:(this.flushing=this.drain().finally(()=>{this.flushing=void 0}),this.flushing)}destroy(){if(!this.destroyed){this.destroyed=!0,this.interval&&clearInterval(this.interval),this.active&&(window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onRejection),document.removeEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&D(this),this.options.captureConsole===!0&&W(this)),this.dropped+=this.queue.length+this.inFlight,this.queue.length=0;for(let e of this.cancelWaits)e();for(let e of this.controllers)e.abort();this.resolveDestroy(U)}}getStats(){return{queued:this.queue.length,sent:this.sent,dropped:this.dropped}}absoluteUrl(e){try{return new URL(e,window.location.href).href}catch{return e}}isCollectorUrl(e){if(!this.endpoint)return!1;try{let t=new URL(e,window.location.href),n=new URL(this.endpoint,window.location.href);return t.origin===n.origin&&t.pathname===n.pathname}catch{return e===this.endpoint}}cleanUrl(e){let t=m(e);if(this.options.sanitizeUrl){try{t=this.options.sanitizeUrl(t)}catch{throw new k("URL sanitizer failed")}if(typeof t!="string"||!t)throw new k("URL sanitizer returned no URL")}try{let n=new URL(t,window.location.href);return n.protocol!=="http:"&&n.protocol!=="https:"?"[unsupported URL]":(n.username="",n.password="",n.search="",n.hash="",this.cleanText(n.href,1024,!1))}catch{return this.cleanText(t,1024,!1).split(/[?#]/,1)[0]}}cleanText(e,t,n=!0){let o=m(e);return n&&(o=o.replace(/https?:\/\/[^\s<>"'()[\]]+/gi,i=>this.cleanUrl(i))),o.replace(/\bBearer\s+[A-Za-z0-9._~+\/-]+/gi,"Bearer [REDACTED]").replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,"[REDACTED_EMAIL]").slice(0,t)}cleanContext(e){if(!e||typeof e!="object")return;let t={},n;try{n=Object.keys(e)}catch{return}for(let o of n){if(Object.keys(t).length>=12)break;if(P.test(o))continue;let i=c(e,o),a=this.cleanText(o,80);typeof i=="string"?t[a]=this.cleanText(i,500):(typeof i=="number"&&Number.isFinite(i)||typeof i=="boolean"||i===null)&&(t[a]=i)}return Object.keys(t).length?t:void 0}cleanNetwork(e){if(!e||typeof e!="object")return;let t=c(e,"status"),n=typeof t=="number"&&Number.isInteger(t)&&t>=100&&t<=599?t:void 0;return{method:this.cleanText(c(e,"method")??"GET",16).toUpperCase(),url:this.cleanUrl(c(e,"url")??""),...n===void 0?{}:{status:n},durationMs:b(c(e,"durationMs"),0,36e5,0)}}makeEvent(e,t,n={}){let o=c(navigator,"userAgent"),i=this.cleanContext(n.context),a=this.cleanNetwork(n.network);return{schemaVersion:1,eventId:_(),project:this.cleanText(this.options.project,80),environment:this.cleanText(this.options.environment??"development",80),release:this.cleanText(this.options.release??"unknown",120),sdkVersion:"0.1.0",occurredAt:new Date().toISOString(),viewId:this.viewId,sequence:++this.sequence,elapsedMs:b(w()-this.startedAt,0,36e5,0),type:e,message:this.cleanText(t,1024),...n.name===void 0?{}:{name:this.cleanText(n.name,100)},...n.stack===void 0?{}:{stack:this.cleanText(n.stack,4096)},...n.componentStack===void 0?{}:{componentStack:this.cleanText(n.componentStack,2048)},page:this.cleanUrl(window.location.href),runtime:M.has(this.options.runtime)?this.options.runtime:"browser",...typeof o=="string"?{browser:this.cleanText(o,300)}:{},...i?{context:i}:{},...a?{network:a}:{}}}sanitizeReplacement(e,t){let n=c(e,"type"),o=c(e,"runtime"),i=c(e,"name"),a=c(e,"stack"),d=c(e,"componentStack"),l=c(e,"browser"),u=this.cleanContext(c(e,"context")),p=this.cleanNetwork(c(e,"network"));return{...t,type:B.has(n)?n:t.type,message:this.cleanText(c(e,"message")??t.message,1024),...i===void 0?{name:t.name}:{name:this.cleanText(i,100)},...a===void 0?{stack:t.stack}:{stack:this.cleanText(a,4096)},...d===void 0?{componentStack:t.componentStack}:{componentStack:this.cleanText(d,2048)},page:this.cleanUrl(c(e,"page")??t.page),runtime:M.has(o)?o:t.runtime,...l===void 0?{browser:t.browser}:{browser:this.cleanText(l,300)},...u?{context:u}:{context:void 0},...p?{network:p}:{network:void 0}}}enqueue(e){if(this.destroyed)return!1;let t=e;if(this.options.beforeSend)try{let n={...e,context:e.context&&{...e.context},network:e.network&&{...e.network}},o=this.options.beforeSend(n);if(o===!1||o===null)return!1;t=this.sanitizeReplacement(o===void 0?n:o,e)}catch{return this.dropped++,!1}return S(JSON.stringify(t))>48e3?(this.dropped++,!1):(this.queue.length>=this.maxQueueSize&&(this.queue.shift(),this.dropped++),this.queue.push(t),this.queue.length>=this.maxQueueSize&&this.flush(),!0)}takeBatch(e=20,t=48e3){let n=[],o=S('{"schemaVersion":1,"events":[]}');for(;this.queue.length&&n.length<e;){let i=this.queue[0],a=S(JSON.stringify(i))+(n.length?1:0);if(n.length&&o+a>t)break;if(!n.length&&o+a>t){this.queue.shift(),this.dropped++;continue}n.push(this.queue.shift()),o+=a}return{schemaVersion:1,events:n}}async drain(){for(;!this.destroyed&&this.queue.length;){let e=this.takeBatch();if(e.events.length){this.inFlight+=e.events.length;try{await this.sendWithRetry(e)}finally{this.inFlight-=e.events.length}}}}async sendWithRetry(e){for(let t=0;t<=L.length&&!this.destroyed;t++){let n=new AbortController;this.controllers.add(n);let o,i=new Promise((d,l)=>{o=setTimeout(()=>{n.abort(),l(new Error("Collector transport timed out"))},this.transportTimeoutMs)}),a;try{if(await Promise.race([this.send(e,n.signal),i,this.destroySignal])===U||this.destroyed)return;this.sent+=e.events.length;return}catch(d){if(this.destroyed)return;let l=c(d,"status");if(!(typeof l!="number"||l===429||l>=500)||t===L.length)break;a=L[t]}finally{o&&clearTimeout(o),this.controllers.delete(n)}a!==void 0&&await this.wait(a)}this.destroyed||(this.dropped+=e.events.length)}async send(e,t){if(this.options.transport){await this.options.transport(e,{signal:t});return}let n=await window.fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"omit",signal:t});if(!n.ok)throw new q(n.status)}wait(e){return new Promise(t=>{let n=()=>{clearTimeout(o),this.cancelWaits.delete(n),t()},o=setTimeout(n,e);this.cancelWaits.add(n)})}tryBeacon(){if(!(this.destroyed||this.options.transport||!this.endpoint||!this.queue.length))try{let e=new URL(this.endpoint);if(e.origin!==window.location.origin||e.search||e.hash||e.username||e.password||typeof navigator.sendBeacon!="function")return;let t=this.queue.slice(0,10);for(;t.length>0;){let o=JSON.stringify({schemaVersion:1,events:t});if(S(o)<=24e3){navigator.sendBeacon(this.endpoint,new Blob([o],{type:"application/json"}))&&this.queue.splice(0,t.length);return}t.pop()}}catch{}}},U=Symbol("destroyed"),T,R;function X(r){T||(T=F()),T.clients.add(r)}function D(r){let e=T;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),T=void 0))}function V(r,e){let t=typeof Request<"u"&&r instanceof Request?r:void 0;return{url:t?t.url:m(r),method:m(e?.method??t?.method??"GET"),signal:e?.signal??t?.signal}}function F(){let r=new Set,e=window.fetch,t;typeof e=="function"&&(t=function(...u){if(!r.size)return Reflect.apply(e,this,u);let p=w(),f;try{f=V(u[0],u[1])}catch{f={url:"",method:"GET"}}let g;try{g=Reflect.apply(e,this,u)}catch(h){if(!C(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h}return g.then(h=>{try{let v=h.status;for(let y of r)y.reportNetwork(f.method,f.url,v,w()-p)}catch{}return h},h=>{if(!C(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h})},window.fetch=t);let n=typeof XMLHttpRequest>"u"?void 0:XMLHttpRequest.prototype,o=n?.open,i=n?.send,a=new WeakMap,d,l;return n&&o&&i&&(d=(function(...u){let p=Reflect.apply(o,this,u);try{a.set(this,{method:m(u[0]),url:m(u[1])})}catch{}return p}),l=function(...u){if(!r.size)return Reflect.apply(i,this,u);let p=a.get(this);if(!p)return Reflect.apply(i,this,u);let f=w(),g=!1,h=!1,v=()=>{g=!0},y=()=>{h=!0},x=()=>{if(this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",x),!g)try{let E=this.status;if(E===0&&!h)return;for(let I of r)I.reportNetwork(p.method,p.url,E===0?void 0:E,w()-f)}catch{}};try{this.addEventListener("abort",v),this.addEventListener("error",y),this.addEventListener("timeout",y),this.addEventListener("loadend",x)}catch{return Reflect.apply(i,this,u)}try{return Reflect.apply(i,this,u)}catch(E){throw this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",x),E}},n.open=d,n.send=l),{clients:r,cleanup(){t&&window.fetch===t&&(window.fetch=e),n&&d&&n.open===d&&(n.open=o),n&&l&&n.send===l&&(n.send=i)}}}function Y(r){R||(R=Q()),R.clients.add(r)}function W(r){let e=R;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),R=void 0))}function Q(){let r=new Set,e=console.error,t=function(...n){try{return Reflect.apply(e,this,n)}finally{for(let o of r)o.captureConsole(n)}};return console.error=t,{clients:r,cleanup(){console.error===t&&(console.error=e)}}}function G(r){return new A(r)}export{G as init};
|
|
1
|
+
var j=Object.defineProperty;var O=(r,e,t)=>e in r?j(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var s=(r,e,t)=>O(r,typeof e!="symbol"?e+"":e,t);var L=[250,500],P=/(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i,B=new Set(["javascript","promise","react","console","http","network","manual"]),M=new Set(["browser","webview-ios","webview-android","unknown"]),z=0;function c(r,e){try{return r==null?void 0:Reflect.get(Object(r),e)}catch{return}}function m(r){try{return String(r)}catch{return"[unavailable]"}}function H(r,e){return m(r).slice(0,e)}function w(){try{return performance.now()}catch{return Date.now()}}function _(){try{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID()}catch{}return`${Date.now().toString(36)}-${(++z).toString(36)}-${Math.random().toString(36).slice(2,10)}`}function C(r,e){try{if(e?.aborted)return!0}catch{}return c(r,"name")==="AbortError"||c(r,"code")===20}function N(r){if(r==null)return{message:"Unknown error"};let e=c(r,"message"),t=c(r,"name"),n=c(r,"stack");return{message:e==null?m(r):m(e),...t==null?{}:{name:m(t)},...n==null?{}:{stack:m(n)}}}function S(r){return new TextEncoder().encode(r).length}function b(r,e,t,n){return typeof r=="number"&&Number.isFinite(r)?Math.min(t,Math.max(e,Math.floor(r))):n}var q=class extends Error{constructor(t){super(`Collector responded ${t}`);s(this,"status",t)}},k=class extends Error{},A=class{constructor(e){s(this,"active");s(this,"endpoint");s(this,"options");s(this,"networkCaptureEnabled");s(this,"transportTimeoutMs");s(this,"startedAt",w());s(this,"viewId",_());s(this,"seenErrors",new WeakSet);s(this,"queue",[]);s(this,"maxQueueSize");s(this,"cancelWaits",new Set);s(this,"controllers",new Set);s(this,"destroySignal");s(this,"resolveDestroy");s(this,"interval");s(this,"flushing");s(this,"sequence",0);s(this,"inFlight",0);s(this,"sent",0);s(this,"dropped",0);s(this,"destroyed",!1);s(this,"onError",e=>{this.captureException(e.error??e.message??"Script error",{type:"javascript"})});s(this,"onRejection",e=>{this.captureException(e.reason,{type:"promise"})});s(this,"onVisibility",()=>{document.visibilityState==="hidden"&&this.tryBeacon()});if(this.options=e,this.active=e.enabled!==!1&&typeof window<"u"&&typeof document<"u",this.networkCaptureEnabled=e.captureNetwork!==!1&&(!e.transport||!!e.endpoint),this.maxQueueSize=b(e.maxQueueSize,1,100,50),this.transportTimeoutMs=b(e.transportTimeoutMs,250,6e4,5e3),this.destroySignal=new Promise(t=>{this.resolveDestroy=t}),!!this.active){if(!e.project||typeof e.project!="string")throw new Error("project is required");if(!e.transport&&!e.endpoint)throw new Error("endpoint or transport is required");this.endpoint=e.endpoint?this.absoluteUrl(e.endpoint):void 0,window.addEventListener("error",this.onError),window.addEventListener("unhandledrejection",this.onRejection),document.addEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&X(this),e.captureConsole===!0&&Y(this),this.interval=setInterval(()=>{this.flush()},b(e.flushIntervalMs,10,6e4,1e4))}}captureException(e,t={}){if(!(!this.active||this.destroyed||C(e)))try{if(e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.has(e))return;let n=N(e),o=B.has(t.type)?t.type:"manual",i=this.makeEvent(o,n.message,{name:n.name,stack:n.stack,componentStack:t.componentStack,context:t.context});this.enqueue(i)&&e!==null&&(typeof e=="object"||typeof e=="function")&&this.seenErrors.add(e)}catch(n){n instanceof k&&this.dropped++}}reportNetwork(e,t,n,o,i){if(!(this.destroyed||!this.active||this.isCollectorUrl(t)||C(i)))try{if(n!==void 0&&!(this.options.shouldCaptureHttp?this.options.shouldCaptureHttp(n,t,e):n>=500))return;let a={method:H(e||"GET",16).toUpperCase(),url:t,status:n,durationMs:o},d=i===void 0?void 0:N(i),l=this.makeEvent(n===void 0?"network":"http",n===void 0?d?.message||"Network request failed":`HTTP ${n}`,{name:d?.name,network:a});this.enqueue(l)}catch{this.dropped++}}captureConsole(e){if(!(!this.active||this.destroyed))try{this.enqueue(this.makeEvent("console",e.map(m).join(" ")))}catch{this.dropped++}}flush(){return!this.active||this.destroyed?Promise.resolve():this.flushing?this.flushing:(this.flushing=this.drain().finally(()=>{this.flushing=void 0}),this.flushing)}destroy(){if(!this.destroyed){this.destroyed=!0,this.interval&&clearInterval(this.interval),this.active&&(window.removeEventListener("error",this.onError),window.removeEventListener("unhandledrejection",this.onRejection),document.removeEventListener("visibilitychange",this.onVisibility),this.networkCaptureEnabled&&D(this),this.options.captureConsole===!0&&W(this)),this.dropped+=this.queue.length+this.inFlight,this.queue.length=0;for(let e of this.cancelWaits)e();for(let e of this.controllers)e.abort();this.resolveDestroy(U)}}getStats(){return{queued:this.queue.length,sent:this.sent,dropped:this.dropped}}absoluteUrl(e){try{return new URL(e,window.location.href).href}catch{return e}}isCollectorUrl(e){if(!this.endpoint)return!1;try{let t=new URL(e,window.location.href),n=new URL(this.endpoint,window.location.href);return t.origin===n.origin&&t.pathname===n.pathname}catch{return e===this.endpoint}}cleanUrl(e){let t=m(e);if(this.options.sanitizeUrl){try{t=this.options.sanitizeUrl(t)}catch{throw new k("URL sanitizer failed")}if(typeof t!="string"||!t)throw new k("URL sanitizer returned no URL")}try{let n=new URL(t,window.location.href);return n.protocol!=="http:"&&n.protocol!=="https:"?"[unsupported URL]":(n.username="",n.password="",n.search="",n.hash="",this.cleanText(n.href,1024,!1))}catch{return this.cleanText(t,1024,!1).split(/[?#]/,1)[0]}}cleanText(e,t,n=!0){let o=m(e);return n&&(o=o.replace(/https?:\/\/[^\s<>"'()[\]]+/gi,i=>this.cleanUrl(i))),o.replace(/\bBearer\s+[A-Za-z0-9._~+\/-]+/gi,"Bearer [REDACTED]").replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,"[REDACTED_EMAIL]").slice(0,t)}cleanContext(e){if(!e||typeof e!="object")return;let t={},n;try{n=Object.keys(e)}catch{return}for(let o of n){if(Object.keys(t).length>=12)break;if(P.test(o))continue;let i=c(e,o),a=this.cleanText(o,80);typeof i=="string"?t[a]=this.cleanText(i,500):(typeof i=="number"&&Number.isFinite(i)||typeof i=="boolean"||i===null)&&(t[a]=i)}return Object.keys(t).length?t:void 0}cleanNetwork(e){if(!e||typeof e!="object")return;let t=c(e,"status"),n=typeof t=="number"&&Number.isInteger(t)&&t>=100&&t<=599?t:void 0;return{method:this.cleanText(c(e,"method")??"GET",16).toUpperCase(),url:this.cleanUrl(c(e,"url")??""),...n===void 0?{}:{status:n},durationMs:b(c(e,"durationMs"),0,36e5,0)}}makeEvent(e,t,n={}){let o=c(navigator,"userAgent"),i=this.cleanContext(n.context),a=this.cleanNetwork(n.network);return{schemaVersion:1,eventId:_(),project:this.cleanText(this.options.project,80),environment:this.cleanText(this.options.environment??"development",80),release:this.cleanText(this.options.release??"unknown",120),sdkVersion:"0.1.1",occurredAt:new Date().toISOString(),viewId:this.viewId,sequence:++this.sequence,elapsedMs:b(w()-this.startedAt,0,36e5,0),type:e,message:this.cleanText(t,1024),...n.name===void 0?{}:{name:this.cleanText(n.name,100)},...n.stack===void 0?{}:{stack:this.cleanText(n.stack,4096)},...n.componentStack===void 0?{}:{componentStack:this.cleanText(n.componentStack,2048)},page:this.cleanUrl(window.location.href),runtime:M.has(this.options.runtime)?this.options.runtime:"browser",...typeof o=="string"?{browser:this.cleanText(o,300)}:{},...i?{context:i}:{},...a?{network:a}:{}}}sanitizeReplacement(e,t){let n=c(e,"type"),o=c(e,"runtime"),i=c(e,"name"),a=c(e,"stack"),d=c(e,"componentStack"),l=c(e,"browser"),u=this.cleanContext(c(e,"context")),p=this.cleanNetwork(c(e,"network"));return{...t,type:B.has(n)?n:t.type,message:this.cleanText(c(e,"message")??t.message,1024),...i===void 0?{name:t.name}:{name:this.cleanText(i,100)},...a===void 0?{stack:t.stack}:{stack:this.cleanText(a,4096)},...d===void 0?{componentStack:t.componentStack}:{componentStack:this.cleanText(d,2048)},page:this.cleanUrl(c(e,"page")??t.page),runtime:M.has(o)?o:t.runtime,...l===void 0?{browser:t.browser}:{browser:this.cleanText(l,300)},...u?{context:u}:{context:void 0},...p?{network:p}:{network:void 0}}}enqueue(e){if(this.destroyed)return!1;let t=e;if(this.options.beforeSend)try{let n={...e,context:e.context&&{...e.context},network:e.network&&{...e.network}},o=this.options.beforeSend(n);if(o===!1||o===null)return!1;t=this.sanitizeReplacement(o===void 0?n:o,e)}catch{return this.dropped++,!1}return S(JSON.stringify(t))>48e3?(this.dropped++,!1):(this.queue.length>=this.maxQueueSize&&(this.queue.shift(),this.dropped++),this.queue.push(t),this.queue.length>=this.maxQueueSize&&this.flush(),!0)}takeBatch(e=20,t=48e3){let n=[],o=S('{"schemaVersion":1,"events":[]}');for(;this.queue.length&&n.length<e;){let i=this.queue[0],a=S(JSON.stringify(i))+(n.length?1:0);if(n.length&&o+a>t)break;if(!n.length&&o+a>t){this.queue.shift(),this.dropped++;continue}n.push(this.queue.shift()),o+=a}return{schemaVersion:1,events:n}}async drain(){for(;!this.destroyed&&this.queue.length;){let e=this.takeBatch();if(e.events.length){this.inFlight+=e.events.length;try{await this.sendWithRetry(e)}finally{this.inFlight-=e.events.length}}}}async sendWithRetry(e){for(let t=0;t<=L.length&&!this.destroyed;t++){let n=new AbortController;this.controllers.add(n);let o,i=new Promise((d,l)=>{o=setTimeout(()=>{n.abort(),l(new Error("Collector transport timed out"))},this.transportTimeoutMs)}),a;try{if(await Promise.race([this.send(e,n.signal),i,this.destroySignal])===U||this.destroyed)return;this.sent+=e.events.length;return}catch(d){if(this.destroyed)return;let l=c(d,"status");if(!(typeof l!="number"||l===429||l>=500)||t===L.length)break;a=L[t]}finally{o&&clearTimeout(o),this.controllers.delete(n)}a!==void 0&&await this.wait(a)}this.destroyed||(this.dropped+=e.events.length)}async send(e,t){if(this.options.transport){await this.options.transport(e,{signal:t});return}let n=await window.fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"omit",signal:t});if(!n.ok)throw new q(n.status)}wait(e){return new Promise(t=>{let n=()=>{clearTimeout(o),this.cancelWaits.delete(n),t()},o=setTimeout(n,e);this.cancelWaits.add(n)})}tryBeacon(){if(!(this.destroyed||this.options.transport||!this.endpoint||!this.queue.length))try{let e=new URL(this.endpoint);if(e.origin!==window.location.origin||e.search||e.hash||e.username||e.password||typeof navigator.sendBeacon!="function")return;let t=this.queue.slice(0,10);for(;t.length>0;){let o=JSON.stringify({schemaVersion:1,events:t});if(S(o)<=24e3){navigator.sendBeacon(this.endpoint,new Blob([o],{type:"application/json"}))&&this.queue.splice(0,t.length);return}t.pop()}}catch{}}},U=Symbol("destroyed"),T,R;function X(r){T||(T=F()),T.clients.add(r)}function D(r){let e=T;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),T=void 0))}function V(r,e){let t=typeof Request<"u"&&r instanceof Request?r:void 0;return{url:t?t.url:m(r),method:m(e?.method??t?.method??"GET"),signal:e?.signal??t?.signal}}function F(){let r=new Set,e=window.fetch,t;typeof e=="function"&&(t=function(...u){if(!r.size)return Reflect.apply(e,this,u);let p=w(),f;try{f=V(u[0],u[1])}catch{f={url:"",method:"GET"}}let g;try{g=Reflect.apply(e,this,u)}catch(h){if(!C(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h}return g.then(h=>{try{let v=h.status;for(let y of r)y.reportNetwork(f.method,f.url,v,w()-p)}catch{}return h},h=>{if(!C(h,f.signal))for(let v of r)v.reportNetwork(f.method,f.url,void 0,w()-p,h);throw h})},window.fetch=t);let n=typeof XMLHttpRequest>"u"?void 0:XMLHttpRequest.prototype,o=n?.open,i=n?.send,a=new WeakMap,d,l;return n&&o&&i&&(d=(function(...u){let p=Reflect.apply(o,this,u);try{a.set(this,{method:m(u[0]),url:m(u[1])})}catch{}return p}),l=function(...u){if(!r.size)return Reflect.apply(i,this,u);let p=a.get(this);if(!p)return Reflect.apply(i,this,u);let f=w(),g=!1,h=!1,v=()=>{g=!0},y=()=>{h=!0},x=()=>{if(this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",x),!g)try{let E=this.status;if(E===0&&!h)return;for(let I of r)I.reportNetwork(p.method,p.url,E===0?void 0:E,w()-f)}catch{}};try{this.addEventListener("abort",v),this.addEventListener("error",y),this.addEventListener("timeout",y),this.addEventListener("loadend",x)}catch{return Reflect.apply(i,this,u)}try{return Reflect.apply(i,this,u)}catch(E){throw this.removeEventListener("abort",v),this.removeEventListener("error",y),this.removeEventListener("timeout",y),this.removeEventListener("loadend",x),E}},n.open=d,n.send=l),{clients:r,cleanup(){t&&window.fetch===t&&(window.fetch=e),n&&d&&n.open===d&&(n.open=o),n&&l&&n.send===l&&(n.send=i)}}}function Y(r){R||(R=Q()),R.clients.add(r)}function W(r){let e=R;e&&(e.clients.delete(r),e.clients.size||(e.cleanup(),R=void 0))}function Q(){let r=new Set,e=console.error,t=function(...n){try{return Reflect.apply(e,this,n)}finally{for(let o of r)o.captureConsole(n)}};return console.error=t,{clients:r,cleanup(){console.error===t&&(console.error=e)}}}function G(r){return new A(r)}export{G as init};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ErrorEventRecord, EventBatch, EventType, Runtime } from 'browser-error-log-protocol';\n\nexport type { ErrorEventRecord, EventBatch } from 'browser-error-log-protocol';\n\nexport interface CaptureExtra {\n type?: EventType;\n context?: Record<string, unknown>;\n componentStack?: string;\n}\n\nexport interface TransportContext { signal: AbortSignal }\n\nexport interface BrowserErrorLogOptions {\n project: string;\n environment?: string;\n release?: string;\n endpoint?: string;\n transport?: (batch: EventBatch, context: TransportContext) => void | Promise<void>;\n runtime?: Runtime;\n enabled?: boolean;\n captureConsole?: boolean;\n /** Defaults to true. With a custom transport, automatic network capture requires endpoint for collector exclusion. */\n captureNetwork?: boolean;\n beforeSend?: (event: ErrorEventRecord) => ErrorEventRecord | false | null | void;\n sanitizeUrl?: (url: string) => string;\n shouldCaptureHttp?: (status: number, url: string, method: string) => boolean;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n /** Per-attempt deadline, clamped to 250\u201360000 ms; defaults to 5000 ms. */\n transportTimeoutMs?: number;\n}\n\nexport interface BrowserErrorLogClient {\n captureException(error: unknown, extra?: CaptureExtra): void;\n flush(): Promise<void>;\n destroy(): void;\n getStats(): { queued: number; sent: number; dropped: number };\n}\n\nconst MAX_BATCH_EVENTS = 20;\nconst MAX_BATCH_BYTES = 48_000;\nconst MAX_BEACON_EVENTS = 10;\nconst MAX_BEACON_BYTES = 24_000;\nconst RETRY_DELAYS_MS = [250, 500];\nconst SENSITIVE_KEY = /(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i;\nconst EVENT_TYPES = new Set<EventType>(['javascript', 'promise', 'react', 'console', 'http', 'network', 'manual']);\nconst RUNTIMES = new Set<Runtime>(['browser', 'webview-ios', 'webview-android', 'unknown']);\nlet fallbackId = 0;\n\nfunction read(object: unknown, key: PropertyKey): unknown {\n try { return object == null ? undefined : Reflect.get(Object(object), key); }\n catch { return undefined; }\n}\n\nfunction safeString(value: unknown): string {\n try { return String(value); }\n catch { return '[unavailable]'; }\n}\n\nfunction trim(value: unknown, length: number): string {\n return safeString(value).slice(0, length);\n}\n\nfunction now(): number {\n try { return performance.now(); }\n catch { return Date.now(); }\n}\n\nfunction newId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n } catch { /* fall through */ }\n return `${Date.now().toString(36)}-${(++fallbackId).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isAbort(error: unknown, signal?: AbortSignal | null): boolean {\n try { if (signal?.aborted) return true; }\n catch { /* ignore hostile signal */ }\n return read(error, 'name') === 'AbortError' || read(error, 'code') === 20;\n}\n\nfunction errorFields(error: unknown): { message: string; name?: string; stack?: string } {\n if (error === null || error === undefined) return { message: 'Unknown error' };\n const message = read(error, 'message');\n const name = read(error, 'name');\n const stack = read(error, 'stack');\n return {\n message: message == null ? safeString(error) : safeString(message),\n ...(name == null ? {} : { name: safeString(name) }),\n ...(stack == null ? {} : { stack: safeString(stack) }),\n };\n}\n\nfunction byteLength(value: string): number {\n return new TextEncoder().encode(value).length;\n}\n\nfunction boundedNumber(value: unknown, minimum: number, maximum: number, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(maximum, Math.max(minimum, Math.floor(value)))\n : fallback;\n}\n\nclass HttpDeliveryError extends Error {\n constructor(readonly status: number) { super(`Collector responded ${status}`); }\n}\n\nclass UrlSanitizationError extends Error {}\n\nclass Client implements BrowserErrorLogClient {\n private readonly active: boolean;\n private readonly endpoint?: string;\n private readonly options: BrowserErrorLogOptions;\n private readonly networkCaptureEnabled: boolean;\n private readonly transportTimeoutMs: number;\n private readonly startedAt = now();\n private readonly viewId = newId();\n private readonly seenErrors = new WeakSet<object>();\n private readonly queue: ErrorEventRecord[] = [];\n private readonly maxQueueSize: number;\n private readonly cancelWaits = new Set<() => void>();\n private readonly controllers = new Set<AbortController>();\n private readonly destroySignal: Promise<symbol>;\n private resolveDestroy!: (value: symbol) => void;\n private interval?: ReturnType<typeof setInterval>;\n private flushing?: Promise<void>;\n private sequence = 0;\n private inFlight = 0;\n private sent = 0;\n private dropped = 0;\n private destroyed = false;\n\n constructor(options: BrowserErrorLogOptions) {\n this.options = options;\n this.active = options.enabled !== false && typeof window !== 'undefined' && typeof document !== 'undefined';\n this.networkCaptureEnabled = options.captureNetwork !== false && (!options.transport || Boolean(options.endpoint));\n this.maxQueueSize = boundedNumber(options.maxQueueSize, 1, 100, 50);\n this.transportTimeoutMs = boundedNumber(options.transportTimeoutMs, 250, 60_000, 5_000);\n this.destroySignal = new Promise(resolve => { this.resolveDestroy = resolve; });\n if (!this.active) return;\n if (!options.project || typeof options.project !== 'string') throw new Error('project is required');\n if (!options.transport && !options.endpoint) throw new Error('endpoint or transport is required');\n this.endpoint = options.endpoint ? this.absoluteUrl(options.endpoint) : undefined;\n window.addEventListener('error', this.onError);\n window.addEventListener('unhandledrejection', this.onRejection);\n document.addEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) addNetworkClient(this);\n if (options.captureConsole === true) addConsoleClient(this);\n this.interval = setInterval(() => { void this.flush(); }, boundedNumber(options.flushIntervalMs, 10, 60_000, 10_000));\n }\n\n captureException(error: unknown, extra: CaptureExtra = {}): void {\n if (!this.active || this.destroyed || isAbort(error)) return;\n try {\n if (error !== null && (typeof error === 'object' || typeof error === 'function')) {\n if (this.seenErrors.has(error)) return;\n }\n const fields = errorFields(error);\n const type = EVENT_TYPES.has(extra.type as EventType) ? extra.type! : 'manual';\n const event = this.makeEvent(type, fields.message, {\n name: fields.name,\n stack: fields.stack,\n componentStack: extra.componentStack,\n context: extra.context,\n });\n if (this.enqueue(event) && error !== null && (typeof error === 'object' || typeof error === 'function')) {\n this.seenErrors.add(error);\n }\n } catch (error) {\n if (error instanceof UrlSanitizationError) this.dropped++;\n // Diagnostics must never affect the host app.\n }\n }\n\n reportNetwork(method: string, url: string, status: number | undefined, durationMs: number, failure?: unknown): void {\n if (this.destroyed || !this.active || this.isCollectorUrl(url) || isAbort(failure)) return;\n try {\n if (status !== undefined) {\n const include = this.options.shouldCaptureHttp\n ? this.options.shouldCaptureHttp(status, url, method)\n : status >= 500;\n if (!include) return;\n }\n const network = { method: trim(method || 'GET', 16).toUpperCase(), url, status, durationMs };\n const fields = failure === undefined ? undefined : errorFields(failure);\n const event = this.makeEvent(status === undefined ? 'network' : 'http',\n status === undefined ? (fields?.message || 'Network request failed') : `HTTP ${status}`,\n { name: fields?.name, network });\n this.enqueue(event);\n } catch { this.dropped++; /* a failing hook must not expose an unfiltered event */ }\n }\n\n captureConsole(values: unknown[]): void {\n if (!this.active || this.destroyed) return;\n try { this.enqueue(this.makeEvent('console', values.map(safeString).join(' '))); }\n catch { this.dropped++; /* console behavior is never changed by capture */ }\n }\n\n flush(): Promise<void> {\n if (!this.active || this.destroyed) return Promise.resolve();\n if (this.flushing) return this.flushing;\n this.flushing = this.drain().finally(() => { this.flushing = undefined; });\n return this.flushing;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.interval) clearInterval(this.interval);\n if (this.active) {\n window.removeEventListener('error', this.onError);\n window.removeEventListener('unhandledrejection', this.onRejection);\n document.removeEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) removeNetworkClient(this);\n if (this.options.captureConsole === true) removeConsoleClient(this);\n }\n this.dropped += this.queue.length + this.inFlight;\n this.queue.length = 0;\n for (const cancel of this.cancelWaits) cancel();\n for (const controller of this.controllers) controller.abort();\n this.resolveDestroy(DESTROYED);\n }\n\n getStats(): { queued: number; sent: number; dropped: number } {\n return { queued: this.queue.length, sent: this.sent, dropped: this.dropped };\n }\n\n private readonly onError = (event: ErrorEvent): void => {\n this.captureException(event.error ?? event.message ?? 'Script error', { type: 'javascript' });\n };\n\n private readonly onRejection = (event: PromiseRejectionEvent): void => {\n this.captureException(event.reason, { type: 'promise' });\n };\n\n private readonly onVisibility = (): void => {\n if (document.visibilityState === 'hidden') this.tryBeacon();\n };\n\n private absoluteUrl(url: string): string {\n try { return new URL(url, window.location.href).href; }\n catch { return url; }\n }\n\n private isCollectorUrl(url: string): boolean {\n if (!this.endpoint) return false;\n try {\n const request = new URL(url, window.location.href);\n const collector = new URL(this.endpoint, window.location.href);\n return request.origin === collector.origin && request.pathname === collector.pathname;\n } catch { return url === this.endpoint; }\n }\n\n private cleanUrl(input: unknown): string {\n let value = safeString(input);\n if (this.options.sanitizeUrl) {\n try { value = this.options.sanitizeUrl(value); }\n catch { throw new UrlSanitizationError('URL sanitizer failed'); }\n if (typeof value !== 'string' || !value) throw new UrlSanitizationError('URL sanitizer returned no URL');\n }\n try {\n const url = new URL(value, window.location.href);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '[unsupported URL]';\n url.username = '';\n url.password = '';\n url.search = '';\n url.hash = '';\n return this.cleanText(url.href, 1024, false);\n } catch { return this.cleanText(value, 1024, false).split(/[?#]/, 1)[0]; }\n }\n\n private cleanText(input: unknown, length: number, replaceUrls = true): string {\n let value = safeString(input);\n if (replaceUrls) {\n value = value.replace(/https?:\\/\\/[^\\s<>\"'()[\\]]+/gi, match => this.cleanUrl(match));\n }\n return value\n .replace(/\\bBearer\\s+[A-Za-z0-9._~+\\/-]+/gi, 'Bearer [REDACTED]')\n .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi, '[REDACTED_EMAIL]')\n .slice(0, length);\n }\n\n private cleanContext(input: unknown): ErrorEventRecord['context'] {\n if (!input || typeof input !== 'object') return undefined;\n const output: NonNullable<ErrorEventRecord['context']> = {};\n let keys: string[];\n try { keys = Object.keys(input); }\n catch { return undefined; }\n for (const key of keys) {\n if (Object.keys(output).length >= 12) break;\n if (SENSITIVE_KEY.test(key)) continue;\n const value = read(input, key);\n const safeKey = this.cleanText(key, 80);\n if (typeof value === 'string') output[safeKey] = this.cleanText(value, 500);\n else if (typeof value === 'number' && Number.isFinite(value)) output[safeKey] = value;\n else if (typeof value === 'boolean' || value === null) output[safeKey] = value;\n }\n return Object.keys(output).length ? output : undefined;\n }\n\n private cleanNetwork(input: unknown): ErrorEventRecord['network'] {\n if (!input || typeof input !== 'object') return undefined;\n const rawStatus = read(input, 'status');\n const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) && rawStatus >= 100 && rawStatus <= 599\n ? rawStatus : undefined;\n return {\n method: this.cleanText(read(input, 'method') ?? 'GET', 16).toUpperCase(),\n url: this.cleanUrl(read(input, 'url') ?? ''),\n ...(status === undefined ? {} : { status }),\n durationMs: boundedNumber(read(input, 'durationMs'), 0, 3_600_000, 0),\n };\n }\n\n private makeEvent(type: EventType, message: string, extra: {\n name?: string; stack?: string; componentStack?: string;\n context?: unknown; network?: unknown;\n } = {}): ErrorEventRecord {\n const userAgent = read(navigator, 'userAgent');\n const context = this.cleanContext(extra.context);\n const network = this.cleanNetwork(extra.network);\n return {\n schemaVersion: 1,\n eventId: newId(),\n project: this.cleanText(this.options.project, 80),\n environment: this.cleanText(this.options.environment ?? 'development', 80),\n release: this.cleanText(this.options.release ?? 'unknown', 120),\n sdkVersion: '0.1.0',\n occurredAt: new Date().toISOString(),\n viewId: this.viewId,\n sequence: ++this.sequence,\n elapsedMs: boundedNumber(now() - this.startedAt, 0, 3_600_000, 0),\n type,\n message: this.cleanText(message, 1024),\n ...(extra.name === undefined ? {} : { name: this.cleanText(extra.name, 100) }),\n ...(extra.stack === undefined ? {} : { stack: this.cleanText(extra.stack, 4096) }),\n ...(extra.componentStack === undefined ? {} : { componentStack: this.cleanText(extra.componentStack, 2048) }),\n page: this.cleanUrl(window.location.href),\n runtime: RUNTIMES.has(this.options.runtime as Runtime) ? this.options.runtime! : 'browser',\n ...(typeof userAgent === 'string' ? { browser: this.cleanText(userAgent, 300) } : {}),\n ...(context ? { context } : {}),\n ...(network ? { network } : {}),\n };\n }\n\n private sanitizeReplacement(candidate: unknown, base: ErrorEventRecord): ErrorEventRecord {\n const type = read(candidate, 'type');\n const runtime = read(candidate, 'runtime');\n const name = read(candidate, 'name');\n const stack = read(candidate, 'stack');\n const componentStack = read(candidate, 'componentStack');\n const browser = read(candidate, 'browser');\n const context = this.cleanContext(read(candidate, 'context'));\n const network = this.cleanNetwork(read(candidate, 'network'));\n return {\n ...base,\n type: EVENT_TYPES.has(type as EventType) ? type as EventType : base.type,\n message: this.cleanText(read(candidate, 'message') ?? base.message, 1024),\n ...(name === undefined ? { name: base.name } : { name: this.cleanText(name, 100) }),\n ...(stack === undefined ? { stack: base.stack } : { stack: this.cleanText(stack, 4096) }),\n ...(componentStack === undefined ? { componentStack: base.componentStack } : { componentStack: this.cleanText(componentStack, 2048) }),\n page: this.cleanUrl(read(candidate, 'page') ?? base.page),\n runtime: RUNTIMES.has(runtime as Runtime) ? runtime as Runtime : base.runtime,\n ...(browser === undefined ? { browser: base.browser } : { browser: this.cleanText(browser, 300) }),\n ...(context ? { context } : { context: undefined }),\n ...(network ? { network } : { network: undefined }),\n };\n }\n\n private enqueue(event: ErrorEventRecord): boolean {\n if (this.destroyed) return false;\n let finalEvent = event;\n if (this.options.beforeSend) {\n try {\n const copy = { ...event, context: event.context && { ...event.context }, network: event.network && { ...event.network } };\n const result = this.options.beforeSend(copy);\n if (result === false || result === null) return false;\n finalEvent = this.sanitizeReplacement(result === undefined ? copy : result, event);\n } catch { this.dropped++; return false; }\n }\n // A single event can never make a batch exceed its byte budget.\n if (byteLength(JSON.stringify(finalEvent)) > MAX_BATCH_BYTES) { this.dropped++; return false; }\n if (this.queue.length >= this.maxQueueSize) { this.queue.shift(); this.dropped++; }\n this.queue.push(finalEvent);\n if (this.queue.length >= this.maxQueueSize) void this.flush();\n return true;\n }\n\n private takeBatch(maxEvents = MAX_BATCH_EVENTS, maxBytes = MAX_BATCH_BYTES): EventBatch {\n const events: ErrorEventRecord[] = [];\n let bytes = byteLength('{\"schemaVersion\":1,\"events\":[]}');\n while (this.queue.length && events.length < maxEvents) {\n const next = this.queue[0];\n const size = byteLength(JSON.stringify(next)) + (events.length ? 1 : 0);\n if (events.length && bytes + size > maxBytes) break;\n if (!events.length && bytes + size > maxBytes) { this.queue.shift(); this.dropped++; continue; }\n events.push(this.queue.shift()!);\n bytes += size;\n }\n return { schemaVersion: 1, events };\n }\n\n private async drain(): Promise<void> {\n while (!this.destroyed && this.queue.length) {\n const batch = this.takeBatch();\n if (batch.events.length) {\n this.inFlight += batch.events.length;\n try { await this.sendWithRetry(batch); }\n finally { this.inFlight -= batch.events.length; }\n }\n }\n }\n\n private async sendWithRetry(batch: EventBatch): Promise<void> {\n for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length && !this.destroyed; attempt++) {\n const controller = new AbortController();\n this.controllers.add(controller);\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timeoutHandle = setTimeout(() => {\n controller.abort();\n reject(new Error('Collector transport timed out'));\n }, this.transportTimeoutMs);\n });\n let retryDelay: number | undefined;\n try {\n const result = await Promise.race([this.send(batch, controller.signal), timeout, this.destroySignal]);\n if (result === DESTROYED || this.destroyed) return;\n this.sent += batch.events.length;\n return;\n } catch (error) {\n if (this.destroyed) return;\n const status = read(error, 'status');\n const retryable = typeof status !== 'number' || status === 429 || status >= 500;\n if (!retryable || attempt === RETRY_DELAYS_MS.length) break;\n retryDelay = RETRY_DELAYS_MS[attempt];\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n this.controllers.delete(controller);\n }\n if (retryDelay !== undefined) await this.wait(retryDelay);\n }\n if (!this.destroyed) this.dropped += batch.events.length;\n }\n\n private async send(batch: EventBatch, signal: AbortSignal): Promise<void> {\n if (this.options.transport) { await this.options.transport(batch, { signal }); return; }\n const response = await window.fetch(this.endpoint!, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(batch),\n credentials: 'omit',\n signal,\n });\n if (!response.ok) throw new HttpDeliveryError(response.status);\n }\n\n private wait(milliseconds: number): Promise<void> {\n return new Promise(resolve => {\n const finish = () => { clearTimeout(timer); this.cancelWaits.delete(finish); resolve(); };\n const timer = setTimeout(finish, milliseconds);\n this.cancelWaits.add(finish);\n });\n }\n\n private tryBeacon(): void {\n if (this.destroyed || this.options.transport || !this.endpoint || !this.queue.length) return;\n try {\n const url = new URL(this.endpoint);\n if (url.origin !== window.location.origin || url.search || url.hash || url.username || url.password) return;\n if (typeof navigator.sendBeacon !== 'function') return;\n const candidates = this.queue.slice(0, MAX_BEACON_EVENTS);\n while (candidates.length > 0) {\n const batch: EventBatch = { schemaVersion: 1, events: candidates };\n const body = JSON.stringify(batch);\n if (byteLength(body) <= MAX_BEACON_BYTES) {\n if (navigator.sendBeacon(this.endpoint, new Blob([body], { type: 'application/json' }))) {\n this.queue.splice(0, candidates.length);\n }\n return;\n }\n candidates.pop();\n }\n } catch { /* best effort only */ }\n }\n}\n\nconst DESTROYED = Symbol('destroyed');\n\ninterface Instrumentation {\n clients: Set<Client>;\n cleanup(): void;\n}\n\nlet networkInstrumentation: Instrumentation | undefined;\nlet consoleInstrumentation: Instrumentation | undefined;\n\nfunction addNetworkClient(client: Client): void {\n if (!networkInstrumentation) networkInstrumentation = installNetwork();\n networkInstrumentation.clients.add(client);\n}\n\nfunction removeNetworkClient(client: Client): void {\n const instrumentation = networkInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n networkInstrumentation = undefined;\n }\n}\n\nfunction describeFetch(input: RequestInfo | URL, init?: RequestInit): { url: string; method: string; signal?: AbortSignal | null } {\n const request = typeof Request !== 'undefined' && input instanceof Request ? input : undefined;\n const url = request ? request.url : safeString(input);\n return { url, method: safeString(init?.method ?? request?.method ?? 'GET'), signal: init?.signal ?? request?.signal };\n}\n\nfunction installNetwork(): Instrumentation {\n const clients = new Set<Client>();\n const originalFetch = window.fetch;\n let wrappedFetch: typeof fetch | undefined;\n if (typeof originalFetch === 'function') {\n wrappedFetch = function (this: typeof window, ...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n if (!clients.size) return Reflect.apply(originalFetch, this, args);\n const started = now();\n let request: ReturnType<typeof describeFetch>;\n try { request = describeFetch(args[0], args[1]); }\n catch { request = { url: '', method: 'GET' }; }\n let promise: ReturnType<typeof fetch>;\n try { promise = Reflect.apply(originalFetch, this, args); }\n catch (error) {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n }\n return promise.then(\n response => {\n try {\n const status = response.status;\n for (const client of clients) client.reportNetwork(request.method, request.url, status, now() - started);\n } catch { /* telemetry cannot change the fetch result */ }\n return response;\n },\n error => {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n },\n );\n };\n window.fetch = wrappedFetch;\n }\n\n const prototype = typeof XMLHttpRequest === 'undefined' ? undefined : XMLHttpRequest.prototype;\n const originalOpen = prototype?.open;\n const originalSend = prototype?.send;\n const metadata = new WeakMap<XMLHttpRequest, { method: string; url: string }>();\n let wrappedOpen: typeof XMLHttpRequest.prototype.open | undefined;\n let wrappedSend: typeof XMLHttpRequest.prototype.send | undefined;\n if (prototype && originalOpen && originalSend) {\n wrappedOpen = (function (this: XMLHttpRequest, ...args: unknown[]): void {\n const result = Reflect.apply(originalOpen, this, args);\n try { metadata.set(this, { method: safeString(args[0]), url: safeString(args[1]) }); }\n catch { /* native open already succeeded */ }\n return result;\n }) as typeof XMLHttpRequest.prototype.open;\n wrappedSend = function (this: XMLHttpRequest, ...args: Parameters<XMLHttpRequest['send']>): void {\n if (!clients.size) return Reflect.apply(originalSend, this, args);\n const details = metadata.get(this);\n if (!details) return Reflect.apply(originalSend, this, args);\n const started = now();\n let aborted = false;\n let failed = false;\n const onAbort = () => { aborted = true; };\n const onFailure = () => { failed = true; };\n const onLoadEnd = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n if (aborted) return;\n try {\n const status = this.status;\n if (status === 0 && !failed) return;\n for (const client of clients) client.reportNetwork(details.method, details.url,\n status === 0 ? undefined : status, now() - started);\n } catch { /* XHR state may be unavailable */ }\n };\n try {\n this.addEventListener('abort', onAbort);\n this.addEventListener('error', onFailure);\n this.addEventListener('timeout', onFailure);\n this.addEventListener('loadend', onLoadEnd);\n } catch { return Reflect.apply(originalSend, this, args); }\n try { return Reflect.apply(originalSend, this, args); }\n catch (error) {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n throw error;\n }\n };\n prototype.open = wrappedOpen!;\n prototype.send = wrappedSend;\n }\n return {\n clients,\n cleanup() {\n if (wrappedFetch && window.fetch === wrappedFetch) window.fetch = originalFetch;\n if (prototype && wrappedOpen && prototype.open === wrappedOpen) prototype.open = originalOpen!;\n if (prototype && wrappedSend && prototype.send === wrappedSend) prototype.send = originalSend!;\n },\n };\n}\n\nfunction addConsoleClient(client: Client): void {\n if (!consoleInstrumentation) consoleInstrumentation = installConsole();\n consoleInstrumentation.clients.add(client);\n}\n\nfunction removeConsoleClient(client: Client): void {\n const instrumentation = consoleInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n consoleInstrumentation = undefined;\n }\n}\n\nfunction installConsole(): Instrumentation {\n const clients = new Set<Client>();\n const original = console.error;\n const wrapped: typeof console.error = function (this: Console, ...values: unknown[]): void {\n try { return Reflect.apply(original, this, values); }\n finally { for (const client of clients) client.captureConsole(values); }\n };\n console.error = wrapped;\n return { clients, cleanup() { if (console.error === wrapped) console.error = original; } };\n}\n\nexport function init(options: BrowserErrorLogOptions): BrowserErrorLogClient {\n return new Client(options);\n}\n"],
|
|
4
|
+
"sourcesContent": ["import type { ErrorEventRecord, EventBatch, EventType, Runtime } from 'browser-error-log-protocol';\n\nexport type { ErrorEventRecord, EventBatch } from 'browser-error-log-protocol';\n\nexport interface CaptureExtra {\n type?: EventType;\n context?: Record<string, unknown>;\n componentStack?: string;\n}\n\nexport interface TransportContext { signal: AbortSignal }\n\nexport interface BrowserErrorLogOptions {\n project: string;\n environment?: string;\n release?: string;\n endpoint?: string;\n transport?: (batch: EventBatch, context: TransportContext) => void | Promise<void>;\n runtime?: Runtime;\n enabled?: boolean;\n captureConsole?: boolean;\n /** Defaults to true. With a custom transport, automatic network capture requires endpoint for collector exclusion. */\n captureNetwork?: boolean;\n beforeSend?: (event: ErrorEventRecord) => ErrorEventRecord | false | null | void;\n sanitizeUrl?: (url: string) => string;\n shouldCaptureHttp?: (status: number, url: string, method: string) => boolean;\n flushIntervalMs?: number;\n maxQueueSize?: number;\n /** Per-attempt deadline, clamped to 250\u201360000 ms; defaults to 5000 ms. */\n transportTimeoutMs?: number;\n}\n\nexport interface BrowserErrorLogClient {\n captureException(error: unknown, extra?: CaptureExtra): void;\n flush(): Promise<void>;\n destroy(): void;\n getStats(): { queued: number; sent: number; dropped: number };\n}\n\nconst MAX_BATCH_EVENTS = 20;\nconst MAX_BATCH_BYTES = 48_000;\nconst MAX_BEACON_EVENTS = 10;\nconst MAX_BEACON_BYTES = 24_000;\nconst RETRY_DELAYS_MS = [250, 500];\nconst SENSITIVE_KEY = /(?:password|passwd|token|secret|authorization|cookie|session|api.?key|credential|bearer|private.?key)/i;\nconst EVENT_TYPES = new Set<EventType>(['javascript', 'promise', 'react', 'console', 'http', 'network', 'manual']);\nconst RUNTIMES = new Set<Runtime>(['browser', 'webview-ios', 'webview-android', 'unknown']);\nlet fallbackId = 0;\n\nfunction read(object: unknown, key: PropertyKey): unknown {\n try { return object == null ? undefined : Reflect.get(Object(object), key); }\n catch { return undefined; }\n}\n\nfunction safeString(value: unknown): string {\n try { return String(value); }\n catch { return '[unavailable]'; }\n}\n\nfunction trim(value: unknown, length: number): string {\n return safeString(value).slice(0, length);\n}\n\nfunction now(): number {\n try { return performance.now(); }\n catch { return Date.now(); }\n}\n\nfunction newId(): string {\n try {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') return crypto.randomUUID();\n } catch { /* fall through */ }\n return `${Date.now().toString(36)}-${(++fallbackId).toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction isAbort(error: unknown, signal?: AbortSignal | null): boolean {\n try { if (signal?.aborted) return true; }\n catch { /* ignore hostile signal */ }\n return read(error, 'name') === 'AbortError' || read(error, 'code') === 20;\n}\n\nfunction errorFields(error: unknown): { message: string; name?: string; stack?: string } {\n if (error === null || error === undefined) return { message: 'Unknown error' };\n const message = read(error, 'message');\n const name = read(error, 'name');\n const stack = read(error, 'stack');\n return {\n message: message == null ? safeString(error) : safeString(message),\n ...(name == null ? {} : { name: safeString(name) }),\n ...(stack == null ? {} : { stack: safeString(stack) }),\n };\n}\n\nfunction byteLength(value: string): number {\n return new TextEncoder().encode(value).length;\n}\n\nfunction boundedNumber(value: unknown, minimum: number, maximum: number, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.min(maximum, Math.max(minimum, Math.floor(value)))\n : fallback;\n}\n\nclass HttpDeliveryError extends Error {\n constructor(readonly status: number) { super(`Collector responded ${status}`); }\n}\n\nclass UrlSanitizationError extends Error {}\n\nclass Client implements BrowserErrorLogClient {\n private readonly active: boolean;\n private readonly endpoint?: string;\n private readonly options: BrowserErrorLogOptions;\n private readonly networkCaptureEnabled: boolean;\n private readonly transportTimeoutMs: number;\n private readonly startedAt = now();\n private readonly viewId = newId();\n private readonly seenErrors = new WeakSet<object>();\n private readonly queue: ErrorEventRecord[] = [];\n private readonly maxQueueSize: number;\n private readonly cancelWaits = new Set<() => void>();\n private readonly controllers = new Set<AbortController>();\n private readonly destroySignal: Promise<symbol>;\n private resolveDestroy!: (value: symbol) => void;\n private interval?: ReturnType<typeof setInterval>;\n private flushing?: Promise<void>;\n private sequence = 0;\n private inFlight = 0;\n private sent = 0;\n private dropped = 0;\n private destroyed = false;\n\n constructor(options: BrowserErrorLogOptions) {\n this.options = options;\n this.active = options.enabled !== false && typeof window !== 'undefined' && typeof document !== 'undefined';\n this.networkCaptureEnabled = options.captureNetwork !== false && (!options.transport || Boolean(options.endpoint));\n this.maxQueueSize = boundedNumber(options.maxQueueSize, 1, 100, 50);\n this.transportTimeoutMs = boundedNumber(options.transportTimeoutMs, 250, 60_000, 5_000);\n this.destroySignal = new Promise(resolve => { this.resolveDestroy = resolve; });\n if (!this.active) return;\n if (!options.project || typeof options.project !== 'string') throw new Error('project is required');\n if (!options.transport && !options.endpoint) throw new Error('endpoint or transport is required');\n this.endpoint = options.endpoint ? this.absoluteUrl(options.endpoint) : undefined;\n window.addEventListener('error', this.onError);\n window.addEventListener('unhandledrejection', this.onRejection);\n document.addEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) addNetworkClient(this);\n if (options.captureConsole === true) addConsoleClient(this);\n this.interval = setInterval(() => { void this.flush(); }, boundedNumber(options.flushIntervalMs, 10, 60_000, 10_000));\n }\n\n captureException(error: unknown, extra: CaptureExtra = {}): void {\n if (!this.active || this.destroyed || isAbort(error)) return;\n try {\n if (error !== null && (typeof error === 'object' || typeof error === 'function')) {\n if (this.seenErrors.has(error)) return;\n }\n const fields = errorFields(error);\n const type = EVENT_TYPES.has(extra.type as EventType) ? extra.type! : 'manual';\n const event = this.makeEvent(type, fields.message, {\n name: fields.name,\n stack: fields.stack,\n componentStack: extra.componentStack,\n context: extra.context,\n });\n if (this.enqueue(event) && error !== null && (typeof error === 'object' || typeof error === 'function')) {\n this.seenErrors.add(error);\n }\n } catch (error) {\n if (error instanceof UrlSanitizationError) this.dropped++;\n // Diagnostics must never affect the host app.\n }\n }\n\n reportNetwork(method: string, url: string, status: number | undefined, durationMs: number, failure?: unknown): void {\n if (this.destroyed || !this.active || this.isCollectorUrl(url) || isAbort(failure)) return;\n try {\n if (status !== undefined) {\n const include = this.options.shouldCaptureHttp\n ? this.options.shouldCaptureHttp(status, url, method)\n : status >= 500;\n if (!include) return;\n }\n const network = { method: trim(method || 'GET', 16).toUpperCase(), url, status, durationMs };\n const fields = failure === undefined ? undefined : errorFields(failure);\n const event = this.makeEvent(status === undefined ? 'network' : 'http',\n status === undefined ? (fields?.message || 'Network request failed') : `HTTP ${status}`,\n { name: fields?.name, network });\n this.enqueue(event);\n } catch { this.dropped++; /* a failing hook must not expose an unfiltered event */ }\n }\n\n captureConsole(values: unknown[]): void {\n if (!this.active || this.destroyed) return;\n try { this.enqueue(this.makeEvent('console', values.map(safeString).join(' '))); }\n catch { this.dropped++; /* console behavior is never changed by capture */ }\n }\n\n flush(): Promise<void> {\n if (!this.active || this.destroyed) return Promise.resolve();\n if (this.flushing) return this.flushing;\n this.flushing = this.drain().finally(() => { this.flushing = undefined; });\n return this.flushing;\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n if (this.interval) clearInterval(this.interval);\n if (this.active) {\n window.removeEventListener('error', this.onError);\n window.removeEventListener('unhandledrejection', this.onRejection);\n document.removeEventListener('visibilitychange', this.onVisibility);\n if (this.networkCaptureEnabled) removeNetworkClient(this);\n if (this.options.captureConsole === true) removeConsoleClient(this);\n }\n this.dropped += this.queue.length + this.inFlight;\n this.queue.length = 0;\n for (const cancel of this.cancelWaits) cancel();\n for (const controller of this.controllers) controller.abort();\n this.resolveDestroy(DESTROYED);\n }\n\n getStats(): { queued: number; sent: number; dropped: number } {\n return { queued: this.queue.length, sent: this.sent, dropped: this.dropped };\n }\n\n private readonly onError = (event: ErrorEvent): void => {\n this.captureException(event.error ?? event.message ?? 'Script error', { type: 'javascript' });\n };\n\n private readonly onRejection = (event: PromiseRejectionEvent): void => {\n this.captureException(event.reason, { type: 'promise' });\n };\n\n private readonly onVisibility = (): void => {\n if (document.visibilityState === 'hidden') this.tryBeacon();\n };\n\n private absoluteUrl(url: string): string {\n try { return new URL(url, window.location.href).href; }\n catch { return url; }\n }\n\n private isCollectorUrl(url: string): boolean {\n if (!this.endpoint) return false;\n try {\n const request = new URL(url, window.location.href);\n const collector = new URL(this.endpoint, window.location.href);\n return request.origin === collector.origin && request.pathname === collector.pathname;\n } catch { return url === this.endpoint; }\n }\n\n private cleanUrl(input: unknown): string {\n let value = safeString(input);\n if (this.options.sanitizeUrl) {\n try { value = this.options.sanitizeUrl(value); }\n catch { throw new UrlSanitizationError('URL sanitizer failed'); }\n if (typeof value !== 'string' || !value) throw new UrlSanitizationError('URL sanitizer returned no URL');\n }\n try {\n const url = new URL(value, window.location.href);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '[unsupported URL]';\n url.username = '';\n url.password = '';\n url.search = '';\n url.hash = '';\n return this.cleanText(url.href, 1024, false);\n } catch { return this.cleanText(value, 1024, false).split(/[?#]/, 1)[0]; }\n }\n\n private cleanText(input: unknown, length: number, replaceUrls = true): string {\n let value = safeString(input);\n if (replaceUrls) {\n value = value.replace(/https?:\\/\\/[^\\s<>\"'()[\\]]+/gi, match => this.cleanUrl(match));\n }\n return value\n .replace(/\\bBearer\\s+[A-Za-z0-9._~+\\/-]+/gi, 'Bearer [REDACTED]')\n .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi, '[REDACTED_EMAIL]')\n .slice(0, length);\n }\n\n private cleanContext(input: unknown): ErrorEventRecord['context'] {\n if (!input || typeof input !== 'object') return undefined;\n const output: NonNullable<ErrorEventRecord['context']> = {};\n let keys: string[];\n try { keys = Object.keys(input); }\n catch { return undefined; }\n for (const key of keys) {\n if (Object.keys(output).length >= 12) break;\n if (SENSITIVE_KEY.test(key)) continue;\n const value = read(input, key);\n const safeKey = this.cleanText(key, 80);\n if (typeof value === 'string') output[safeKey] = this.cleanText(value, 500);\n else if (typeof value === 'number' && Number.isFinite(value)) output[safeKey] = value;\n else if (typeof value === 'boolean' || value === null) output[safeKey] = value;\n }\n return Object.keys(output).length ? output : undefined;\n }\n\n private cleanNetwork(input: unknown): ErrorEventRecord['network'] {\n if (!input || typeof input !== 'object') return undefined;\n const rawStatus = read(input, 'status');\n const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) && rawStatus >= 100 && rawStatus <= 599\n ? rawStatus : undefined;\n return {\n method: this.cleanText(read(input, 'method') ?? 'GET', 16).toUpperCase(),\n url: this.cleanUrl(read(input, 'url') ?? ''),\n ...(status === undefined ? {} : { status }),\n durationMs: boundedNumber(read(input, 'durationMs'), 0, 3_600_000, 0),\n };\n }\n\n private makeEvent(type: EventType, message: string, extra: {\n name?: string; stack?: string; componentStack?: string;\n context?: unknown; network?: unknown;\n } = {}): ErrorEventRecord {\n const userAgent = read(navigator, 'userAgent');\n const context = this.cleanContext(extra.context);\n const network = this.cleanNetwork(extra.network);\n return {\n schemaVersion: 1,\n eventId: newId(),\n project: this.cleanText(this.options.project, 80),\n environment: this.cleanText(this.options.environment ?? 'development', 80),\n release: this.cleanText(this.options.release ?? 'unknown', 120),\n sdkVersion: '0.1.1',\n occurredAt: new Date().toISOString(),\n viewId: this.viewId,\n sequence: ++this.sequence,\n elapsedMs: boundedNumber(now() - this.startedAt, 0, 3_600_000, 0),\n type,\n message: this.cleanText(message, 1024),\n ...(extra.name === undefined ? {} : { name: this.cleanText(extra.name, 100) }),\n ...(extra.stack === undefined ? {} : { stack: this.cleanText(extra.stack, 4096) }),\n ...(extra.componentStack === undefined ? {} : { componentStack: this.cleanText(extra.componentStack, 2048) }),\n page: this.cleanUrl(window.location.href),\n runtime: RUNTIMES.has(this.options.runtime as Runtime) ? this.options.runtime! : 'browser',\n ...(typeof userAgent === 'string' ? { browser: this.cleanText(userAgent, 300) } : {}),\n ...(context ? { context } : {}),\n ...(network ? { network } : {}),\n };\n }\n\n private sanitizeReplacement(candidate: unknown, base: ErrorEventRecord): ErrorEventRecord {\n const type = read(candidate, 'type');\n const runtime = read(candidate, 'runtime');\n const name = read(candidate, 'name');\n const stack = read(candidate, 'stack');\n const componentStack = read(candidate, 'componentStack');\n const browser = read(candidate, 'browser');\n const context = this.cleanContext(read(candidate, 'context'));\n const network = this.cleanNetwork(read(candidate, 'network'));\n return {\n ...base,\n type: EVENT_TYPES.has(type as EventType) ? type as EventType : base.type,\n message: this.cleanText(read(candidate, 'message') ?? base.message, 1024),\n ...(name === undefined ? { name: base.name } : { name: this.cleanText(name, 100) }),\n ...(stack === undefined ? { stack: base.stack } : { stack: this.cleanText(stack, 4096) }),\n ...(componentStack === undefined ? { componentStack: base.componentStack } : { componentStack: this.cleanText(componentStack, 2048) }),\n page: this.cleanUrl(read(candidate, 'page') ?? base.page),\n runtime: RUNTIMES.has(runtime as Runtime) ? runtime as Runtime : base.runtime,\n ...(browser === undefined ? { browser: base.browser } : { browser: this.cleanText(browser, 300) }),\n ...(context ? { context } : { context: undefined }),\n ...(network ? { network } : { network: undefined }),\n };\n }\n\n private enqueue(event: ErrorEventRecord): boolean {\n if (this.destroyed) return false;\n let finalEvent = event;\n if (this.options.beforeSend) {\n try {\n const copy = { ...event, context: event.context && { ...event.context }, network: event.network && { ...event.network } };\n const result = this.options.beforeSend(copy);\n if (result === false || result === null) return false;\n finalEvent = this.sanitizeReplacement(result === undefined ? copy : result, event);\n } catch { this.dropped++; return false; }\n }\n // A single event can never make a batch exceed its byte budget.\n if (byteLength(JSON.stringify(finalEvent)) > MAX_BATCH_BYTES) { this.dropped++; return false; }\n if (this.queue.length >= this.maxQueueSize) { this.queue.shift(); this.dropped++; }\n this.queue.push(finalEvent);\n if (this.queue.length >= this.maxQueueSize) void this.flush();\n return true;\n }\n\n private takeBatch(maxEvents = MAX_BATCH_EVENTS, maxBytes = MAX_BATCH_BYTES): EventBatch {\n const events: ErrorEventRecord[] = [];\n let bytes = byteLength('{\"schemaVersion\":1,\"events\":[]}');\n while (this.queue.length && events.length < maxEvents) {\n const next = this.queue[0];\n const size = byteLength(JSON.stringify(next)) + (events.length ? 1 : 0);\n if (events.length && bytes + size > maxBytes) break;\n if (!events.length && bytes + size > maxBytes) { this.queue.shift(); this.dropped++; continue; }\n events.push(this.queue.shift()!);\n bytes += size;\n }\n return { schemaVersion: 1, events };\n }\n\n private async drain(): Promise<void> {\n while (!this.destroyed && this.queue.length) {\n const batch = this.takeBatch();\n if (batch.events.length) {\n this.inFlight += batch.events.length;\n try { await this.sendWithRetry(batch); }\n finally { this.inFlight -= batch.events.length; }\n }\n }\n }\n\n private async sendWithRetry(batch: EventBatch): Promise<void> {\n for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length && !this.destroyed; attempt++) {\n const controller = new AbortController();\n this.controllers.add(controller);\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_resolve, reject) => {\n timeoutHandle = setTimeout(() => {\n controller.abort();\n reject(new Error('Collector transport timed out'));\n }, this.transportTimeoutMs);\n });\n let retryDelay: number | undefined;\n try {\n const result = await Promise.race([this.send(batch, controller.signal), timeout, this.destroySignal]);\n if (result === DESTROYED || this.destroyed) return;\n this.sent += batch.events.length;\n return;\n } catch (error) {\n if (this.destroyed) return;\n const status = read(error, 'status');\n const retryable = typeof status !== 'number' || status === 429 || status >= 500;\n if (!retryable || attempt === RETRY_DELAYS_MS.length) break;\n retryDelay = RETRY_DELAYS_MS[attempt];\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n this.controllers.delete(controller);\n }\n if (retryDelay !== undefined) await this.wait(retryDelay);\n }\n if (!this.destroyed) this.dropped += batch.events.length;\n }\n\n private async send(batch: EventBatch, signal: AbortSignal): Promise<void> {\n if (this.options.transport) { await this.options.transport(batch, { signal }); return; }\n const response = await window.fetch(this.endpoint!, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(batch),\n credentials: 'omit',\n signal,\n });\n if (!response.ok) throw new HttpDeliveryError(response.status);\n }\n\n private wait(milliseconds: number): Promise<void> {\n return new Promise(resolve => {\n const finish = () => { clearTimeout(timer); this.cancelWaits.delete(finish); resolve(); };\n const timer = setTimeout(finish, milliseconds);\n this.cancelWaits.add(finish);\n });\n }\n\n private tryBeacon(): void {\n if (this.destroyed || this.options.transport || !this.endpoint || !this.queue.length) return;\n try {\n const url = new URL(this.endpoint);\n if (url.origin !== window.location.origin || url.search || url.hash || url.username || url.password) return;\n if (typeof navigator.sendBeacon !== 'function') return;\n const candidates = this.queue.slice(0, MAX_BEACON_EVENTS);\n while (candidates.length > 0) {\n const batch: EventBatch = { schemaVersion: 1, events: candidates };\n const body = JSON.stringify(batch);\n if (byteLength(body) <= MAX_BEACON_BYTES) {\n if (navigator.sendBeacon(this.endpoint, new Blob([body], { type: 'application/json' }))) {\n this.queue.splice(0, candidates.length);\n }\n return;\n }\n candidates.pop();\n }\n } catch { /* best effort only */ }\n }\n}\n\nconst DESTROYED = Symbol('destroyed');\n\ninterface Instrumentation {\n clients: Set<Client>;\n cleanup(): void;\n}\n\nlet networkInstrumentation: Instrumentation | undefined;\nlet consoleInstrumentation: Instrumentation | undefined;\n\nfunction addNetworkClient(client: Client): void {\n if (!networkInstrumentation) networkInstrumentation = installNetwork();\n networkInstrumentation.clients.add(client);\n}\n\nfunction removeNetworkClient(client: Client): void {\n const instrumentation = networkInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n networkInstrumentation = undefined;\n }\n}\n\nfunction describeFetch(input: RequestInfo | URL, init?: RequestInit): { url: string; method: string; signal?: AbortSignal | null } {\n const request = typeof Request !== 'undefined' && input instanceof Request ? input : undefined;\n const url = request ? request.url : safeString(input);\n return { url, method: safeString(init?.method ?? request?.method ?? 'GET'), signal: init?.signal ?? request?.signal };\n}\n\nfunction installNetwork(): Instrumentation {\n const clients = new Set<Client>();\n const originalFetch = window.fetch;\n let wrappedFetch: typeof fetch | undefined;\n if (typeof originalFetch === 'function') {\n wrappedFetch = function (this: typeof window, ...args: Parameters<typeof fetch>): ReturnType<typeof fetch> {\n if (!clients.size) return Reflect.apply(originalFetch, this, args);\n const started = now();\n let request: ReturnType<typeof describeFetch>;\n try { request = describeFetch(args[0], args[1]); }\n catch { request = { url: '', method: 'GET' }; }\n let promise: ReturnType<typeof fetch>;\n try { promise = Reflect.apply(originalFetch, this, args); }\n catch (error) {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n }\n return promise.then(\n response => {\n try {\n const status = response.status;\n for (const client of clients) client.reportNetwork(request.method, request.url, status, now() - started);\n } catch { /* telemetry cannot change the fetch result */ }\n return response;\n },\n error => {\n if (!isAbort(error, request.signal)) for (const client of clients) client.reportNetwork(request.method, request.url, undefined, now() - started, error);\n throw error;\n },\n );\n };\n window.fetch = wrappedFetch;\n }\n\n const prototype = typeof XMLHttpRequest === 'undefined' ? undefined : XMLHttpRequest.prototype;\n const originalOpen = prototype?.open;\n const originalSend = prototype?.send;\n const metadata = new WeakMap<XMLHttpRequest, { method: string; url: string }>();\n let wrappedOpen: typeof XMLHttpRequest.prototype.open | undefined;\n let wrappedSend: typeof XMLHttpRequest.prototype.send | undefined;\n if (prototype && originalOpen && originalSend) {\n wrappedOpen = (function (this: XMLHttpRequest, ...args: unknown[]): void {\n const result = Reflect.apply(originalOpen, this, args);\n try { metadata.set(this, { method: safeString(args[0]), url: safeString(args[1]) }); }\n catch { /* native open already succeeded */ }\n return result;\n }) as typeof XMLHttpRequest.prototype.open;\n wrappedSend = function (this: XMLHttpRequest, ...args: Parameters<XMLHttpRequest['send']>): void {\n if (!clients.size) return Reflect.apply(originalSend, this, args);\n const details = metadata.get(this);\n if (!details) return Reflect.apply(originalSend, this, args);\n const started = now();\n let aborted = false;\n let failed = false;\n const onAbort = () => { aborted = true; };\n const onFailure = () => { failed = true; };\n const onLoadEnd = () => {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n if (aborted) return;\n try {\n const status = this.status;\n if (status === 0 && !failed) return;\n for (const client of clients) client.reportNetwork(details.method, details.url,\n status === 0 ? undefined : status, now() - started);\n } catch { /* XHR state may be unavailable */ }\n };\n try {\n this.addEventListener('abort', onAbort);\n this.addEventListener('error', onFailure);\n this.addEventListener('timeout', onFailure);\n this.addEventListener('loadend', onLoadEnd);\n } catch { return Reflect.apply(originalSend, this, args); }\n try { return Reflect.apply(originalSend, this, args); }\n catch (error) {\n this.removeEventListener('abort', onAbort);\n this.removeEventListener('error', onFailure);\n this.removeEventListener('timeout', onFailure);\n this.removeEventListener('loadend', onLoadEnd);\n throw error;\n }\n };\n prototype.open = wrappedOpen!;\n prototype.send = wrappedSend;\n }\n return {\n clients,\n cleanup() {\n if (wrappedFetch && window.fetch === wrappedFetch) window.fetch = originalFetch;\n if (prototype && wrappedOpen && prototype.open === wrappedOpen) prototype.open = originalOpen!;\n if (prototype && wrappedSend && prototype.send === wrappedSend) prototype.send = originalSend!;\n },\n };\n}\n\nfunction addConsoleClient(client: Client): void {\n if (!consoleInstrumentation) consoleInstrumentation = installConsole();\n consoleInstrumentation.clients.add(client);\n}\n\nfunction removeConsoleClient(client: Client): void {\n const instrumentation = consoleInstrumentation;\n if (!instrumentation) return;\n instrumentation.clients.delete(client);\n if (!instrumentation.clients.size) {\n instrumentation.cleanup();\n consoleInstrumentation = undefined;\n }\n}\n\nfunction installConsole(): Instrumentation {\n const clients = new Set<Client>();\n const original = console.error;\n const wrapped: typeof console.error = function (this: Console, ...values: unknown[]): void {\n try { return Reflect.apply(original, this, values); }\n finally { for (const client of clients) client.captureConsole(values); }\n };\n console.error = wrapped;\n return { clients, cleanup() { if (console.error === wrapped) console.error = original; } };\n}\n\nexport function init(options: BrowserErrorLogOptions): BrowserErrorLogClient {\n return new Client(options);\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["RETRY_DELAYS_MS", "SENSITIVE_KEY", "EVENT_TYPES", "RUNTIMES", "fallbackId", "read", "object", "key", "safeString", "value", "trim", "length", "now", "newId", "isAbort", "error", "signal", "errorFields", "message", "name", "stack", "byteLength", "boundedNumber", "minimum", "maximum", "fallback", "HttpDeliveryError", "status", "__publicField", "UrlSanitizationError", "Client", "options", "event", "resolve", "addNetworkClient", "addConsoleClient", "extra", "fields", "type", "method", "url", "durationMs", "failure", "network", "values", "removeNetworkClient", "removeConsoleClient", "cancel", "controller", "DESTROYED", "request", "collector", "input", "replaceUrls", "match", "output", "keys", "safeKey", "rawStatus", "userAgent", "context", "candidate", "base", "runtime", "componentStack", "browser", "finalEvent", "copy", "result", "maxEvents", "maxBytes", "events", "bytes", "next", "size", "batch", "attempt", "timeoutHandle", "timeout", "_resolve", "reject", "retryDelay", "response", "milliseconds", "finish", "timer", "candidates", "body", "networkInstrumentation", "consoleInstrumentation", "client", "installNetwork", "instrumentation", "describeFetch", "init", "clients", "originalFetch", "wrappedFetch", "args", "started", "promise", "prototype", "originalOpen", "originalSend", "metadata", "wrappedOpen", "wrappedSend", "details", "aborted", "failed", "onAbort", "onFailure", "onLoadEnd", "installConsole", "original", "wrapped"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-error-log",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A small, framework-independent browser error SDK for a collector you control.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"browser",
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"browser-error-log-protocol": "0.1.
|
|
26
|
+
"browser-error-log-protocol": "0.1.1"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
30
|
"README.md",
|
|
31
|
+
"README.ko.md",
|
|
31
32
|
"LICENSE"
|
|
32
33
|
],
|
|
33
34
|
"repository": {
|