@ultron-dev/tracker 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ interface TrackerConfig {
2
+ apiKey: string;
3
+ endpoint: string;
4
+ debug?: boolean;
5
+ /** Report all web vitals, not just poor ones. Default: false */
6
+ reportAllVitals?: boolean;
7
+ /** Threshold in ms above which a network request is flagged as slow. Default: 3000 */
8
+ slowRequestThreshold?: number;
9
+ }
10
+ type EventType = 'error' | 'network' | 'vital' | 'resource_error';
11
+ interface ErrorPayload {
12
+ event_type: EventType;
13
+ message: string;
14
+ stack: string;
15
+ url: string;
16
+ browser: string;
17
+ os: string;
18
+ /** viewport_width:viewport_height:pixel_ratio */
19
+ viewport: string;
20
+ /** e.g. "4g", "wifi", "unknown" */
21
+ connection: string;
22
+ session_id: string;
23
+ metadata?: Record<string, unknown>;
24
+ timestamp: number;
25
+ }
26
+ interface IngestBody {
27
+ errors: ErrorPayload[];
28
+ api_key?: string;
29
+ }
30
+
31
+ declare class UltronTracker {
32
+ private config;
33
+ private queue;
34
+ private attached;
35
+ private capturing;
36
+ private originalOnError;
37
+ private originalOnUnhandledRejection;
38
+ private removeResourceErrorListener;
39
+ constructor(config: TrackerConfig);
40
+ init(): void;
41
+ private buildPayload;
42
+ private attachErrorListeners;
43
+ captureError(error: Error, metadata?: Record<string, unknown>): void;
44
+ destroy(): void;
45
+ }
46
+
47
+ /**
48
+ * Initialize the Ultron error tracker.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { initTracker } from '@ultron/tracker'
53
+ *
54
+ * initTracker({
55
+ * apiKey: 'your-project-api-key',
56
+ * endpoint: 'https://yourdomain.com'
57
+ * })
58
+ * ```
59
+ */
60
+ declare function initTracker(config: TrackerConfig): UltronTracker;
61
+ /**
62
+ * Manually capture an error outside of the automatic listeners.
63
+ */
64
+ declare function captureError(error: Error, metadata?: Record<string, unknown>): void;
65
+ /**
66
+ * Destroy the current tracker instance and flush any pending errors.
67
+ */
68
+ declare function destroyTracker(): void;
69
+
70
+ export { type ErrorPayload, type IngestBody, type TrackerConfig, UltronTracker, captureError, destroyTracker, initTracker };
@@ -0,0 +1,70 @@
1
+ interface TrackerConfig {
2
+ apiKey: string;
3
+ endpoint: string;
4
+ debug?: boolean;
5
+ /** Report all web vitals, not just poor ones. Default: false */
6
+ reportAllVitals?: boolean;
7
+ /** Threshold in ms above which a network request is flagged as slow. Default: 3000 */
8
+ slowRequestThreshold?: number;
9
+ }
10
+ type EventType = 'error' | 'network' | 'vital' | 'resource_error';
11
+ interface ErrorPayload {
12
+ event_type: EventType;
13
+ message: string;
14
+ stack: string;
15
+ url: string;
16
+ browser: string;
17
+ os: string;
18
+ /** viewport_width:viewport_height:pixel_ratio */
19
+ viewport: string;
20
+ /** e.g. "4g", "wifi", "unknown" */
21
+ connection: string;
22
+ session_id: string;
23
+ metadata?: Record<string, unknown>;
24
+ timestamp: number;
25
+ }
26
+ interface IngestBody {
27
+ errors: ErrorPayload[];
28
+ api_key?: string;
29
+ }
30
+
31
+ declare class UltronTracker {
32
+ private config;
33
+ private queue;
34
+ private attached;
35
+ private capturing;
36
+ private originalOnError;
37
+ private originalOnUnhandledRejection;
38
+ private removeResourceErrorListener;
39
+ constructor(config: TrackerConfig);
40
+ init(): void;
41
+ private buildPayload;
42
+ private attachErrorListeners;
43
+ captureError(error: Error, metadata?: Record<string, unknown>): void;
44
+ destroy(): void;
45
+ }
46
+
47
+ /**
48
+ * Initialize the Ultron error tracker.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * import { initTracker } from '@ultron/tracker'
53
+ *
54
+ * initTracker({
55
+ * apiKey: 'your-project-api-key',
56
+ * endpoint: 'https://yourdomain.com'
57
+ * })
58
+ * ```
59
+ */
60
+ declare function initTracker(config: TrackerConfig): UltronTracker;
61
+ /**
62
+ * Manually capture an error outside of the automatic listeners.
63
+ */
64
+ declare function captureError(error: Error, metadata?: Record<string, unknown>): void;
65
+ /**
66
+ * Destroy the current tracker instance and flush any pending errors.
67
+ */
68
+ declare function destroyTracker(): void;
69
+
70
+ export { type ErrorPayload, type IngestBody, type TrackerConfig, UltronTracker, captureError, destroyTracker, initTracker };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var $=Object.defineProperty,H=Object.defineProperties;var M=Object.getOwnPropertyDescriptors;var O=Object.getOwnPropertySymbols;var N=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var T=(t,e,r)=>e in t?$(t,e,{enumerable:true,configurable:true,writable:true,value:r}):t[e]=r,_=(t,e)=>{for(var r in e||(e={}))N.call(e,r)&&T(t,r,e[r]);if(O)for(var r of O(e))V.call(e,r)&&T(t,r,e[r]);return t},L=(t,e)=>H(t,M(e));var q="__ultron_sid__";function C(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=Math.random()*16|0;return (t==="x"?e:e&3|8).toString(16)})}var w=null;function p(){if(w)return w;try{let t=sessionStorage.getItem(q);if(t)return w=t,t;let e=C();return sessionStorage.setItem(q,e),w=e,e}catch(t){return w||(w=C()),w}}function g(t){return /Windows NT/.test(t)?"Windows":/Mac OS X/.test(t)?"macOS":/Android/.test(t)?"Android":/iPhone|iPad|iPod/.test(t)?"iOS":/Linux/.test(t)?"Linux":/CrOS/.test(t)?"ChromeOS":"Unknown"}function h(t){return /Edg\//.test(t)?"Edge":/OPR\/|Opera\//.test(t)?"Opera":/Chrome\//.test(t)?"Chrome":/Firefox\//.test(t)?"Firefox":/Safari\//.test(t)&&!/Chrome/.test(t)?"Safari":"Unknown"}function m(){var t;try{let e=window.innerWidth,r=window.innerHeight,n=Math.round((t=window.devicePixelRatio)!=null?t:1);return `${e}:${r}:${n}`}catch(e){return ""}}function y(){var t,e;try{let n=navigator.connection;return n&&(e=(t=n.type)!=null?t:n.effectiveType)!=null?e:"unknown"}catch(r){return "unknown"}}var S=class{constructor(e){this.queue=[];this.timer=null;this.flushing=false;this.config=e;}start(){this.timer=setInterval(()=>this.flush(),5e3),window.addEventListener("beforeunload",()=>this.flushSync()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flushSync();});}stop(){this.timer&&(clearInterval(this.timer),this.timer=null);}enqueue(e){this.queue.length>=50&&this.queue.shift(),this.queue.push(e),this.config.debug&&console.debug("[Ultron] Error queued:",e.message);}async flush(){if(this.queue.length===0||this.flushing)return;this.flushing=true;let e=this.queue.splice(0,this.queue.length),r={errors:e},n=`${this.config.endpoint}/api/ingest`;try{let c=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.config.apiKey},body:JSON.stringify(r),keepalive:!0});!c.ok&&this.config.debug&&console.warn("[Ultron] Flush failed:",c.status);}catch(c){this.config.debug&&console.warn("[Ultron] Flush error:",c),this.queue.unshift(...e.slice(0,50-this.queue.length));}finally{this.flushing=false;}}flushSync(){if(this.queue.length===0)return;let e=this.queue.splice(0,this.queue.length),r=`${this.config.endpoint}/api/ingest`,n={errors:e,api_key:this.config.apiKey};try{!navigator.sendBeacon(r,new Blob([JSON.stringify(n)],{type:"application/json"}))&&this.config.debug&&console.warn("[Ultron] sendBeacon returned false");}catch(c){this.config.debug&&console.warn("[Ultron] flushSync error:",c);}}};function k(t,e,r){let n=navigator.userAgent;return {event_type:"network",message:t,stack:"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:e,timestamp:Date.now()}}function P(t,e){try{let r=new URL(e).hostname,n=t.startsWith("http")?new URL(t).hostname:null;if(n&&n===r&&t.includes("/api/ingest"))return !0}catch(r){if(t.includes("/api/ingest"))return true}return false}function R(t,e){var s;let r=(s=e.slowRequestThreshold)!=null?s:3e3,n=window.fetch.bind(window);window.fetch=async function(i,a){var b;let u=typeof i=="string"?i:i instanceof Request?i.url:String(i);if(P(u,e.endpoint))return n(i,a);let v=((b=a==null?void 0:a.method)!=null?b:i instanceof Request?i.method:"GET").toUpperCase(),E=Date.now();try{let d=await n(i,a),l=Date.now()-E;return (!d.ok||l>r)&&t.enqueue(k(`${v} ${u} ${d.status} (${l}ms)`,{request_url:u,method:v,status:d.status,duration:l,slow:l>r},e)),d}catch(d){let l=Date.now()-E;throw t.enqueue(k(`${v} ${u} network error (${l}ms)`,{request_url:u,method:v,status:0,duration:l,error:String(d)})),d}};let c=XMLHttpRequest.prototype.open,o=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(i,a,...u){return this.__ultron_method=i.toUpperCase(),this.__ultron_url=String(a),c.call(this,i,a,...u)},XMLHttpRequest.prototype.send=function(...i){var E,b;let a=(E=this.__ultron_method)!=null?E:"GET",u=(b=this.__ultron_url)!=null?b:"",v=Date.now();return P(u,e.endpoint)||this.addEventListener("loadend",()=>{let d=Date.now()-v,l=this.status;(l===0||l>=400||d>r)&&t.enqueue(k(`${a} ${u} ${l||"network error"} (${d}ms)`,{request_url:u,method:a,status:l,duration:d,slow:d>r}));}),o.apply(this,i)};}var A={LCP:2500,CLS:.1,INP:200,FID:100,TTFB:800,FCP:1800};function D(t,e,r){let n=navigator.userAgent;return {event_type:"vital",message:`${t} ${Math.round(e)}${t==="CLS"?"":"ms"} (${r})`,stack:"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:{name:t,value:e,rating:r},timestamp:Date.now()}}function B(t,e){let r=A[t];return t==="CLS"?e<=.1?"good":e<=.25?"needs-improvement":"poor":e<=r?"good":e<=r*2?"needs-improvement":"poor"}function I(t,e){var c;let r=(c=e.reportAllVitals)!=null?c:false;function n(o,s){let i=B(o,s);(r||i!=="good")&&t.enqueue(D(o,s,i));}if(typeof PerformanceObserver!="undefined"){try{let o=0,s=new PerformanceObserver(i=>{let a=i.getEntries();o=a[a.length-1].startTime;});s.observe({type:"largest-contentful-paint",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&o>0&&(n("LCP",o),s.disconnect());},{once:!0});}catch(o){}try{let o=0,s=new PerformanceObserver(i=>{for(let a of i.getEntries()){let u=a;u.hadRecentInput||(o+=u.value);}});s.observe({type:"layout-shift",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(n("CLS",o),s.disconnect());},{once:!0});}catch(o){}try{let o=0,s=new PerformanceObserver(i=>{for(let a of i.getEntries()){let u=a;u.duration>o&&(o=u.duration);}});s.observe({type:"event",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&o>0&&(n("INP",o),s.disconnect());},{once:!0});}catch(o){}try{let o=new PerformanceObserver(s=>{for(let i of s.getEntries())i.name==="first-contentful-paint"&&(n("FCP",i.startTime),o.disconnect());});o.observe({type:"paint",buffered:!0});}catch(o){}try{let o=new PerformanceObserver(s=>{for(let i of s.getEntries()){let a=i;n("TTFB",a.responseStart-a.requestStart),o.disconnect();}});o.observe({type:"navigation",buffered:!0});}catch(o){}}}var F=new Set(["IMG","SCRIPT","LINK","AUDIO","VIDEO","SOURCE"]);function U(t,e){let r=navigator.userAgent;function n(c){let o=c.target;if(!o||!F.has(o.tagName))return;let s=o.src||o.href||"unknown",i={event_type:"resource_error",message:`Failed to load ${o.tagName.toLowerCase()}: ${s}`,stack:"",url:window.location.href,browser:h(r),os:g(r),viewport:m(),connection:y(),session_id:p(),metadata:{tag:o.tagName.toLowerCase(),src:s},timestamp:Date.now()};t.enqueue(i);}return window.addEventListener("error",n,true),()=>window.removeEventListener("error",n,true)}var x=class{constructor(e){this.attached=false;this.capturing=false;this.originalOnError=null;this.originalOnUnhandledRejection=null;this.removeResourceErrorListener=null;this.config=e,this.queue=new S(e);}init(){this.attached||(this.attached=true,this.attachErrorListeners(),this.removeResourceErrorListener=U(this.queue,this.config),R(this.queue,this.config),I(this.queue,this.config),this.queue.start(),this.config.debug&&console.debug("[Ultron] Tracker initialized for endpoint:",this.config.endpoint));}buildPayload(e,r){let n=navigator.userAgent;return {event_type:"error",message:e.message||"Unknown error",stack:e.stack||"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:r,timestamp:Date.now()}}attachErrorListeners(){let e=this;this.originalOnError=window.onerror,window.onerror=function(r,n,c,o,s){var i;return e.capturing||e.captureError(s!=null?s:new Error(String(r))),typeof e.originalOnError=="function"&&(i=e.originalOnError.call(this,r,n,c,o,s))!=null?i:false},this.originalOnUnhandledRejection=r=>{let n=r.reason,c=n instanceof Error?n:new Error(String(n));e.capturing||e.captureError(c);},window.addEventListener("unhandledrejection",this.originalOnUnhandledRejection);}captureError(e,r){if(!this.capturing){this.capturing=true;try{this.queue.enqueue(this.buildPayload(e,r));}finally{this.capturing=false;}}}destroy(){this.queue.stop(),this.originalOnError!==null&&(window.onerror=this.originalOnError),this.originalOnUnhandledRejection&&window.removeEventListener("unhandledrejection",this.originalOnUnhandledRejection),this.removeResourceErrorListener&&this.removeResourceErrorListener(),this.attached=false;}};var f=null;function le(t){if(f&&f.destroy(),!t.apiKey)throw new Error("[Ultron] apiKey is required");if(!t.endpoint)throw new Error("[Ultron] endpoint is required");let e=L(_({},t),{endpoint:t.endpoint.replace(/\/$/,"")});return f=new x(e),f.init(),f}function fe(t,e){if(!f){console.warn("[Ultron] captureError called before initTracker");return}f.captureError(t,e);}function pe(){f&&(f.destroy(),f=null);}
2
+ exports.UltronTracker=x;exports.captureError=fe;exports.destroyTracker=pe;exports.initTracker=le;
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ var $=Object.defineProperty,H=Object.defineProperties;var M=Object.getOwnPropertyDescriptors;var O=Object.getOwnPropertySymbols;var N=Object.prototype.hasOwnProperty,V=Object.prototype.propertyIsEnumerable;var T=(t,e,r)=>e in t?$(t,e,{enumerable:true,configurable:true,writable:true,value:r}):t[e]=r,_=(t,e)=>{for(var r in e||(e={}))N.call(e,r)&&T(t,r,e[r]);if(O)for(var r of O(e))V.call(e,r)&&T(t,r,e[r]);return t},L=(t,e)=>H(t,M(e));var q="__ultron_sid__";function C(){return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=Math.random()*16|0;return (t==="x"?e:e&3|8).toString(16)})}var w=null;function p(){if(w)return w;try{let t=sessionStorage.getItem(q);if(t)return w=t,t;let e=C();return sessionStorage.setItem(q,e),w=e,e}catch(t){return w||(w=C()),w}}function g(t){return /Windows NT/.test(t)?"Windows":/Mac OS X/.test(t)?"macOS":/Android/.test(t)?"Android":/iPhone|iPad|iPod/.test(t)?"iOS":/Linux/.test(t)?"Linux":/CrOS/.test(t)?"ChromeOS":"Unknown"}function h(t){return /Edg\//.test(t)?"Edge":/OPR\/|Opera\//.test(t)?"Opera":/Chrome\//.test(t)?"Chrome":/Firefox\//.test(t)?"Firefox":/Safari\//.test(t)&&!/Chrome/.test(t)?"Safari":"Unknown"}function m(){var t;try{let e=window.innerWidth,r=window.innerHeight,n=Math.round((t=window.devicePixelRatio)!=null?t:1);return `${e}:${r}:${n}`}catch(e){return ""}}function y(){var t,e;try{let n=navigator.connection;return n&&(e=(t=n.type)!=null?t:n.effectiveType)!=null?e:"unknown"}catch(r){return "unknown"}}var S=class{constructor(e){this.queue=[];this.timer=null;this.flushing=false;this.config=e;}start(){this.timer=setInterval(()=>this.flush(),5e3),window.addEventListener("beforeunload",()=>this.flushSync()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flushSync();});}stop(){this.timer&&(clearInterval(this.timer),this.timer=null);}enqueue(e){this.queue.length>=50&&this.queue.shift(),this.queue.push(e),this.config.debug&&console.debug("[Ultron] Error queued:",e.message);}async flush(){if(this.queue.length===0||this.flushing)return;this.flushing=true;let e=this.queue.splice(0,this.queue.length),r={errors:e},n=`${this.config.endpoint}/api/ingest`;try{let c=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.config.apiKey},body:JSON.stringify(r),keepalive:!0});!c.ok&&this.config.debug&&console.warn("[Ultron] Flush failed:",c.status);}catch(c){this.config.debug&&console.warn("[Ultron] Flush error:",c),this.queue.unshift(...e.slice(0,50-this.queue.length));}finally{this.flushing=false;}}flushSync(){if(this.queue.length===0)return;let e=this.queue.splice(0,this.queue.length),r=`${this.config.endpoint}/api/ingest`,n={errors:e,api_key:this.config.apiKey};try{!navigator.sendBeacon(r,new Blob([JSON.stringify(n)],{type:"application/json"}))&&this.config.debug&&console.warn("[Ultron] sendBeacon returned false");}catch(c){this.config.debug&&console.warn("[Ultron] flushSync error:",c);}}};function k(t,e,r){let n=navigator.userAgent;return {event_type:"network",message:t,stack:"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:e,timestamp:Date.now()}}function P(t,e){try{let r=new URL(e).hostname,n=t.startsWith("http")?new URL(t).hostname:null;if(n&&n===r&&t.includes("/api/ingest"))return !0}catch(r){if(t.includes("/api/ingest"))return true}return false}function R(t,e){var s;let r=(s=e.slowRequestThreshold)!=null?s:3e3,n=window.fetch.bind(window);window.fetch=async function(i,a){var b;let u=typeof i=="string"?i:i instanceof Request?i.url:String(i);if(P(u,e.endpoint))return n(i,a);let v=((b=a==null?void 0:a.method)!=null?b:i instanceof Request?i.method:"GET").toUpperCase(),E=Date.now();try{let d=await n(i,a),l=Date.now()-E;return (!d.ok||l>r)&&t.enqueue(k(`${v} ${u} ${d.status} (${l}ms)`,{request_url:u,method:v,status:d.status,duration:l,slow:l>r},e)),d}catch(d){let l=Date.now()-E;throw t.enqueue(k(`${v} ${u} network error (${l}ms)`,{request_url:u,method:v,status:0,duration:l,error:String(d)})),d}};let c=XMLHttpRequest.prototype.open,o=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(i,a,...u){return this.__ultron_method=i.toUpperCase(),this.__ultron_url=String(a),c.call(this,i,a,...u)},XMLHttpRequest.prototype.send=function(...i){var E,b;let a=(E=this.__ultron_method)!=null?E:"GET",u=(b=this.__ultron_url)!=null?b:"",v=Date.now();return P(u,e.endpoint)||this.addEventListener("loadend",()=>{let d=Date.now()-v,l=this.status;(l===0||l>=400||d>r)&&t.enqueue(k(`${a} ${u} ${l||"network error"} (${d}ms)`,{request_url:u,method:a,status:l,duration:d,slow:d>r}));}),o.apply(this,i)};}var A={LCP:2500,CLS:.1,INP:200,FID:100,TTFB:800,FCP:1800};function D(t,e,r){let n=navigator.userAgent;return {event_type:"vital",message:`${t} ${Math.round(e)}${t==="CLS"?"":"ms"} (${r})`,stack:"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:{name:t,value:e,rating:r},timestamp:Date.now()}}function B(t,e){let r=A[t];return t==="CLS"?e<=.1?"good":e<=.25?"needs-improvement":"poor":e<=r?"good":e<=r*2?"needs-improvement":"poor"}function I(t,e){var c;let r=(c=e.reportAllVitals)!=null?c:false;function n(o,s){let i=B(o,s);(r||i!=="good")&&t.enqueue(D(o,s,i));}if(typeof PerformanceObserver!="undefined"){try{let o=0,s=new PerformanceObserver(i=>{let a=i.getEntries();o=a[a.length-1].startTime;});s.observe({type:"largest-contentful-paint",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&o>0&&(n("LCP",o),s.disconnect());},{once:!0});}catch(o){}try{let o=0,s=new PerformanceObserver(i=>{for(let a of i.getEntries()){let u=a;u.hadRecentInput||(o+=u.value);}});s.observe({type:"layout-shift",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&(n("CLS",o),s.disconnect());},{once:!0});}catch(o){}try{let o=0,s=new PerformanceObserver(i=>{for(let a of i.getEntries()){let u=a;u.duration>o&&(o=u.duration);}});s.observe({type:"event",buffered:!0}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&o>0&&(n("INP",o),s.disconnect());},{once:!0});}catch(o){}try{let o=new PerformanceObserver(s=>{for(let i of s.getEntries())i.name==="first-contentful-paint"&&(n("FCP",i.startTime),o.disconnect());});o.observe({type:"paint",buffered:!0});}catch(o){}try{let o=new PerformanceObserver(s=>{for(let i of s.getEntries()){let a=i;n("TTFB",a.responseStart-a.requestStart),o.disconnect();}});o.observe({type:"navigation",buffered:!0});}catch(o){}}}var F=new Set(["IMG","SCRIPT","LINK","AUDIO","VIDEO","SOURCE"]);function U(t,e){let r=navigator.userAgent;function n(c){let o=c.target;if(!o||!F.has(o.tagName))return;let s=o.src||o.href||"unknown",i={event_type:"resource_error",message:`Failed to load ${o.tagName.toLowerCase()}: ${s}`,stack:"",url:window.location.href,browser:h(r),os:g(r),viewport:m(),connection:y(),session_id:p(),metadata:{tag:o.tagName.toLowerCase(),src:s},timestamp:Date.now()};t.enqueue(i);}return window.addEventListener("error",n,true),()=>window.removeEventListener("error",n,true)}var x=class{constructor(e){this.attached=false;this.capturing=false;this.originalOnError=null;this.originalOnUnhandledRejection=null;this.removeResourceErrorListener=null;this.config=e,this.queue=new S(e);}init(){this.attached||(this.attached=true,this.attachErrorListeners(),this.removeResourceErrorListener=U(this.queue,this.config),R(this.queue,this.config),I(this.queue,this.config),this.queue.start(),this.config.debug&&console.debug("[Ultron] Tracker initialized for endpoint:",this.config.endpoint));}buildPayload(e,r){let n=navigator.userAgent;return {event_type:"error",message:e.message||"Unknown error",stack:e.stack||"",url:window.location.href,browser:h(n),os:g(n),viewport:m(),connection:y(),session_id:p(),metadata:r,timestamp:Date.now()}}attachErrorListeners(){let e=this;this.originalOnError=window.onerror,window.onerror=function(r,n,c,o,s){var i;return e.capturing||e.captureError(s!=null?s:new Error(String(r))),typeof e.originalOnError=="function"&&(i=e.originalOnError.call(this,r,n,c,o,s))!=null?i:false},this.originalOnUnhandledRejection=r=>{let n=r.reason,c=n instanceof Error?n:new Error(String(n));e.capturing||e.captureError(c);},window.addEventListener("unhandledrejection",this.originalOnUnhandledRejection);}captureError(e,r){if(!this.capturing){this.capturing=true;try{this.queue.enqueue(this.buildPayload(e,r));}finally{this.capturing=false;}}}destroy(){this.queue.stop(),this.originalOnError!==null&&(window.onerror=this.originalOnError),this.originalOnUnhandledRejection&&window.removeEventListener("unhandledrejection",this.originalOnUnhandledRejection),this.removeResourceErrorListener&&this.removeResourceErrorListener(),this.attached=false;}};var f=null;function le(t){if(f&&f.destroy(),!t.apiKey)throw new Error("[Ultron] apiKey is required");if(!t.endpoint)throw new Error("[Ultron] endpoint is required");let e=L(_({},t),{endpoint:t.endpoint.replace(/\/$/,"")});return f=new x(e),f.init(),f}function fe(t,e){if(!f){console.warn("[Ultron] captureError called before initTracker");return}f.captureError(t,e);}function pe(){f&&(f.destroy(),f=null);}
2
+ export{x as UltronTracker,fe as captureError,pe as destroyTracker,le as initTracker};
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@ultron-dev/tracker",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight browser error tracking SDK — zero dependencies, under 5kb gzipped",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": ["dist", "README.md"],
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "typecheck": "tsc --noEmit"
20
+ },
21
+ "keywords": ["error-tracking", "browser", "monitoring"],
22
+ "license": "MIT",
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "devDependencies": {
27
+ "tsup": "^8.3.5",
28
+ "typescript": "^5.6.3"
29
+ }
30
+ }