@senzops/apm-node 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -9,15 +9,6 @@ interface SenzorOptions {
9
9
  declare const Senzor: {
10
10
  init: (options: SenzorOptions) => void;
11
11
  flush: () => Promise<void>;
12
- track: (data: {
13
- method: string;
14
- route: string;
15
- path: string;
16
- status: number;
17
- duration: number;
18
- ip?: string;
19
- userAgent?: string;
20
- }) => void;
21
12
  requestHandler: () => (req: any, res: any, next: () => void) => void;
22
13
  wrapNextRoute: (handler: Function) => (req: Request | any, context?: any) => Promise<any>;
23
14
  wrapNextPages: (handler: Function) => (req: any, res: any) => Promise<any>;
package/dist/index.d.ts CHANGED
@@ -9,15 +9,6 @@ interface SenzorOptions {
9
9
  declare const Senzor: {
10
10
  init: (options: SenzorOptions) => void;
11
11
  flush: () => Promise<void>;
12
- track: (data: {
13
- method: string;
14
- route: string;
15
- path: string;
16
- status: number;
17
- duration: number;
18
- ip?: string;
19
- userAgent?: string;
20
- }) => void;
21
12
  requestHandler: () => (req: any, res: any, next: () => void) => void;
22
13
  wrapNextRoute: (handler: Function) => (req: Request | any, context?: any) => Promise<any>;
23
14
  wrapNextPages: (handler: Function) => (req: any, res: any) => Promise<any>;
@@ -1,2 +1,2 @@
1
- "use strict";(()=>{var p=class{constructor(t){this.queue=[];this.timer=null;this.config=t,typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),this.config.flushInterval),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=this.config.batchSize&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(a){this.config.debug&&console.error("[Senzor] Ingestion Error:",a)}}};var d=class{constructor(){this.transport=null;this.options=null}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options={endpoint:"https://api.senzor.dev/api/ingest/apm",batchSize:100,flushInterval:1e4,debug:!1,...t},this.transport=new p({apiKey:this.options.apiKey,endpoint:this.options.endpoint,batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,debug:this.options.debug||!1}),this.options.debug&&console.log("[Senzor] Initialized")}track(t){this.transport&&this.transport.add({...t,timestamp:new Date().toISOString()})}async flush(){this.transport&&await this.transport.flush()}},o=new d;var h=()=>(e,t,a)=>{let r=performance.now();t.once("finish",()=>{try{let s=performance.now()-r,n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",o.track({method:e.method,route:n,path:e.originalUrl||e.url,status:t.statusCode,duration:s,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]})}catch{}}),a()};var c=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],l=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:c(t);var f=e=>async t=>{let a=performance.now(),r=200,s=null;try{let n=await e(t);return t.node?.res?.statusCode&&(r=t.node.res.statusCode),n}catch(n){throw s=n,r=n.statusCode||n.status||500,n}finally{let n=performance.now()-a,i=t.node.req,u=i.originalUrl||i.url||"/";o.track({method:i.method||"GET",route:l(t,u),path:u,status:r,duration:n,ip:b(i),userAgent:i.headers["user-agent"]})}},b=e=>e.headers["x-forwarded-for"]||e.socket?.remoteAddress;var m=e=>async(t,a)=>{let r=performance.now(),s=200;try{let n=await e(t,a);return n&&typeof n.status=="number"&&(s=n.status),n}catch(n){throw s=500,n}finally{let n=performance.now()-r,i=t.url?new URL(t.url):{pathname:"/"},u=c(i.pathname);o.track({method:t.method||"GET",route:u,path:i.pathname,status:s,duration:n,userAgent:t.headers.get?t.headers.get("user-agent"):void 0,ip:t.headers.get?t.headers.get("x-forwarded-for"):void 0})}},g=e=>async(t,a)=>{let r=performance.now(),s=()=>{let n=performance.now()-r,i=t.url||"/";o.track({method:t.method||"GET",route:c(i.split("?")[0]),path:i,status:a.statusCode||200,duration:n,ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress,userAgent:t.headers["user-agent"]})};return a.once("finish",s),a.once("close",s),e(t,a)};var y=(e,t,a)=>{t&&t.apiKey&&o.init(t),e.addHook("onRequest",(r,s,n)=>{r.senzorStart=performance.now(),n()}),e.addHook("onResponse",(r,s,n)=>{let i=performance.now()-(r.senzorStart||performance.now()),u=r.routeOptions?.url||r.routerPath;o.track({method:r.method,route:u||"UNKNOWN",path:r.raw.url||r.url,status:s.statusCode,duration:i,ip:r.ip,userAgent:r.headers["user-agent"]}),n()}),a()};var w={init:e=>o.init(e),flush:()=>o.flush(),track:o.track.bind(o),requestHandler:h,wrapNextRoute:m,wrapNextPages:g,wrapH3:f,fastifyPlugin:y},j=w;})();
1
+ "use strict";(()=>{var K=Object.create;var v=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var l=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var j=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of G(t))!L.call(e,o)&&o!==r&&v(e,o,{get:()=>t[o],enumerable:!(n=D(t,o))||n.enumerable});return e};var F=(e,t,r)=>(r=e!=null?K(W(e)):{},j(t||!e||!e.__esModule?v(r,"default",{value:e,enumerable:!0}):r,e));var w=class{constructor(t){this.config=t;this.queue=[];this.timer=null;typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),t.flushInterval||1e4),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=(this.config.batchSize||100)&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint||"https://api.senzor.dev/api/ingest/apm",{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(r){this.config.debug&&console.error("[Senzor] Ingestion Error:",r)}}};var P=l("async_hooks"),x=new P.AsyncLocalStorage,u={run:(e,t)=>x.run(e,t),current:()=>x.getStore(),addSpan:e=>{let t=x.getStore();t&&t.spans.push(e)}};var z=l("crypto");var b=F(l("http")),A=F(l("https")),S=l("url");var T=(e,t,r)=>{if(!e[t])return;let n=e[t];e[t]=r(n)},I=e=>{let t=new S.URL(e).hostname,r=n=>function(...o){let a={},c="";if(typeof o[0]=="string"||o[0]instanceof S.URL)c=o[0].toString(),a=o[1]||{};else{a=o[0]||{};let d=a.protocol||"http:",y=a.hostname||a.host||"localhost",M=a.path||"/";c=`${d}//${y}${M}`}if(c.includes(t)||a.hostname&&a.hostname.includes(t))return n.apply(this,o);let i=u.current();if(!i)return n.apply(this,o);let m=(a.method||"GET").toUpperCase(),h=performance.now()-i.startTime,f=performance.now(),p=n.apply(this,o);return p.on("response",d=>{d.on("end",()=>{let y=performance.now()-f;u.addSpan({name:`${m} ${new S.URL(c).hostname}`,type:"http",startTime:h,duration:y,status:d.statusCode,meta:{url:c,method:m}})})}),p.on("error",d=>{let y=performance.now()-f;u.addSpan({name:`${m} ${c}`,type:"http",startTime:h,duration:y,status:500,meta:{error:d.message}})}),p};T(b.default,"request",r),T(b.default,"get",r),T(A.default,"request",r),T(A.default,"get",r)};var N=()=>{try{let t=l("mongodb").Collection;["find","findOne","insertOne","insertMany","updateOne","updateMany","deleteOne","deleteMany","aggregate","countDocuments"].forEach(n=>{if(!t.prototype[n])return;let o=t.prototype[n];t.prototype[n]=function(...a){let c=u.current();if(!c)return o.apply(this,a);let i=performance.now()-c.startTime,m=performance.now(),h=this.collectionName,f=p=>{let d=performance.now()-m;u.addSpan({name:`MongoDB ${n} (${h})`,type:"db",startTime:i,duration:d,status:p?500:0,meta:{collection:h,operation:n,error:p?p.message:void 0}})};try{let p=o.apply(this,a);return p&&typeof p.then=="function"?p.then(d=>(f(),d),d=>{throw f(d),d}):(f(),p)}catch(p){throw f(p),p}}})}catch{}};var R=()=>{try{let e=l("pg"),t=e.Client.prototype.query;e.Client.prototype.query=function(...r){let n=u.current();if(!n)return t.apply(this,r);let o=performance.now()-n.startTime,a=performance.now(),c=typeof r[0]=="string"?r[0]:r[0].text,i=t.apply(this,r);return i&&typeof i.then=="function"?i.then(m=>{let h=performance.now()-a;return u.addSpan({name:"Postgres Query",type:"db",startTime:o,duration:h,meta:{query:c}}),m}):i}}catch{}};var C=class{constructor(){this.transport=null;this.options=null;this.isInstrumented=!1}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options=t;let r=t.endpoint||"https://api.senzor.dev/api/ingest/apm";if(this.transport=new w({...t,endpoint:r}),!this.isInstrumented){try{I(r)}catch{}try{N()}catch{}try{R()}catch{}this.isInstrumented=!0,t.debug&&console.log("[Senzor] Auto-instrumentation enabled")}t.debug&&console.log("[Senzor] Initialized")}startTrace(t,r){if(!this.transport)return r();let n={id:(0,z.randomUUID)(),startTime:performance.now(),data:t,spans:[]};return u.run(n,r)}endTrace(t,r={}){let n=u.current();if(!n||!this.transport)return;let o=performance.now()-n.startTime,a={traceId:n.id,...n.data,...r,status:t,duration:o,spans:n.spans,timestamp:new Date().toISOString()};this.transport.add(a)}track(t){if(!this.transport)return;let r={traceId:(0,z.randomUUID)(),...t,spans:[],timestamp:new Date().toISOString()};this.transport.add(r)}startSpan(t,r="custom"){let n=u.current();if(!n)return{end:()=>{}};let o=performance.now()-n.startTime,a=performance.now();return{end:(c,i)=>{let m=performance.now()-a;u.addSpan({name:t,type:r,startTime:o,duration:m,status:i,meta:c})}}}async flush(){this.transport&&await this.transport.flush()}},s=new C;var U=()=>(e,t,r)=>{s.startTrace({method:e.method,path:e.originalUrl||e.url,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]},()=>{t.once("finish",()=>{try{let n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",s.endTrace(t.statusCode,{route:n})}catch{}}),r()})};var g=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],O=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:g(t);var $=e=>t=>{let r=t.node.req,n=r.originalUrl||r.url||"/";return s.startTrace({method:r.method||"GET",path:n,ip:r.headers["x-forwarded-for"]||r.socket?.remoteAddress,userAgent:r.headers["user-agent"]},async()=>{try{let o=await e(t),a=200;return t.node.res.statusCode&&(a=t.node.res.statusCode),o&&o.statusCode&&(a=o.statusCode),s.endTrace(a,{route:O(t,n)}),o}catch(o){let a=o.statusCode||o.status||500;throw s.endTrace(a,{route:O(t,n)}),o}})};var E=e=>async(t,r)=>{let n=t.url?new URL(t.url):{pathname:"/"},o=t.method||"GET",a=t.headers.get?t.headers.get("user-agent"):void 0,c=t.headers.get?t.headers.get("x-forwarded-for"):void 0;return s.startTrace({method:o,path:n.pathname,userAgent:a,ip:c},async()=>{try{let i=await e(t,r),m=i?.status||200;return s.endTrace(m,{route:g(n.pathname)}),i}catch(i){throw s.endTrace(500,{route:g(n.pathname)}),i}})},H=e=>async(t,r)=>{let n=t.url?t.url.split("?")[0]:"/";return s.startTrace({method:t.method||"GET",path:n,userAgent:t.headers["user-agent"],ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress},async()=>{let o=()=>{s.endTrace(r.statusCode||200,{route:g(n)})};r.once("finish",o),r.once("close",o);try{return await e(t,r)}catch(a){throw a}})};var k=(e,t,r)=>{t&&t.apiKey&&s.init(t),e.addHook("onRequest",(n,o,a)=>{n.senzorStart=performance.now(),a()}),e.addHook("onResponse",(n,o,a)=>{let c=performance.now()-(n.senzorStart||performance.now()),i=n.routeOptions?.url||n.routerPath;s.track({method:n.method,route:i||"UNKNOWN",path:n.raw.url||n.url,status:o.statusCode,duration:c,ip:n.ip,userAgent:n.headers["user-agent"]}),a()}),r()};var J={init:e=>s.init(e),flush:()=>s.flush(),requestHandler:U,wrapNextRoute:E,wrapNextPages:H,wrapH3:$,fastifyPlugin:k},At=J;})();
2
2
  //# sourceMappingURL=index.global.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/transport.ts","../src/core/client.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts","../src/index.ts"],"sourcesContent":["export interface TransportConfig {\r\n apiKey: string;\r\n endpoint: string;\r\n batchSize: number;\r\n flushInterval: number;\r\n debug: boolean;\r\n}\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private config: TransportConfig;\r\n private timer: any = null;\r\n\r\n constructor(config: TransportConfig) {\r\n this.config = config;\r\n // Only start timer in non-serverless environments (long running processes)\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), this.config.flushInterval);\r\n // Unref if in Node.js to allow process exit\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref();\r\n }\r\n }\r\n }\r\n\r\n public add(event: any) {\r\n this.queue.push(event);\r\n if (this.queue.length >= this.config.batchSize) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use native fetch (Node 18+, Edge, Browser)\r\n await fetch(this.config.endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n // keepalive ensures connection stays open even if function ends (vital for APM)\r\n keepalive: true,\r\n });\r\n\r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // We drop data on failure to prevent memory leaks in the app\r\n }\r\n }\r\n}","import { Transport } from './transport';\r\n\r\nexport interface SenzorOptions {\r\n apiKey: string;\r\n endpoint?: string;\r\n batchSize?: number;\r\n flushInterval?: number;\r\n debug?: boolean;\r\n}\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n\r\n this.options = {\r\n endpoint: 'https://api.senzor.dev/api/ingest/apm',\r\n batchSize: 100,\r\n flushInterval: 10000,\r\n debug: false,\r\n ...options\r\n };\r\n\r\n this.transport = new Transport({\r\n apiKey: this.options.apiKey,\r\n endpoint: this.options.endpoint!,\r\n batchSize: this.options.batchSize!,\r\n flushInterval: this.options.flushInterval!,\r\n debug: this.options.debug || false\r\n });\r\n\r\n if (this.options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n // --- Manual Tracking (For any framework) ---\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n\r\n this.transport.add({\r\n ...data,\r\n timestamp: new Date().toISOString()\r\n });\r\n }\r\n\r\n // --- Force Flush (For Serverless/Lambda) ---\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // Start Timer (High Precision)\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n res.once('finish', () => {\r\n try {\r\n const duration = performance.now() - start;\r\n\r\n // Route Normalization Logic\r\n // Express stores route info in req.route\r\n let route = 'UNKNOWN';\r\n\r\n if (req.route && req.route.path) {\r\n // Combined baseUrl (if mounted on /api) + path (/:id)\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n // Fallback for unmapped routes or static files\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.track({\r\n method: req.method,\r\n route: route,\r\n path: req.originalUrl || req.url,\r\n status: res.statusCode,\r\n duration: duration,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Types for H3 (Mocked to avoid heavy peer dependencies)\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return async (event: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n let error: any = null;\r\n\r\n try {\r\n const response = await handler(event);\r\n // Try to determine status from response or event\r\n if (event.node?.res?.statusCode) {\r\n status = event.node.res.statusCode;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n error = err;\r\n status = err.statusCode || err.status || 500;\r\n throw err;\r\n } finally {\r\n // Non-blocking collection\r\n const duration = performance.now() - start;\r\n const req = event.node.req;\r\n\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: getRoute(event, path), // H3 often attaches context to event\r\n path: path,\r\n status: status,\r\n duration: duration,\r\n ip: getIp(req),\r\n userAgent: req.headers['user-agent'],\r\n });\r\n\r\n // If serverless, we might need to await flush, but for general H3 usage (Node preset)\r\n // we assume the process stays alive or uses ctx.waitUntil\r\n }\r\n };\r\n};\r\n\r\nconst getIp = (req: any) => {\r\n return req.headers['x-forwarded-for'] || req.socket?.remoteAddress;\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (GET, POST, etc.) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n\r\n try {\r\n const response = await handler(req, context);\r\n if (response && typeof response.status === 'number') {\r\n status = response.status;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n status = 500;\r\n throw err;\r\n } finally {\r\n const duration = performance.now() - start;\r\n\r\n // App Router Request is a standard Web Request object\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n\r\n // In App Router, we often rely on file-system path, but context.params helps\r\n // For now, robust heuristic normalization is best\r\n const route = normalizePath(url.pathname);\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: route,\r\n path: url.pathname,\r\n status: status,\r\n duration: duration,\r\n userAgent: req.headers.get ? req.headers.get('user-agent') : undefined,\r\n // IP extraction from Web Request is tricky without headers\r\n ip: req.headers.get ? req.headers.get('x-forwarded-for') : undefined,\r\n });\r\n\r\n // Vercel/Serverless Flush Safety\r\n // We purposefully do NOT await flush here to avoid latency.\r\n // Ideally user configures transport to sync flush or uses waitUntil\r\n }\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (req, res) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n const done = () => {\r\n const duration = performance.now() - start;\r\n const path = req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: normalizePath(path.split('?')[0]),\r\n path: path,\r\n status: res.statusCode || 200,\r\n duration: duration,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent']\r\n });\r\n };\r\n\r\n res.once('finish', done);\r\n // Handle error case if next/error isn't used\r\n res.once('close', done);\r\n\r\n return handler(req, res);\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/client';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};","import { client, SenzorOptions } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n track: client.track.bind(client),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };"],"mappings":"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","names":["Transport","config","event","batch","err","SenzorClient","options","Transport","data","client","expressMiddleware","req","res","next","start","duration","route","client","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","start","status","error","response","err","duration","req","path","client","getRoute","getIp","wrapNextRoute","handler","req","context","start","status","response","err","duration","url","route","normalizePath","client","wrapNextPages","res","done","path","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
1
+ {"version":3,"sources":["../src/core/transport.ts","../src/core/context.ts","../src/core/client.ts","../src/instrumentation/http.ts","../src/instrumentation/mongo.ts","../src/instrumentation/pg.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts","../src/index.ts"],"sourcesContent":["import { SenzorOptions } from './types';\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private timer: NodeJS.Timeout | null = null;\r\n\r\n constructor(private config: SenzorOptions) {\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), config.flushInterval || 10000);\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref(); // Don't block process exit\r\n }\r\n }\r\n }\r\n\r\n public add(trace: any) {\r\n this.queue.push(trace);\r\n if (this.queue.length >= (this.config.batchSize || 100)) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use global fetch (Node 18+)\r\n await fetch(this.config.endpoint || 'https://api.senzor.dev/api/ingest/apm', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n keepalive: true,\r\n });\r\n \r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // Dropping data to prevent memory leaks is preferred in APM\r\n }\r\n }\r\n}","import { AsyncLocalStorage } from 'async_hooks';\r\nimport { ActiveTrace } from './types';\r\n\r\n// Storage to hold the current Trace for any async operation\r\nexport const storage = new AsyncLocalStorage<ActiveTrace>();\r\n\r\nexport const Context = {\r\n // Run a function within a trace context\r\n // Updated to be Generic <T> to allow returning values (Promises, etc)\r\n run: <T>(trace: ActiveTrace, fn: () => T): T => {\r\n return storage.run(trace, fn);\r\n },\r\n\r\n // Get current trace (safe)\r\n current: (): ActiveTrace | undefined => {\r\n return storage.getStore();\r\n },\r\n\r\n // Add a span to the current trace\r\n addSpan: (span: any) => {\r\n const store = storage.getStore();\r\n if (store) {\r\n store.spans.push(span);\r\n }\r\n }\r\n};","import { Transport } from './transport';\r\nimport { Context } from './context';\r\nimport { SenzorOptions, ActiveTrace } from './types';\r\nimport { randomUUID } from 'crypto';\r\nimport { instrumentHttp } from '../instrumentation/http';\r\nimport { instrumentMongo } from '../instrumentation/mongo';\r\nimport { instrumentPg } from '../instrumentation/pg';\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n private isInstrumented = false;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n this.options = options;\r\n const endpoint = options.endpoint || 'https://api.senzor.dev/api/ingest/apm';\r\n\r\n this.transport = new Transport({\r\n ...options,\r\n endpoint\r\n });\r\n\r\n if (!this.isInstrumented) {\r\n try { instrumentHttp(endpoint); } catch (e) { }\r\n try { instrumentMongo(); } catch (e) { }\r\n try { instrumentPg(); } catch (e) { }\r\n\r\n this.isInstrumented = true;\r\n if (options.debug) console.log('[Senzor] Auto-instrumentation enabled');\r\n }\r\n\r\n if (options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n public startTrace<T>(data: Partial<ActiveTrace['data']>, next: () => T): T {\r\n if (!this.transport) return next();\r\n\r\n const trace: ActiveTrace = {\r\n id: randomUUID(),\r\n startTime: performance.now(),\r\n data: data,\r\n spans: []\r\n };\r\n\r\n return Context.run(trace, next);\r\n }\r\n\r\n public endTrace(status: number, extraData: any = {}) {\r\n const trace = Context.current();\r\n if (!trace || !this.transport) return;\r\n\r\n const duration = performance.now() - trace.startTime;\r\n\r\n const payload = {\r\n traceId: trace.id,\r\n ...trace.data,\r\n ...extraData,\r\n status,\r\n duration,\r\n spans: trace.spans,\r\n timestamp: new Date().toISOString()\r\n };\r\n\r\n this.transport.add(payload);\r\n }\r\n\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n const payload = {\r\n traceId: randomUUID(),\r\n ...data,\r\n spans: [],\r\n timestamp: new Date().toISOString()\r\n };\r\n this.transport.add(payload);\r\n }\r\n\r\n public startSpan(name: string, type: 'db' | 'http' | 'function' | 'custom' = 'custom') {\r\n const trace = Context.current();\r\n if (!trace) return { end: () => { } };\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n return {\r\n end: (meta?: any, status?: number) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name,\r\n type,\r\n startTime,\r\n duration,\r\n status,\r\n meta\r\n });\r\n }\r\n };\r\n }\r\n\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import http from 'http';\r\nimport https from 'https';\r\nimport { URL } from 'url';\r\nimport { Context } from '../core/context';\r\n\r\n// Helper to safely wrap modules\r\nconst shimmer = (module: any, methodName: string, wrapper: (original: Function) => Function) => {\r\n if (!module[methodName]) return;\r\n const original = module[methodName];\r\n module[methodName] = wrapper(original);\r\n};\r\n\r\nexport const instrumentHttp = (ingestUrl: string) => {\r\n const ingestHost = new URL(ingestUrl).hostname;\r\n\r\n const requestWrapper = (original: Function) => {\r\n return function (this: any, ...args: any[]) {\r\n // 1. Parse Arguments to get URL\r\n let options: any = {};\r\n let urlStr = '';\r\n\r\n if (typeof args[0] === 'string' || args[0] instanceof URL) {\r\n urlStr = args[0].toString();\r\n options = args[1] || {};\r\n } else {\r\n options = args[0] || {};\r\n const protocol = options.protocol || 'http:';\r\n const host = options.hostname || options.host || 'localhost';\r\n const path = options.path || '/';\r\n urlStr = `${protocol}//${host}${path}`;\r\n }\r\n\r\n // 2. SAFETY GUARD: Ignore calls to Senzor Ingest API (Prevent Infinite Loop)\r\n if (urlStr.includes(ingestHost) || (options.hostname && options.hostname.includes(ingestHost))) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 3. Check if we are inside an Active Trace\r\n // If we are not handling a user request, don't trace background http calls\r\n const trace = Context.current();\r\n if (!trace) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 4. Start Span\r\n const method = (options.method || 'GET').toUpperCase();\r\n const startTime = performance.now() - trace.startTime; // Relative to trace start\r\n const spanStartAbs = performance.now();\r\n\r\n // 5. Execute Request\r\n const req = original.apply(this, args);\r\n\r\n // 6. Hook into Response/Error\r\n req.on('response', (res: any) => {\r\n // Wait for end of stream to calculate full duration (TTFB + Download)\r\n res.on('end', () => {\r\n const duration = performance.now() - spanStartAbs;\r\n \r\n Context.addSpan({\r\n name: `${method} ${new URL(urlStr).hostname}`, // e.g. \"GET api.stripe.com\"\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: res.statusCode,\r\n meta: {\r\n url: urlStr,\r\n method: method,\r\n }\r\n });\r\n });\r\n });\r\n\r\n req.on('error', (err: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `${method} ${urlStr}`,\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: 500, // Client Error\r\n meta: { error: err.message }\r\n });\r\n });\r\n\r\n return req;\r\n };\r\n };\r\n\r\n // Apply patches\r\n shimmer(http, 'request', requestWrapper);\r\n shimmer(http, 'get', requestWrapper);\r\n shimmer(https, 'request', requestWrapper);\r\n shimmer(https, 'get', requestWrapper);\r\n};","import { Context } from '../core/context';\r\n\r\nexport const instrumentMongo = () => {\r\n try {\r\n const mongodb = require('mongodb');\r\n const Collection = mongodb.Collection;\r\n\r\n const methods = [\r\n 'find',\r\n 'findOne',\r\n 'insertOne',\r\n 'insertMany',\r\n 'updateOne',\r\n 'updateMany',\r\n 'deleteOne',\r\n 'deleteMany',\r\n 'aggregate',\r\n 'countDocuments'\r\n ];\r\n\r\n methods.forEach((method) => {\r\n if (!Collection.prototype[method]) return;\r\n\r\n const original = Collection.prototype[method];\r\n\r\n Collection.prototype[method] = function (...args: any[]) {\r\n const trace = Context.current();\r\n // If not inside a tracked request, just run normally\r\n if (!trace) return original.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n const collectionName = this.collectionName;\r\n\r\n // Helper to finish span\r\n const endSpan = (err?: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `MongoDB ${method} (${collectionName})`,\r\n type: 'db',\r\n startTime,\r\n duration,\r\n status: err ? 500 : 0,\r\n meta: {\r\n collection: collectionName,\r\n operation: method,\r\n error: err ? err.message : undefined\r\n }\r\n });\r\n };\r\n\r\n try {\r\n const result = original.apply(this, args);\r\n\r\n // Handle Promise/Cursor\r\n if (result && typeof result.then === 'function') {\r\n return result.then(\r\n (res: any) => { endSpan(); return res; },\r\n (err: any) => { endSpan(err); throw err; }\r\n );\r\n }\r\n // Handle FindCursor (it doesn't execute immediately, but for APM visuals we mark start/return)\r\n // Ideally we'd wrap toArray() but that's complex. For 'find', we just mark the creation.\r\n endSpan();\r\n return result;\r\n\r\n } catch (err: any) {\r\n endSpan(err);\r\n throw err;\r\n }\r\n };\r\n });\r\n\r\n } catch (e) {\r\n // User doesn't use mongodb, ignore\r\n }\r\n};","import { Context } from '../core/context';\r\n\r\n// Simple shim for 'pg' library\r\nexport const instrumentPg = () => {\r\n try {\r\n // Try to require pg (it might not be installed by user)\r\n const pg = require('pg');\r\n const originalQuery = pg.Client.prototype.query;\r\n\r\n pg.Client.prototype.query = function (...args: any[]) {\r\n const trace = Context.current();\r\n if (!trace) return originalQuery.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n // Extract SQL (first arg usually string or config object)\r\n const sql = typeof args[0] === 'string' ? args[0] : args[0].text;\r\n\r\n // Wrap callback if present, or handle Promise\r\n const result = originalQuery.apply(this, args);\r\n\r\n if (result && typeof result.then === 'function') {\r\n return result.then((res: any) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: 'Postgres Query',\r\n type: 'db',\r\n startTime,\r\n duration,\r\n meta: { query: sql }\r\n });\r\n return res;\r\n });\r\n }\r\n return result;\r\n };\r\n } catch (e) {\r\n // User doesn't use pg, ignore\r\n }\r\n};","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // We MUST use startTrace to enable Auto-Instrumentation for this request\r\n client.startTrace({\r\n method: req.method,\r\n path: req.originalUrl || req.url,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, () => {\r\n\r\n res.once('finish', () => {\r\n try {\r\n let route = 'UNKNOWN';\r\n if (req.route && req.route.path) {\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.endTrace(res.statusCode, { route });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n });\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Minimal types for H3 to avoid peer-deps\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return (event: any) => {\r\n const req = event.node.req;\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n // Start Trace Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, async () => {\r\n try {\r\n const response = await handler(event);\r\n\r\n // H3/Nitro response status\r\n let status = 200;\r\n if (event.node.res.statusCode) status = event.node.res.statusCode;\r\n // Check if response is an error object\r\n if (response && response.statusCode) status = response.statusCode;\r\n\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n return response;\r\n } catch (err: any) {\r\n const status = err.statusCode || err.status || 500;\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (Route Handlers) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n // 1. Extract Info\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n const method = req.method || 'GET';\r\n const ua = req.headers.get ? req.headers.get('user-agent') : undefined;\r\n const ip = req.headers.get ? req.headers.get('x-forwarded-for') : undefined;\r\n\r\n // 2. Run in Context\r\n return client.startTrace({\r\n method,\r\n path: url.pathname,\r\n userAgent: ua,\r\n ip: ip\r\n }, async () => {\r\n try {\r\n const response = await handler(req, context);\r\n const status = response?.status || 200;\r\n \r\n client.endTrace(status, { route: normalizePath(url.pathname) });\r\n return response;\r\n } catch (err: any) {\r\n client.endTrace(500, { route: normalizePath(url.pathname) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (API Routes) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const path = req.url ? req.url.split('?')[0] : '/';\r\n \r\n // 1. Run in Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n userAgent: req.headers['user-agent'],\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n }, async () => {\r\n \r\n // 2. Hook Response\r\n const done = () => {\r\n client.endTrace(res.statusCode || 200, { route: normalizePath(path) });\r\n };\r\n \r\n res.once('finish', done);\r\n res.once('close', done); // Fallback if finish doesn't fire\r\n\r\n // 3. Execute\r\n try {\r\n return await handler(req, res);\r\n } catch (e) {\r\n // Next.js Pages router usually handles errors internally, \r\n // but we ensure we catch sync errors here\r\n throw e;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/types';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};","import { client } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\nimport { SenzorOptions } from './core/types';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };"],"mappings":"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","names":["Transport","config","trace","batch","err","import_async_hooks","storage","Context","trace","fn","span","store","import_crypto","import_http","import_https","import_url","shimmer","module","methodName","wrapper","original","instrumentHttp","ingestUrl","ingestHost","requestWrapper","args","options","urlStr","protocol","host","path","trace","Context","method","startTime","spanStartAbs","req","res","duration","err","http","https","instrumentMongo","Collection","method","original","args","trace","Context","startTime","spanStartAbs","collectionName","endSpan","err","duration","result","res","instrumentPg","pg","originalQuery","args","trace","Context","startTime","spanStartAbs","sql","result","res","duration","SenzorClient","options","endpoint","Transport","instrumentHttp","instrumentMongo","instrumentPg","data","next","trace","Context","status","extraData","duration","payload","name","type","startTime","spanStartAbs","meta","client","expressMiddleware","req","res","next","client","route","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","req","path","client","response","status","getRoute","err","wrapNextRoute","handler","req","context","url","method","ua","ip","client","response","status","normalizePath","err","wrapNextPages","res","path","done","e","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var d=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var z=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var v=(e,t)=>{for(var a in t)d(e,a,{get:t[a],enumerable:!0})},A=(e,t,a,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of z(t))!S.call(e,o)&&o!==a&&d(e,o,{get:()=>t[o],enumerable:!(r=x(t,o))||r.enumerable});return e};var k=e=>A(d({},"__esModule",{value:!0}),e);var O={};v(O,{Senzor:()=>w,default:()=>F});module.exports=k(O);var p=class{constructor(t){this.queue=[];this.timer=null;this.config=t,typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),this.config.flushInterval),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=this.config.batchSize&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(a){this.config.debug&&console.error("[Senzor] Ingestion Error:",a)}}};var h=class{constructor(){this.transport=null;this.options=null}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options={endpoint:"https://api.senzor.dev/api/ingest/apm",batchSize:100,flushInterval:1e4,debug:!1,...t},this.transport=new p({apiKey:this.options.apiKey,endpoint:this.options.endpoint,batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,debug:this.options.debug||!1}),this.options.debug&&console.log("[Senzor] Initialized")}track(t){this.transport&&this.transport.add({...t,timestamp:new Date().toISOString()})}async flush(){this.transport&&await this.transport.flush()}},s=new h;var l=()=>(e,t,a)=>{let r=performance.now();t.once("finish",()=>{try{let o=performance.now()-r,n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",s.track({method:e.method,route:n,path:e.originalUrl||e.url,status:t.statusCode,duration:o,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]})}catch{}}),a()};var c=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],f=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:c(t);var m=e=>async t=>{let a=performance.now(),r=200,o=null;try{let n=await e(t);return t.node?.res?.statusCode&&(r=t.node.res.statusCode),n}catch(n){throw o=n,r=n.statusCode||n.status||500,n}finally{let n=performance.now()-a,i=t.node.req,u=i.originalUrl||i.url||"/";s.track({method:i.method||"GET",route:f(t,u),path:u,status:r,duration:n,ip:I(i),userAgent:i.headers["user-agent"]})}},I=e=>e.headers["x-forwarded-for"]||e.socket?.remoteAddress;var g=e=>async(t,a)=>{let r=performance.now(),o=200;try{let n=await e(t,a);return n&&typeof n.status=="number"&&(o=n.status),n}catch(n){throw o=500,n}finally{let n=performance.now()-r,i=t.url?new URL(t.url):{pathname:"/"},u=c(i.pathname);s.track({method:t.method||"GET",route:u,path:i.pathname,status:o,duration:n,userAgent:t.headers.get?t.headers.get("user-agent"):void 0,ip:t.headers.get?t.headers.get("x-forwarded-for"):void 0})}},y=e=>async(t,a)=>{let r=performance.now(),o=()=>{let n=performance.now()-r,i=t.url||"/";s.track({method:t.method||"GET",route:c(i.split("?")[0]),path:i,status:a.statusCode||200,duration:n,ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress,userAgent:t.headers["user-agent"]})};return a.once("finish",o),a.once("close",o),e(t,a)};var b=(e,t,a)=>{t&&t.apiKey&&s.init(t),e.addHook("onRequest",(r,o,n)=>{r.senzorStart=performance.now(),n()}),e.addHook("onResponse",(r,o,n)=>{let i=performance.now()-(r.senzorStart||performance.now()),u=r.routeOptions?.url||r.routerPath;s.track({method:r.method,route:u||"UNKNOWN",path:r.raw.url||r.url,status:o.statusCode,duration:i,ip:r.ip,userAgent:r.headers["user-agent"]}),n()}),a()};var w={init:e=>s.init(e),flush:()=>s.flush(),track:s.track.bind(s),requestHandler:l,wrapNextRoute:g,wrapNextPages:y,wrapH3:m,fastifyPlugin:b},F=w;0&&(module.exports={Senzor});
1
+ "use strict";var D=Object.create;var g=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,j=Object.prototype.hasOwnProperty;var J=(e,t)=>{for(var r in t)g(e,r,{get:t[r],enumerable:!0})},v=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of W(t))!j.call(e,o)&&o!==r&&g(e,o,{get:()=>t[o],enumerable:!(n=G(t,o))||n.enumerable});return e};var F=(e,t,r)=>(r=e!=null?D(L(e)):{},v(t||!e||!e.__esModule?g(r,"default",{value:e,enumerable:!0}):r,e)),Q=e=>v(g({},"__esModule",{value:!0}),e);var V={};J(V,{Senzor:()=>M,default:()=>B});module.exports=Q(V);var w=class{constructor(t){this.config=t;this.queue=[];this.timer=null;typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),t.flushInterval||1e4),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=(this.config.batchSize||100)&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint||"https://api.senzor.dev/api/ingest/apm",{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(r){this.config.debug&&console.error("[Senzor] Ingestion Error:",r)}}};var P=require("async_hooks"),x=new P.AsyncLocalStorage,u={run:(e,t)=>x.run(e,t),current:()=>x.getStore(),addSpan:e=>{let t=x.getStore();t&&t.spans.push(e)}};var z=require("crypto");var b=F(require("http")),A=F(require("https")),S=require("url");var T=(e,t,r)=>{if(!e[t])return;let n=e[t];e[t]=r(n)},I=e=>{let t=new S.URL(e).hostname,r=n=>function(...o){let a={},c="";if(typeof o[0]=="string"||o[0]instanceof S.URL)c=o[0].toString(),a=o[1]||{};else{a=o[0]||{};let d=a.protocol||"http:",l=a.hostname||a.host||"localhost",K=a.path||"/";c=`${d}//${l}${K}`}if(c.includes(t)||a.hostname&&a.hostname.includes(t))return n.apply(this,o);let i=u.current();if(!i)return n.apply(this,o);let m=(a.method||"GET").toUpperCase(),h=performance.now()-i.startTime,f=performance.now(),p=n.apply(this,o);return p.on("response",d=>{d.on("end",()=>{let l=performance.now()-f;u.addSpan({name:`${m} ${new S.URL(c).hostname}`,type:"http",startTime:h,duration:l,status:d.statusCode,meta:{url:c,method:m}})})}),p.on("error",d=>{let l=performance.now()-f;u.addSpan({name:`${m} ${c}`,type:"http",startTime:h,duration:l,status:500,meta:{error:d.message}})}),p};T(b.default,"request",r),T(b.default,"get",r),T(A.default,"request",r),T(A.default,"get",r)};var N=()=>{try{let t=require("mongodb").Collection;["find","findOne","insertOne","insertMany","updateOne","updateMany","deleteOne","deleteMany","aggregate","countDocuments"].forEach(n=>{if(!t.prototype[n])return;let o=t.prototype[n];t.prototype[n]=function(...a){let c=u.current();if(!c)return o.apply(this,a);let i=performance.now()-c.startTime,m=performance.now(),h=this.collectionName,f=p=>{let d=performance.now()-m;u.addSpan({name:`MongoDB ${n} (${h})`,type:"db",startTime:i,duration:d,status:p?500:0,meta:{collection:h,operation:n,error:p?p.message:void 0}})};try{let p=o.apply(this,a);return p&&typeof p.then=="function"?p.then(d=>(f(),d),d=>{throw f(d),d}):(f(),p)}catch(p){throw f(p),p}}})}catch{}};var R=()=>{try{let e=require("pg"),t=e.Client.prototype.query;e.Client.prototype.query=function(...r){let n=u.current();if(!n)return t.apply(this,r);let o=performance.now()-n.startTime,a=performance.now(),c=typeof r[0]=="string"?r[0]:r[0].text,i=t.apply(this,r);return i&&typeof i.then=="function"?i.then(m=>{let h=performance.now()-a;return u.addSpan({name:"Postgres Query",type:"db",startTime:o,duration:h,meta:{query:c}}),m}):i}}catch{}};var C=class{constructor(){this.transport=null;this.options=null;this.isInstrumented=!1}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options=t;let r=t.endpoint||"https://api.senzor.dev/api/ingest/apm";if(this.transport=new w({...t,endpoint:r}),!this.isInstrumented){try{I(r)}catch{}try{N()}catch{}try{R()}catch{}this.isInstrumented=!0,t.debug&&console.log("[Senzor] Auto-instrumentation enabled")}t.debug&&console.log("[Senzor] Initialized")}startTrace(t,r){if(!this.transport)return r();let n={id:(0,z.randomUUID)(),startTime:performance.now(),data:t,spans:[]};return u.run(n,r)}endTrace(t,r={}){let n=u.current();if(!n||!this.transport)return;let o=performance.now()-n.startTime,a={traceId:n.id,...n.data,...r,status:t,duration:o,spans:n.spans,timestamp:new Date().toISOString()};this.transport.add(a)}track(t){if(!this.transport)return;let r={traceId:(0,z.randomUUID)(),...t,spans:[],timestamp:new Date().toISOString()};this.transport.add(r)}startSpan(t,r="custom"){let n=u.current();if(!n)return{end:()=>{}};let o=performance.now()-n.startTime,a=performance.now();return{end:(c,i)=>{let m=performance.now()-a;u.addSpan({name:t,type:r,startTime:o,duration:m,status:i,meta:c})}}}async flush(){this.transport&&await this.transport.flush()}},s=new C;var U=()=>(e,t,r)=>{s.startTrace({method:e.method,path:e.originalUrl||e.url,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]},()=>{t.once("finish",()=>{try{let n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",s.endTrace(t.statusCode,{route:n})}catch{}}),r()})};var y=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],O=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:y(t);var $=e=>t=>{let r=t.node.req,n=r.originalUrl||r.url||"/";return s.startTrace({method:r.method||"GET",path:n,ip:r.headers["x-forwarded-for"]||r.socket?.remoteAddress,userAgent:r.headers["user-agent"]},async()=>{try{let o=await e(t),a=200;return t.node.res.statusCode&&(a=t.node.res.statusCode),o&&o.statusCode&&(a=o.statusCode),s.endTrace(a,{route:O(t,n)}),o}catch(o){let a=o.statusCode||o.status||500;throw s.endTrace(a,{route:O(t,n)}),o}})};var E=e=>async(t,r)=>{let n=t.url?new URL(t.url):{pathname:"/"},o=t.method||"GET",a=t.headers.get?t.headers.get("user-agent"):void 0,c=t.headers.get?t.headers.get("x-forwarded-for"):void 0;return s.startTrace({method:o,path:n.pathname,userAgent:a,ip:c},async()=>{try{let i=await e(t,r),m=i?.status||200;return s.endTrace(m,{route:y(n.pathname)}),i}catch(i){throw s.endTrace(500,{route:y(n.pathname)}),i}})},H=e=>async(t,r)=>{let n=t.url?t.url.split("?")[0]:"/";return s.startTrace({method:t.method||"GET",path:n,userAgent:t.headers["user-agent"],ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress},async()=>{let o=()=>{s.endTrace(r.statusCode||200,{route:y(n)})};r.once("finish",o),r.once("close",o);try{return await e(t,r)}catch(a){throw a}})};var k=(e,t,r)=>{t&&t.apiKey&&s.init(t),e.addHook("onRequest",(n,o,a)=>{n.senzorStart=performance.now(),a()}),e.addHook("onResponse",(n,o,a)=>{let c=performance.now()-(n.senzorStart||performance.now()),i=n.routeOptions?.url||n.routerPath;s.track({method:n.method,route:i||"UNKNOWN",path:n.raw.url||n.url,status:o.statusCode,duration:c,ip:n.ip,userAgent:n.headers["user-agent"]}),a()}),r()};var M={init:e=>s.init(e),flush:()=>s.flush(),requestHandler:U,wrapNextRoute:E,wrapNextPages:H,wrapH3:$,fastifyPlugin:k},B=M;0&&(module.exports={Senzor});
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/transport.ts","../src/core/client.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts"],"sourcesContent":["import { client, SenzorOptions } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n track: client.track.bind(client),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };","export interface TransportConfig {\r\n apiKey: string;\r\n endpoint: string;\r\n batchSize: number;\r\n flushInterval: number;\r\n debug: boolean;\r\n}\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private config: TransportConfig;\r\n private timer: any = null;\r\n\r\n constructor(config: TransportConfig) {\r\n this.config = config;\r\n // Only start timer in non-serverless environments (long running processes)\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), this.config.flushInterval);\r\n // Unref if in Node.js to allow process exit\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref();\r\n }\r\n }\r\n }\r\n\r\n public add(event: any) {\r\n this.queue.push(event);\r\n if (this.queue.length >= this.config.batchSize) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use native fetch (Node 18+, Edge, Browser)\r\n await fetch(this.config.endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n // keepalive ensures connection stays open even if function ends (vital for APM)\r\n keepalive: true,\r\n });\r\n\r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // We drop data on failure to prevent memory leaks in the app\r\n }\r\n }\r\n}","import { Transport } from './transport';\r\n\r\nexport interface SenzorOptions {\r\n apiKey: string;\r\n endpoint?: string;\r\n batchSize?: number;\r\n flushInterval?: number;\r\n debug?: boolean;\r\n}\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n\r\n this.options = {\r\n endpoint: 'https://api.senzor.dev/api/ingest/apm',\r\n batchSize: 100,\r\n flushInterval: 10000,\r\n debug: false,\r\n ...options\r\n };\r\n\r\n this.transport = new Transport({\r\n apiKey: this.options.apiKey,\r\n endpoint: this.options.endpoint!,\r\n batchSize: this.options.batchSize!,\r\n flushInterval: this.options.flushInterval!,\r\n debug: this.options.debug || false\r\n });\r\n\r\n if (this.options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n // --- Manual Tracking (For any framework) ---\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n\r\n this.transport.add({\r\n ...data,\r\n timestamp: new Date().toISOString()\r\n });\r\n }\r\n\r\n // --- Force Flush (For Serverless/Lambda) ---\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // Start Timer (High Precision)\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n res.once('finish', () => {\r\n try {\r\n const duration = performance.now() - start;\r\n\r\n // Route Normalization Logic\r\n // Express stores route info in req.route\r\n let route = 'UNKNOWN';\r\n\r\n if (req.route && req.route.path) {\r\n // Combined baseUrl (if mounted on /api) + path (/:id)\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n // Fallback for unmapped routes or static files\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.track({\r\n method: req.method,\r\n route: route,\r\n path: req.originalUrl || req.url,\r\n status: res.statusCode,\r\n duration: duration,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Types for H3 (Mocked to avoid heavy peer dependencies)\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return async (event: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n let error: any = null;\r\n\r\n try {\r\n const response = await handler(event);\r\n // Try to determine status from response or event\r\n if (event.node?.res?.statusCode) {\r\n status = event.node.res.statusCode;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n error = err;\r\n status = err.statusCode || err.status || 500;\r\n throw err;\r\n } finally {\r\n // Non-blocking collection\r\n const duration = performance.now() - start;\r\n const req = event.node.req;\r\n\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: getRoute(event, path), // H3 often attaches context to event\r\n path: path,\r\n status: status,\r\n duration: duration,\r\n ip: getIp(req),\r\n userAgent: req.headers['user-agent'],\r\n });\r\n\r\n // If serverless, we might need to await flush, but for general H3 usage (Node preset)\r\n // we assume the process stays alive or uses ctx.waitUntil\r\n }\r\n };\r\n};\r\n\r\nconst getIp = (req: any) => {\r\n return req.headers['x-forwarded-for'] || req.socket?.remoteAddress;\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (GET, POST, etc.) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n\r\n try {\r\n const response = await handler(req, context);\r\n if (response && typeof response.status === 'number') {\r\n status = response.status;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n status = 500;\r\n throw err;\r\n } finally {\r\n const duration = performance.now() - start;\r\n\r\n // App Router Request is a standard Web Request object\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n\r\n // In App Router, we often rely on file-system path, but context.params helps\r\n // For now, robust heuristic normalization is best\r\n const route = normalizePath(url.pathname);\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: route,\r\n path: url.pathname,\r\n status: status,\r\n duration: duration,\r\n userAgent: req.headers.get ? req.headers.get('user-agent') : undefined,\r\n // IP extraction from Web Request is tricky without headers\r\n ip: req.headers.get ? req.headers.get('x-forwarded-for') : undefined,\r\n });\r\n\r\n // Vercel/Serverless Flush Safety\r\n // We purposefully do NOT await flush here to avoid latency.\r\n // Ideally user configures transport to sync flush or uses waitUntil\r\n }\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (req, res) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n const done = () => {\r\n const duration = performance.now() - start;\r\n const path = req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: normalizePath(path.split('?')[0]),\r\n path: path,\r\n status: res.statusCode || 200,\r\n duration: duration,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent']\r\n });\r\n };\r\n\r\n res.once('finish', done);\r\n // Handle error case if next/error isn't used\r\n res.once('close', done);\r\n\r\n return handler(req, res);\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/client';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};"],"mappings":"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","names":["index_exports","__export","Senzor","index_default","__toCommonJS","Transport","config","event","batch","err","SenzorClient","options","Transport","data","client","expressMiddleware","req","res","next","start","duration","route","client","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","start","status","error","response","err","duration","req","path","client","getRoute","getIp","wrapNextRoute","handler","req","context","start","status","response","err","duration","url","route","normalizePath","client","wrapNextPages","res","done","path","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/transport.ts","../src/core/context.ts","../src/core/client.ts","../src/instrumentation/http.ts","../src/instrumentation/mongo.ts","../src/instrumentation/pg.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts"],"sourcesContent":["import { client } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\nimport { SenzorOptions } from './core/types';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };","import { SenzorOptions } from './types';\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private timer: NodeJS.Timeout | null = null;\r\n\r\n constructor(private config: SenzorOptions) {\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), config.flushInterval || 10000);\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref(); // Don't block process exit\r\n }\r\n }\r\n }\r\n\r\n public add(trace: any) {\r\n this.queue.push(trace);\r\n if (this.queue.length >= (this.config.batchSize || 100)) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use global fetch (Node 18+)\r\n await fetch(this.config.endpoint || 'https://api.senzor.dev/api/ingest/apm', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n keepalive: true,\r\n });\r\n \r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // Dropping data to prevent memory leaks is preferred in APM\r\n }\r\n }\r\n}","import { AsyncLocalStorage } from 'async_hooks';\r\nimport { ActiveTrace } from './types';\r\n\r\n// Storage to hold the current Trace for any async operation\r\nexport const storage = new AsyncLocalStorage<ActiveTrace>();\r\n\r\nexport const Context = {\r\n // Run a function within a trace context\r\n // Updated to be Generic <T> to allow returning values (Promises, etc)\r\n run: <T>(trace: ActiveTrace, fn: () => T): T => {\r\n return storage.run(trace, fn);\r\n },\r\n\r\n // Get current trace (safe)\r\n current: (): ActiveTrace | undefined => {\r\n return storage.getStore();\r\n },\r\n\r\n // Add a span to the current trace\r\n addSpan: (span: any) => {\r\n const store = storage.getStore();\r\n if (store) {\r\n store.spans.push(span);\r\n }\r\n }\r\n};","import { Transport } from './transport';\r\nimport { Context } from './context';\r\nimport { SenzorOptions, ActiveTrace } from './types';\r\nimport { randomUUID } from 'crypto';\r\nimport { instrumentHttp } from '../instrumentation/http';\r\nimport { instrumentMongo } from '../instrumentation/mongo';\r\nimport { instrumentPg } from '../instrumentation/pg';\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n private isInstrumented = false;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n this.options = options;\r\n const endpoint = options.endpoint || 'https://api.senzor.dev/api/ingest/apm';\r\n\r\n this.transport = new Transport({\r\n ...options,\r\n endpoint\r\n });\r\n\r\n if (!this.isInstrumented) {\r\n try { instrumentHttp(endpoint); } catch (e) { }\r\n try { instrumentMongo(); } catch (e) { }\r\n try { instrumentPg(); } catch (e) { }\r\n\r\n this.isInstrumented = true;\r\n if (options.debug) console.log('[Senzor] Auto-instrumentation enabled');\r\n }\r\n\r\n if (options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n public startTrace<T>(data: Partial<ActiveTrace['data']>, next: () => T): T {\r\n if (!this.transport) return next();\r\n\r\n const trace: ActiveTrace = {\r\n id: randomUUID(),\r\n startTime: performance.now(),\r\n data: data,\r\n spans: []\r\n };\r\n\r\n return Context.run(trace, next);\r\n }\r\n\r\n public endTrace(status: number, extraData: any = {}) {\r\n const trace = Context.current();\r\n if (!trace || !this.transport) return;\r\n\r\n const duration = performance.now() - trace.startTime;\r\n\r\n const payload = {\r\n traceId: trace.id,\r\n ...trace.data,\r\n ...extraData,\r\n status,\r\n duration,\r\n spans: trace.spans,\r\n timestamp: new Date().toISOString()\r\n };\r\n\r\n this.transport.add(payload);\r\n }\r\n\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n const payload = {\r\n traceId: randomUUID(),\r\n ...data,\r\n spans: [],\r\n timestamp: new Date().toISOString()\r\n };\r\n this.transport.add(payload);\r\n }\r\n\r\n public startSpan(name: string, type: 'db' | 'http' | 'function' | 'custom' = 'custom') {\r\n const trace = Context.current();\r\n if (!trace) return { end: () => { } };\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n return {\r\n end: (meta?: any, status?: number) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name,\r\n type,\r\n startTime,\r\n duration,\r\n status,\r\n meta\r\n });\r\n }\r\n };\r\n }\r\n\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import http from 'http';\r\nimport https from 'https';\r\nimport { URL } from 'url';\r\nimport { Context } from '../core/context';\r\n\r\n// Helper to safely wrap modules\r\nconst shimmer = (module: any, methodName: string, wrapper: (original: Function) => Function) => {\r\n if (!module[methodName]) return;\r\n const original = module[methodName];\r\n module[methodName] = wrapper(original);\r\n};\r\n\r\nexport const instrumentHttp = (ingestUrl: string) => {\r\n const ingestHost = new URL(ingestUrl).hostname;\r\n\r\n const requestWrapper = (original: Function) => {\r\n return function (this: any, ...args: any[]) {\r\n // 1. Parse Arguments to get URL\r\n let options: any = {};\r\n let urlStr = '';\r\n\r\n if (typeof args[0] === 'string' || args[0] instanceof URL) {\r\n urlStr = args[0].toString();\r\n options = args[1] || {};\r\n } else {\r\n options = args[0] || {};\r\n const protocol = options.protocol || 'http:';\r\n const host = options.hostname || options.host || 'localhost';\r\n const path = options.path || '/';\r\n urlStr = `${protocol}//${host}${path}`;\r\n }\r\n\r\n // 2. SAFETY GUARD: Ignore calls to Senzor Ingest API (Prevent Infinite Loop)\r\n if (urlStr.includes(ingestHost) || (options.hostname && options.hostname.includes(ingestHost))) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 3. Check if we are inside an Active Trace\r\n // If we are not handling a user request, don't trace background http calls\r\n const trace = Context.current();\r\n if (!trace) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 4. Start Span\r\n const method = (options.method || 'GET').toUpperCase();\r\n const startTime = performance.now() - trace.startTime; // Relative to trace start\r\n const spanStartAbs = performance.now();\r\n\r\n // 5. Execute Request\r\n const req = original.apply(this, args);\r\n\r\n // 6. Hook into Response/Error\r\n req.on('response', (res: any) => {\r\n // Wait for end of stream to calculate full duration (TTFB + Download)\r\n res.on('end', () => {\r\n const duration = performance.now() - spanStartAbs;\r\n \r\n Context.addSpan({\r\n name: `${method} ${new URL(urlStr).hostname}`, // e.g. \"GET api.stripe.com\"\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: res.statusCode,\r\n meta: {\r\n url: urlStr,\r\n method: method,\r\n }\r\n });\r\n });\r\n });\r\n\r\n req.on('error', (err: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `${method} ${urlStr}`,\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: 500, // Client Error\r\n meta: { error: err.message }\r\n });\r\n });\r\n\r\n return req;\r\n };\r\n };\r\n\r\n // Apply patches\r\n shimmer(http, 'request', requestWrapper);\r\n shimmer(http, 'get', requestWrapper);\r\n shimmer(https, 'request', requestWrapper);\r\n shimmer(https, 'get', requestWrapper);\r\n};","import { Context } from '../core/context';\r\n\r\nexport const instrumentMongo = () => {\r\n try {\r\n const mongodb = require('mongodb');\r\n const Collection = mongodb.Collection;\r\n\r\n const methods = [\r\n 'find',\r\n 'findOne',\r\n 'insertOne',\r\n 'insertMany',\r\n 'updateOne',\r\n 'updateMany',\r\n 'deleteOne',\r\n 'deleteMany',\r\n 'aggregate',\r\n 'countDocuments'\r\n ];\r\n\r\n methods.forEach((method) => {\r\n if (!Collection.prototype[method]) return;\r\n\r\n const original = Collection.prototype[method];\r\n\r\n Collection.prototype[method] = function (...args: any[]) {\r\n const trace = Context.current();\r\n // If not inside a tracked request, just run normally\r\n if (!trace) return original.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n const collectionName = this.collectionName;\r\n\r\n // Helper to finish span\r\n const endSpan = (err?: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `MongoDB ${method} (${collectionName})`,\r\n type: 'db',\r\n startTime,\r\n duration,\r\n status: err ? 500 : 0,\r\n meta: {\r\n collection: collectionName,\r\n operation: method,\r\n error: err ? err.message : undefined\r\n }\r\n });\r\n };\r\n\r\n try {\r\n const result = original.apply(this, args);\r\n\r\n // Handle Promise/Cursor\r\n if (result && typeof result.then === 'function') {\r\n return result.then(\r\n (res: any) => { endSpan(); return res; },\r\n (err: any) => { endSpan(err); throw err; }\r\n );\r\n }\r\n // Handle FindCursor (it doesn't execute immediately, but for APM visuals we mark start/return)\r\n // Ideally we'd wrap toArray() but that's complex. For 'find', we just mark the creation.\r\n endSpan();\r\n return result;\r\n\r\n } catch (err: any) {\r\n endSpan(err);\r\n throw err;\r\n }\r\n };\r\n });\r\n\r\n } catch (e) {\r\n // User doesn't use mongodb, ignore\r\n }\r\n};","import { Context } from '../core/context';\r\n\r\n// Simple shim for 'pg' library\r\nexport const instrumentPg = () => {\r\n try {\r\n // Try to require pg (it might not be installed by user)\r\n const pg = require('pg');\r\n const originalQuery = pg.Client.prototype.query;\r\n\r\n pg.Client.prototype.query = function (...args: any[]) {\r\n const trace = Context.current();\r\n if (!trace) return originalQuery.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n // Extract SQL (first arg usually string or config object)\r\n const sql = typeof args[0] === 'string' ? args[0] : args[0].text;\r\n\r\n // Wrap callback if present, or handle Promise\r\n const result = originalQuery.apply(this, args);\r\n\r\n if (result && typeof result.then === 'function') {\r\n return result.then((res: any) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: 'Postgres Query',\r\n type: 'db',\r\n startTime,\r\n duration,\r\n meta: { query: sql }\r\n });\r\n return res;\r\n });\r\n }\r\n return result;\r\n };\r\n } catch (e) {\r\n // User doesn't use pg, ignore\r\n }\r\n};","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // We MUST use startTrace to enable Auto-Instrumentation for this request\r\n client.startTrace({\r\n method: req.method,\r\n path: req.originalUrl || req.url,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, () => {\r\n\r\n res.once('finish', () => {\r\n try {\r\n let route = 'UNKNOWN';\r\n if (req.route && req.route.path) {\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.endTrace(res.statusCode, { route });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n });\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Minimal types for H3 to avoid peer-deps\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return (event: any) => {\r\n const req = event.node.req;\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n // Start Trace Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, async () => {\r\n try {\r\n const response = await handler(event);\r\n\r\n // H3/Nitro response status\r\n let status = 200;\r\n if (event.node.res.statusCode) status = event.node.res.statusCode;\r\n // Check if response is an error object\r\n if (response && response.statusCode) status = response.statusCode;\r\n\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n return response;\r\n } catch (err: any) {\r\n const status = err.statusCode || err.status || 500;\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (Route Handlers) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n // 1. Extract Info\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n const method = req.method || 'GET';\r\n const ua = req.headers.get ? req.headers.get('user-agent') : undefined;\r\n const ip = req.headers.get ? req.headers.get('x-forwarded-for') : undefined;\r\n\r\n // 2. Run in Context\r\n return client.startTrace({\r\n method,\r\n path: url.pathname,\r\n userAgent: ua,\r\n ip: ip\r\n }, async () => {\r\n try {\r\n const response = await handler(req, context);\r\n const status = response?.status || 200;\r\n \r\n client.endTrace(status, { route: normalizePath(url.pathname) });\r\n return response;\r\n } catch (err: any) {\r\n client.endTrace(500, { route: normalizePath(url.pathname) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (API Routes) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const path = req.url ? req.url.split('?')[0] : '/';\r\n \r\n // 1. Run in Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n userAgent: req.headers['user-agent'],\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n }, async () => {\r\n \r\n // 2. Hook Response\r\n const done = () => {\r\n client.endTrace(res.statusCode || 200, { route: normalizePath(path) });\r\n };\r\n \r\n res.once('finish', done);\r\n res.once('close', done); // Fallback if finish doesn't fire\r\n\r\n // 3. Execute\r\n try {\r\n return await handler(req, res);\r\n } catch (e) {\r\n // Next.js Pages router usually handles errors internally, \r\n // but we ensure we catch sync errors here\r\n throw e;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/types';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};"],"mappings":"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","names":["index_exports","__export","Senzor","index_default","__toCommonJS","Transport","config","trace","batch","err","import_async_hooks","storage","Context","trace","fn","span","store","import_crypto","import_http","import_https","import_url","shimmer","module","methodName","wrapper","original","instrumentHttp","ingestUrl","ingestHost","requestWrapper","args","options","urlStr","protocol","host","path","trace","Context","method","startTime","spanStartAbs","req","res","duration","err","http","https","instrumentMongo","Collection","method","original","args","trace","Context","startTime","spanStartAbs","collectionName","endSpan","err","duration","result","res","instrumentPg","pg","originalQuery","args","trace","Context","startTime","spanStartAbs","sql","result","res","duration","SenzorClient","options","endpoint","Transport","instrumentHttp","instrumentMongo","instrumentPg","data","next","trace","Context","status","extraData","duration","payload","name","type","startTime","spanStartAbs","meta","client","expressMiddleware","req","res","next","client","route","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","req","path","client","response","status","getRoute","err","wrapNextRoute","handler","req","context","url","method","ua","ip","client","response","status","normalizePath","err","wrapNextPages","res","path","done","e","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var p=class{constructor(t){this.queue=[];this.timer=null;this.config=t,typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),this.config.flushInterval),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=this.config.batchSize&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint,{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(a){this.config.debug&&console.error("[Senzor] Ingestion Error:",a)}}};var d=class{constructor(){this.transport=null;this.options=null}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options={endpoint:"https://api.senzor.dev/api/ingest/apm",batchSize:100,flushInterval:1e4,debug:!1,...t},this.transport=new p({apiKey:this.options.apiKey,endpoint:this.options.endpoint,batchSize:this.options.batchSize,flushInterval:this.options.flushInterval,debug:this.options.debug||!1}),this.options.debug&&console.log("[Senzor] Initialized")}track(t){this.transport&&this.transport.add({...t,timestamp:new Date().toISOString()})}async flush(){this.transport&&await this.transport.flush()}},o=new d;var h=()=>(e,t,a)=>{let r=performance.now();t.once("finish",()=>{try{let s=performance.now()-r,n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",o.track({method:e.method,route:n,path:e.originalUrl||e.url,status:t.statusCode,duration:s,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]})}catch{}}),a()};var c=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],l=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:c(t);var f=e=>async t=>{let a=performance.now(),r=200,s=null;try{let n=await e(t);return t.node?.res?.statusCode&&(r=t.node.res.statusCode),n}catch(n){throw s=n,r=n.statusCode||n.status||500,n}finally{let n=performance.now()-a,i=t.node.req,u=i.originalUrl||i.url||"/";o.track({method:i.method||"GET",route:l(t,u),path:u,status:r,duration:n,ip:b(i),userAgent:i.headers["user-agent"]})}},b=e=>e.headers["x-forwarded-for"]||e.socket?.remoteAddress;var m=e=>async(t,a)=>{let r=performance.now(),s=200;try{let n=await e(t,a);return n&&typeof n.status=="number"&&(s=n.status),n}catch(n){throw s=500,n}finally{let n=performance.now()-r,i=t.url?new URL(t.url):{pathname:"/"},u=c(i.pathname);o.track({method:t.method||"GET",route:u,path:i.pathname,status:s,duration:n,userAgent:t.headers.get?t.headers.get("user-agent"):void 0,ip:t.headers.get?t.headers.get("x-forwarded-for"):void 0})}},g=e=>async(t,a)=>{let r=performance.now(),s=()=>{let n=performance.now()-r,i=t.url||"/";o.track({method:t.method||"GET",route:c(i.split("?")[0]),path:i,status:a.statusCode||200,duration:n,ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress,userAgent:t.headers["user-agent"]})};return a.once("finish",s),a.once("close",s),e(t,a)};var y=(e,t,a)=>{t&&t.apiKey&&o.init(t),e.addHook("onRequest",(r,s,n)=>{r.senzorStart=performance.now(),n()}),e.addHook("onResponse",(r,s,n)=>{let i=performance.now()-(r.senzorStart||performance.now()),u=r.routeOptions?.url||r.routerPath;o.track({method:r.method,route:u||"UNKNOWN",path:r.raw.url||r.url,status:s.statusCode,duration:i,ip:r.ip,userAgent:r.headers["user-agent"]}),n()}),a()};var w={init:e=>o.init(e),flush:()=>o.flush(),track:o.track.bind(o),requestHandler:h,wrapNextRoute:m,wrapNextPages:g,wrapH3:f,fastifyPlugin:y},j=w;export{w as Senzor,j as default};
1
+ var A=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var g=class{constructor(t){this.config=t;this.queue=[];this.timer=null;typeof setInterval<"u"&&(this.timer=setInterval(()=>this.flush(),t.flushInterval||1e4),this.timer&&typeof this.timer.unref=="function"&&this.timer.unref())}add(t){this.queue.push(t),this.queue.length>=(this.config.batchSize||100)&&this.flush()}async flush(){if(this.queue.length===0)return;let t=[...this.queue];this.queue=[];try{await fetch(this.config.endpoint||"https://api.senzor.dev/api/ingest/apm",{method:"POST",headers:{"Content-Type":"application/json","x-service-api-key":this.config.apiKey},body:JSON.stringify(t),keepalive:!0}),this.config.debug&&console.log(`[Senzor] Flushed ${t.length} traces`)}catch(r){this.config.debug&&console.error("[Senzor] Ingestion Error:",r)}}};import{AsyncLocalStorage as H}from"async_hooks";var T=new H,u={run:(e,t)=>T.run(e,t),current:()=>T.getStore(),addSpan:e=>{let t=T.getStore();t&&t.spans.push(e)}};import{randomUUID as P}from"crypto";import z from"http";import C from"https";import{URL as S}from"url";var w=(e,t,r)=>{if(!e[t])return;let n=e[t];e[t]=r(n)},O=e=>{let t=new S(e).hostname,r=n=>function(...o){let a={},c="";if(typeof o[0]=="string"||o[0]instanceof S)c=o[0].toString(),a=o[1]||{};else{a=o[0]||{};let d=a.protocol||"http:",l=a.hostname||a.host||"localhost",E=a.path||"/";c=`${d}//${l}${E}`}if(c.includes(t)||a.hostname&&a.hostname.includes(t))return n.apply(this,o);let i=u.current();if(!i)return n.apply(this,o);let m=(a.method||"GET").toUpperCase(),h=performance.now()-i.startTime,f=performance.now(),p=n.apply(this,o);return p.on("response",d=>{d.on("end",()=>{let l=performance.now()-f;u.addSpan({name:`${m} ${new S(c).hostname}`,type:"http",startTime:h,duration:l,status:d.statusCode,meta:{url:c,method:m}})})}),p.on("error",d=>{let l=performance.now()-f;u.addSpan({name:`${m} ${c}`,type:"http",startTime:h,duration:l,status:500,meta:{error:d.message}})}),p};w(z,"request",r),w(z,"get",r),w(C,"request",r),w(C,"get",r)};var v=()=>{try{let t=A("mongodb").Collection;["find","findOne","insertOne","insertMany","updateOne","updateMany","deleteOne","deleteMany","aggregate","countDocuments"].forEach(n=>{if(!t.prototype[n])return;let o=t.prototype[n];t.prototype[n]=function(...a){let c=u.current();if(!c)return o.apply(this,a);let i=performance.now()-c.startTime,m=performance.now(),h=this.collectionName,f=p=>{let d=performance.now()-m;u.addSpan({name:`MongoDB ${n} (${h})`,type:"db",startTime:i,duration:d,status:p?500:0,meta:{collection:h,operation:n,error:p?p.message:void 0}})};try{let p=o.apply(this,a);return p&&typeof p.then=="function"?p.then(d=>(f(),d),d=>{throw f(d),d}):(f(),p)}catch(p){throw f(p),p}}})}catch{}};var F=()=>{try{let e=A("pg"),t=e.Client.prototype.query;e.Client.prototype.query=function(...r){let n=u.current();if(!n)return t.apply(this,r);let o=performance.now()-n.startTime,a=performance.now(),c=typeof r[0]=="string"?r[0]:r[0].text,i=t.apply(this,r);return i&&typeof i.then=="function"?i.then(m=>{let h=performance.now()-a;return u.addSpan({name:"Postgres Query",type:"db",startTime:o,duration:h,meta:{query:c}}),m}):i}}catch{}};var x=class{constructor(){this.transport=null;this.options=null;this.isInstrumented=!1}init(t){if(!t.apiKey){console.warn("[Senzor] API Key missing. SDK disabled.");return}this.options=t;let r=t.endpoint||"https://api.senzor.dev/api/ingest/apm";if(this.transport=new g({...t,endpoint:r}),!this.isInstrumented){try{O(r)}catch{}try{v()}catch{}try{F()}catch{}this.isInstrumented=!0,t.debug&&console.log("[Senzor] Auto-instrumentation enabled")}t.debug&&console.log("[Senzor] Initialized")}startTrace(t,r){if(!this.transport)return r();let n={id:P(),startTime:performance.now(),data:t,spans:[]};return u.run(n,r)}endTrace(t,r={}){let n=u.current();if(!n||!this.transport)return;let o=performance.now()-n.startTime,a={traceId:n.id,...n.data,...r,status:t,duration:o,spans:n.spans,timestamp:new Date().toISOString()};this.transport.add(a)}track(t){if(!this.transport)return;let r={traceId:P(),...t,spans:[],timestamp:new Date().toISOString()};this.transport.add(r)}startSpan(t,r="custom"){let n=u.current();if(!n)return{end:()=>{}};let o=performance.now()-n.startTime,a=performance.now();return{end:(c,i)=>{let m=performance.now()-a;u.addSpan({name:t,type:r,startTime:o,duration:m,status:i,meta:c})}}}async flush(){this.transport&&await this.transport.flush()}},s=new x;var I=()=>(e,t,r)=>{s.startTrace({method:e.method,path:e.originalUrl||e.url,ip:e.ip||e.socket?.remoteAddress,userAgent:e.headers["user-agent"]},()=>{t.once("finish",()=>{try{let n="UNKNOWN";e.route&&e.route.path?n=(e.baseUrl||"")+e.route.path:t.statusCode===404?n="Not Found":n=e.path||"Wildcard",s.endTrace(t.statusCode,{route:n})}catch{}}),r()})};var y=e=>!e||e==="/"?"/":e.replace(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,":uuid").replace(/[0-9a-fA-F]{24}/g,":objectId").replace(/\/(\d+)(?=\/|$)/g,"/:id").split("?")[0],b=(e,t)=>e.route&&e.route.path?(e.baseUrl||"")+e.route.path:e.context&&e.context.matchedRoute?e.context.matchedRoute.path:e.routerPath?e.routerPath:y(t);var N=e=>t=>{let r=t.node.req,n=r.originalUrl||r.url||"/";return s.startTrace({method:r.method||"GET",path:n,ip:r.headers["x-forwarded-for"]||r.socket?.remoteAddress,userAgent:r.headers["user-agent"]},async()=>{try{let o=await e(t),a=200;return t.node.res.statusCode&&(a=t.node.res.statusCode),o&&o.statusCode&&(a=o.statusCode),s.endTrace(a,{route:b(t,n)}),o}catch(o){let a=o.statusCode||o.status||500;throw s.endTrace(a,{route:b(t,n)}),o}})};var R=e=>async(t,r)=>{let n=t.url?new URL(t.url):{pathname:"/"},o=t.method||"GET",a=t.headers.get?t.headers.get("user-agent"):void 0,c=t.headers.get?t.headers.get("x-forwarded-for"):void 0;return s.startTrace({method:o,path:n.pathname,userAgent:a,ip:c},async()=>{try{let i=await e(t,r),m=i?.status||200;return s.endTrace(m,{route:y(n.pathname)}),i}catch(i){throw s.endTrace(500,{route:y(n.pathname)}),i}})},U=e=>async(t,r)=>{let n=t.url?t.url.split("?")[0]:"/";return s.startTrace({method:t.method||"GET",path:n,userAgent:t.headers["user-agent"],ip:t.headers["x-forwarded-for"]||t.socket?.remoteAddress},async()=>{let o=()=>{s.endTrace(r.statusCode||200,{route:y(n)})};r.once("finish",o),r.once("close",o);try{return await e(t,r)}catch(a){throw a}})};var $=(e,t,r)=>{t&&t.apiKey&&s.init(t),e.addHook("onRequest",(n,o,a)=>{n.senzorStart=performance.now(),a()}),e.addHook("onResponse",(n,o,a)=>{let c=performance.now()-(n.senzorStart||performance.now()),i=n.routeOptions?.url||n.routerPath;s.track({method:n.method,route:i||"UNKNOWN",path:n.raw.url||n.url,status:o.statusCode,duration:c,ip:n.ip,userAgent:n.headers["user-agent"]}),a()}),r()};var k={init:e=>s.init(e),flush:()=>s.flush(),requestHandler:I,wrapNextRoute:R,wrapNextPages:U,wrapH3:N,fastifyPlugin:$},St=k;export{k as Senzor,St as default};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/transport.ts","../src/core/client.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts","../src/index.ts"],"sourcesContent":["export interface TransportConfig {\r\n apiKey: string;\r\n endpoint: string;\r\n batchSize: number;\r\n flushInterval: number;\r\n debug: boolean;\r\n}\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private config: TransportConfig;\r\n private timer: any = null;\r\n\r\n constructor(config: TransportConfig) {\r\n this.config = config;\r\n // Only start timer in non-serverless environments (long running processes)\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), this.config.flushInterval);\r\n // Unref if in Node.js to allow process exit\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref();\r\n }\r\n }\r\n }\r\n\r\n public add(event: any) {\r\n this.queue.push(event);\r\n if (this.queue.length >= this.config.batchSize) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use native fetch (Node 18+, Edge, Browser)\r\n await fetch(this.config.endpoint, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n // keepalive ensures connection stays open even if function ends (vital for APM)\r\n keepalive: true,\r\n });\r\n\r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // We drop data on failure to prevent memory leaks in the app\r\n }\r\n }\r\n}","import { Transport } from './transport';\r\n\r\nexport interface SenzorOptions {\r\n apiKey: string;\r\n endpoint?: string;\r\n batchSize?: number;\r\n flushInterval?: number;\r\n debug?: boolean;\r\n}\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n\r\n this.options = {\r\n endpoint: 'https://api.senzor.dev/api/ingest/apm',\r\n batchSize: 100,\r\n flushInterval: 10000,\r\n debug: false,\r\n ...options\r\n };\r\n\r\n this.transport = new Transport({\r\n apiKey: this.options.apiKey,\r\n endpoint: this.options.endpoint!,\r\n batchSize: this.options.batchSize!,\r\n flushInterval: this.options.flushInterval!,\r\n debug: this.options.debug || false\r\n });\r\n\r\n if (this.options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n // --- Manual Tracking (For any framework) ---\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n\r\n this.transport.add({\r\n ...data,\r\n timestamp: new Date().toISOString()\r\n });\r\n }\r\n\r\n // --- Force Flush (For Serverless/Lambda) ---\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // Start Timer (High Precision)\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n res.once('finish', () => {\r\n try {\r\n const duration = performance.now() - start;\r\n\r\n // Route Normalization Logic\r\n // Express stores route info in req.route\r\n let route = 'UNKNOWN';\r\n\r\n if (req.route && req.route.path) {\r\n // Combined baseUrl (if mounted on /api) + path (/:id)\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n // Fallback for unmapped routes or static files\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.track({\r\n method: req.method,\r\n route: route,\r\n path: req.originalUrl || req.url,\r\n status: res.statusCode,\r\n duration: duration,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Types for H3 (Mocked to avoid heavy peer dependencies)\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return async (event: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n let error: any = null;\r\n\r\n try {\r\n const response = await handler(event);\r\n // Try to determine status from response or event\r\n if (event.node?.res?.statusCode) {\r\n status = event.node.res.statusCode;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n error = err;\r\n status = err.statusCode || err.status || 500;\r\n throw err;\r\n } finally {\r\n // Non-blocking collection\r\n const duration = performance.now() - start;\r\n const req = event.node.req;\r\n\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: getRoute(event, path), // H3 often attaches context to event\r\n path: path,\r\n status: status,\r\n duration: duration,\r\n ip: getIp(req),\r\n userAgent: req.headers['user-agent'],\r\n });\r\n\r\n // If serverless, we might need to await flush, but for general H3 usage (Node preset)\r\n // we assume the process stays alive or uses ctx.waitUntil\r\n }\r\n };\r\n};\r\n\r\nconst getIp = (req: any) => {\r\n return req.headers['x-forwarded-for'] || req.socket?.remoteAddress;\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (GET, POST, etc.) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n const start = performance.now();\r\n let status = 200;\r\n\r\n try {\r\n const response = await handler(req, context);\r\n if (response && typeof response.status === 'number') {\r\n status = response.status;\r\n }\r\n return response;\r\n } catch (err: any) {\r\n status = 500;\r\n throw err;\r\n } finally {\r\n const duration = performance.now() - start;\r\n\r\n // App Router Request is a standard Web Request object\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n\r\n // In App Router, we often rely on file-system path, but context.params helps\r\n // For now, robust heuristic normalization is best\r\n const route = normalizePath(url.pathname);\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: route,\r\n path: url.pathname,\r\n status: status,\r\n duration: duration,\r\n userAgent: req.headers.get ? req.headers.get('user-agent') : undefined,\r\n // IP extraction from Web Request is tricky without headers\r\n ip: req.headers.get ? req.headers.get('x-forwarded-for') : undefined,\r\n });\r\n\r\n // Vercel/Serverless Flush Safety\r\n // We purposefully do NOT await flush here to avoid latency.\r\n // Ideally user configures transport to sync flush or uses waitUntil\r\n }\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (req, res) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const start = performance.now();\r\n\r\n // Hook into response finish\r\n const done = () => {\r\n const duration = performance.now() - start;\r\n const path = req.url || '/';\r\n\r\n client.track({\r\n method: req.method || 'GET',\r\n route: normalizePath(path.split('?')[0]),\r\n path: path,\r\n status: res.statusCode || 200,\r\n duration: duration,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent']\r\n });\r\n };\r\n\r\n res.once('finish', done);\r\n // Handle error case if next/error isn't used\r\n res.once('close', done);\r\n\r\n return handler(req, res);\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/client';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};","import { client, SenzorOptions } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n track: client.track.bind(client),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };"],"mappings":"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","names":["Transport","config","event","batch","err","SenzorClient","options","Transport","data","client","expressMiddleware","req","res","next","start","duration","route","client","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","start","status","error","response","err","duration","req","path","client","getRoute","getIp","wrapNextRoute","handler","req","context","start","status","response","err","duration","url","route","normalizePath","client","wrapNextPages","res","done","path","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
1
+ {"version":3,"sources":["../src/core/transport.ts","../src/core/context.ts","../src/core/client.ts","../src/instrumentation/http.ts","../src/instrumentation/mongo.ts","../src/instrumentation/pg.ts","../src/middleware/express.ts","../src/core/normalizer.ts","../src/wrappers/h3.ts","../src/wrappers/next.ts","../src/wrappers/fastify.ts","../src/index.ts"],"sourcesContent":["import { SenzorOptions } from './types';\r\n\r\nexport class Transport {\r\n private queue: any[] = [];\r\n private timer: NodeJS.Timeout | null = null;\r\n\r\n constructor(private config: SenzorOptions) {\r\n if (typeof setInterval !== 'undefined') {\r\n this.timer = setInterval(() => this.flush(), config.flushInterval || 10000);\r\n if (this.timer && typeof this.timer.unref === 'function') {\r\n this.timer.unref(); // Don't block process exit\r\n }\r\n }\r\n }\r\n\r\n public add(trace: any) {\r\n this.queue.push(trace);\r\n if (this.queue.length >= (this.config.batchSize || 100)) {\r\n this.flush();\r\n }\r\n }\r\n\r\n public async flush() {\r\n if (this.queue.length === 0) return;\r\n\r\n const batch = [...this.queue];\r\n this.queue = [];\r\n\r\n try {\r\n // Use global fetch (Node 18+)\r\n await fetch(this.config.endpoint || 'https://api.senzor.dev/api/ingest/apm', {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'x-service-api-key': this.config.apiKey,\r\n },\r\n body: JSON.stringify(batch),\r\n keepalive: true,\r\n });\r\n \r\n if (this.config.debug) console.log(`[Senzor] Flushed ${batch.length} traces`);\r\n } catch (err) {\r\n if (this.config.debug) console.error('[Senzor] Ingestion Error:', err);\r\n // Dropping data to prevent memory leaks is preferred in APM\r\n }\r\n }\r\n}","import { AsyncLocalStorage } from 'async_hooks';\r\nimport { ActiveTrace } from './types';\r\n\r\n// Storage to hold the current Trace for any async operation\r\nexport const storage = new AsyncLocalStorage<ActiveTrace>();\r\n\r\nexport const Context = {\r\n // Run a function within a trace context\r\n // Updated to be Generic <T> to allow returning values (Promises, etc)\r\n run: <T>(trace: ActiveTrace, fn: () => T): T => {\r\n return storage.run(trace, fn);\r\n },\r\n\r\n // Get current trace (safe)\r\n current: (): ActiveTrace | undefined => {\r\n return storage.getStore();\r\n },\r\n\r\n // Add a span to the current trace\r\n addSpan: (span: any) => {\r\n const store = storage.getStore();\r\n if (store) {\r\n store.spans.push(span);\r\n }\r\n }\r\n};","import { Transport } from './transport';\r\nimport { Context } from './context';\r\nimport { SenzorOptions, ActiveTrace } from './types';\r\nimport { randomUUID } from 'crypto';\r\nimport { instrumentHttp } from '../instrumentation/http';\r\nimport { instrumentMongo } from '../instrumentation/mongo';\r\nimport { instrumentPg } from '../instrumentation/pg';\r\n\r\nexport class SenzorClient {\r\n private transport: Transport | null = null;\r\n private options: SenzorOptions | null = null;\r\n private isInstrumented = false;\r\n\r\n public init(options: SenzorOptions) {\r\n if (!options.apiKey) {\r\n console.warn('[Senzor] API Key missing. SDK disabled.');\r\n return;\r\n }\r\n this.options = options;\r\n const endpoint = options.endpoint || 'https://api.senzor.dev/api/ingest/apm';\r\n\r\n this.transport = new Transport({\r\n ...options,\r\n endpoint\r\n });\r\n\r\n if (!this.isInstrumented) {\r\n try { instrumentHttp(endpoint); } catch (e) { }\r\n try { instrumentMongo(); } catch (e) { }\r\n try { instrumentPg(); } catch (e) { }\r\n\r\n this.isInstrumented = true;\r\n if (options.debug) console.log('[Senzor] Auto-instrumentation enabled');\r\n }\r\n\r\n if (options.debug) console.log('[Senzor] Initialized');\r\n }\r\n\r\n public startTrace<T>(data: Partial<ActiveTrace['data']>, next: () => T): T {\r\n if (!this.transport) return next();\r\n\r\n const trace: ActiveTrace = {\r\n id: randomUUID(),\r\n startTime: performance.now(),\r\n data: data,\r\n spans: []\r\n };\r\n\r\n return Context.run(trace, next);\r\n }\r\n\r\n public endTrace(status: number, extraData: any = {}) {\r\n const trace = Context.current();\r\n if (!trace || !this.transport) return;\r\n\r\n const duration = performance.now() - trace.startTime;\r\n\r\n const payload = {\r\n traceId: trace.id,\r\n ...trace.data,\r\n ...extraData,\r\n status,\r\n duration,\r\n spans: trace.spans,\r\n timestamp: new Date().toISOString()\r\n };\r\n\r\n this.transport.add(payload);\r\n }\r\n\r\n public track(data: {\r\n method: string;\r\n route: string;\r\n path: string;\r\n status: number;\r\n duration: number;\r\n ip?: string;\r\n userAgent?: string;\r\n }) {\r\n if (!this.transport) return;\r\n const payload = {\r\n traceId: randomUUID(),\r\n ...data,\r\n spans: [],\r\n timestamp: new Date().toISOString()\r\n };\r\n this.transport.add(payload);\r\n }\r\n\r\n public startSpan(name: string, type: 'db' | 'http' | 'function' | 'custom' = 'custom') {\r\n const trace = Context.current();\r\n if (!trace) return { end: () => { } };\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n return {\r\n end: (meta?: any, status?: number) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name,\r\n type,\r\n startTime,\r\n duration,\r\n status,\r\n meta\r\n });\r\n }\r\n };\r\n }\r\n\r\n public async flush() {\r\n if (this.transport) await this.transport.flush();\r\n }\r\n}\r\n\r\nexport const client = new SenzorClient();","import http from 'http';\r\nimport https from 'https';\r\nimport { URL } from 'url';\r\nimport { Context } from '../core/context';\r\n\r\n// Helper to safely wrap modules\r\nconst shimmer = (module: any, methodName: string, wrapper: (original: Function) => Function) => {\r\n if (!module[methodName]) return;\r\n const original = module[methodName];\r\n module[methodName] = wrapper(original);\r\n};\r\n\r\nexport const instrumentHttp = (ingestUrl: string) => {\r\n const ingestHost = new URL(ingestUrl).hostname;\r\n\r\n const requestWrapper = (original: Function) => {\r\n return function (this: any, ...args: any[]) {\r\n // 1. Parse Arguments to get URL\r\n let options: any = {};\r\n let urlStr = '';\r\n\r\n if (typeof args[0] === 'string' || args[0] instanceof URL) {\r\n urlStr = args[0].toString();\r\n options = args[1] || {};\r\n } else {\r\n options = args[0] || {};\r\n const protocol = options.protocol || 'http:';\r\n const host = options.hostname || options.host || 'localhost';\r\n const path = options.path || '/';\r\n urlStr = `${protocol}//${host}${path}`;\r\n }\r\n\r\n // 2. SAFETY GUARD: Ignore calls to Senzor Ingest API (Prevent Infinite Loop)\r\n if (urlStr.includes(ingestHost) || (options.hostname && options.hostname.includes(ingestHost))) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 3. Check if we are inside an Active Trace\r\n // If we are not handling a user request, don't trace background http calls\r\n const trace = Context.current();\r\n if (!trace) {\r\n return original.apply(this, args);\r\n }\r\n\r\n // 4. Start Span\r\n const method = (options.method || 'GET').toUpperCase();\r\n const startTime = performance.now() - trace.startTime; // Relative to trace start\r\n const spanStartAbs = performance.now();\r\n\r\n // 5. Execute Request\r\n const req = original.apply(this, args);\r\n\r\n // 6. Hook into Response/Error\r\n req.on('response', (res: any) => {\r\n // Wait for end of stream to calculate full duration (TTFB + Download)\r\n res.on('end', () => {\r\n const duration = performance.now() - spanStartAbs;\r\n \r\n Context.addSpan({\r\n name: `${method} ${new URL(urlStr).hostname}`, // e.g. \"GET api.stripe.com\"\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: res.statusCode,\r\n meta: {\r\n url: urlStr,\r\n method: method,\r\n }\r\n });\r\n });\r\n });\r\n\r\n req.on('error', (err: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `${method} ${urlStr}`,\r\n type: 'http',\r\n startTime,\r\n duration,\r\n status: 500, // Client Error\r\n meta: { error: err.message }\r\n });\r\n });\r\n\r\n return req;\r\n };\r\n };\r\n\r\n // Apply patches\r\n shimmer(http, 'request', requestWrapper);\r\n shimmer(http, 'get', requestWrapper);\r\n shimmer(https, 'request', requestWrapper);\r\n shimmer(https, 'get', requestWrapper);\r\n};","import { Context } from '../core/context';\r\n\r\nexport const instrumentMongo = () => {\r\n try {\r\n const mongodb = require('mongodb');\r\n const Collection = mongodb.Collection;\r\n\r\n const methods = [\r\n 'find',\r\n 'findOne',\r\n 'insertOne',\r\n 'insertMany',\r\n 'updateOne',\r\n 'updateMany',\r\n 'deleteOne',\r\n 'deleteMany',\r\n 'aggregate',\r\n 'countDocuments'\r\n ];\r\n\r\n methods.forEach((method) => {\r\n if (!Collection.prototype[method]) return;\r\n\r\n const original = Collection.prototype[method];\r\n\r\n Collection.prototype[method] = function (...args: any[]) {\r\n const trace = Context.current();\r\n // If not inside a tracked request, just run normally\r\n if (!trace) return original.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n const collectionName = this.collectionName;\r\n\r\n // Helper to finish span\r\n const endSpan = (err?: Error) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: `MongoDB ${method} (${collectionName})`,\r\n type: 'db',\r\n startTime,\r\n duration,\r\n status: err ? 500 : 0,\r\n meta: {\r\n collection: collectionName,\r\n operation: method,\r\n error: err ? err.message : undefined\r\n }\r\n });\r\n };\r\n\r\n try {\r\n const result = original.apply(this, args);\r\n\r\n // Handle Promise/Cursor\r\n if (result && typeof result.then === 'function') {\r\n return result.then(\r\n (res: any) => { endSpan(); return res; },\r\n (err: any) => { endSpan(err); throw err; }\r\n );\r\n }\r\n // Handle FindCursor (it doesn't execute immediately, but for APM visuals we mark start/return)\r\n // Ideally we'd wrap toArray() but that's complex. For 'find', we just mark the creation.\r\n endSpan();\r\n return result;\r\n\r\n } catch (err: any) {\r\n endSpan(err);\r\n throw err;\r\n }\r\n };\r\n });\r\n\r\n } catch (e) {\r\n // User doesn't use mongodb, ignore\r\n }\r\n};","import { Context } from '../core/context';\r\n\r\n// Simple shim for 'pg' library\r\nexport const instrumentPg = () => {\r\n try {\r\n // Try to require pg (it might not be installed by user)\r\n const pg = require('pg');\r\n const originalQuery = pg.Client.prototype.query;\r\n\r\n pg.Client.prototype.query = function (...args: any[]) {\r\n const trace = Context.current();\r\n if (!trace) return originalQuery.apply(this, args);\r\n\r\n const startTime = performance.now() - trace.startTime;\r\n const spanStartAbs = performance.now();\r\n\r\n // Extract SQL (first arg usually string or config object)\r\n const sql = typeof args[0] === 'string' ? args[0] : args[0].text;\r\n\r\n // Wrap callback if present, or handle Promise\r\n const result = originalQuery.apply(this, args);\r\n\r\n if (result && typeof result.then === 'function') {\r\n return result.then((res: any) => {\r\n const duration = performance.now() - spanStartAbs;\r\n Context.addSpan({\r\n name: 'Postgres Query',\r\n type: 'db',\r\n startTime,\r\n duration,\r\n meta: { query: sql }\r\n });\r\n return res;\r\n });\r\n }\r\n return result;\r\n };\r\n } catch (e) {\r\n // User doesn't use pg, ignore\r\n }\r\n};","import { client } from '../core/client';\r\n\r\nexport const expressMiddleware = () => {\r\n return (req: any, res: any, next: () => void) => {\r\n // We MUST use startTrace to enable Auto-Instrumentation for this request\r\n client.startTrace({\r\n method: req.method,\r\n path: req.originalUrl || req.url,\r\n ip: req.ip || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, () => {\r\n\r\n res.once('finish', () => {\r\n try {\r\n let route = 'UNKNOWN';\r\n if (req.route && req.route.path) {\r\n route = (req.baseUrl || '') + req.route.path;\r\n } else if (res.statusCode === 404) {\r\n route = 'Not Found';\r\n } else {\r\n route = req.path || 'Wildcard';\r\n }\r\n\r\n client.endTrace(res.statusCode, { route });\r\n } catch (e) {\r\n // Fail open\r\n }\r\n });\r\n\r\n next();\r\n });\r\n };\r\n};","/**\r\n * Heuristic URL Normalizer\r\n * Converts raw paths with IDs into generic patterns to prevent high cardinality.\r\n * Example: /users/123/orders/abc-def -> /users/:id/orders/:uuid\r\n */\r\nexport const normalizePath = (path: string): string => {\r\n if (!path || path === '/') return '/';\r\n\r\n return path\r\n // Replace UUIDs (long alphanumeric strings)\r\n .replace(\r\n /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g,\r\n ':uuid'\r\n )\r\n // Replace MongoDB ObjectIds (24 hex chars)\r\n .replace(/[0-9a-fA-F]{24}/g, ':objectId')\r\n // Replace pure numeric IDs (e.g., /123)\r\n .replace(/\\/(\\d+)(?=\\/|$)/g, '/:id')\r\n // Remove query strings\r\n .split('?')[0];\r\n};\r\n\r\n/**\r\n * Tries to extract route from Framework internals, falls back to heuristic\r\n */\r\nexport const getRoute = (req: any, fallbackPath: string): string => {\r\n // Express / Connect\r\n if (req.route && req.route.path) {\r\n return (req.baseUrl || '') + req.route.path;\r\n }\r\n\r\n // H3 / Nitro (Nuxt)\r\n if (req.context && req.context.matchedRoute) {\r\n return req.context.matchedRoute.path;\r\n }\r\n\r\n // Fastify\r\n if (req.routerPath) {\r\n return req.routerPath;\r\n }\r\n\r\n // Fallback: Heuristic Normalization\r\n return normalizePath(fallbackPath);\r\n};","import { client } from '../core/client';\r\nimport { getRoute } from '../core/normalizer';\r\n\r\n// Minimal types for H3 to avoid peer-deps\r\ntype EventHandler = (event: any) => any;\r\n\r\nexport const wrapH3 = (handler: EventHandler) => {\r\n return (event: any) => {\r\n const req = event.node.req;\r\n const path = req.originalUrl || req.url || '/';\r\n\r\n // Start Trace Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n userAgent: req.headers['user-agent'],\r\n }, async () => {\r\n try {\r\n const response = await handler(event);\r\n\r\n // H3/Nitro response status\r\n let status = 200;\r\n if (event.node.res.statusCode) status = event.node.res.statusCode;\r\n // Check if response is an error object\r\n if (response && response.statusCode) status = response.statusCode;\r\n\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n return response;\r\n } catch (err: any) {\r\n const status = err.statusCode || err.status || 500;\r\n client.endTrace(status, { route: getRoute(event, path) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { normalizePath } from '../core/normalizer';\r\n\r\n// --- App Router Wrapper (Route Handlers) ---\r\nexport const wrapNextRoute = (handler: Function) => {\r\n return async (req: Request | any, context?: any) => {\r\n // 1. Extract Info\r\n const url = req.url ? new URL(req.url) : { pathname: '/' };\r\n const method = req.method || 'GET';\r\n const ua = req.headers.get ? req.headers.get('user-agent') : undefined;\r\n const ip = req.headers.get ? req.headers.get('x-forwarded-for') : undefined;\r\n\r\n // 2. Run in Context\r\n return client.startTrace({\r\n method,\r\n path: url.pathname,\r\n userAgent: ua,\r\n ip: ip\r\n }, async () => {\r\n try {\r\n const response = await handler(req, context);\r\n const status = response?.status || 200;\r\n \r\n client.endTrace(status, { route: normalizePath(url.pathname) });\r\n return response;\r\n } catch (err: any) {\r\n client.endTrace(500, { route: normalizePath(url.pathname) });\r\n throw err;\r\n }\r\n });\r\n };\r\n};\r\n\r\n// --- Pages Router Wrapper (API Routes) ---\r\nexport const wrapNextPages = (handler: Function) => {\r\n return async (req: any, res: any) => {\r\n const path = req.url ? req.url.split('?')[0] : '/';\r\n \r\n // 1. Run in Context\r\n return client.startTrace({\r\n method: req.method || 'GET',\r\n path: path,\r\n userAgent: req.headers['user-agent'],\r\n ip: req.headers['x-forwarded-for'] || req.socket?.remoteAddress,\r\n }, async () => {\r\n \r\n // 2. Hook Response\r\n const done = () => {\r\n client.endTrace(res.statusCode || 200, { route: normalizePath(path) });\r\n };\r\n \r\n res.once('finish', done);\r\n res.once('close', done); // Fallback if finish doesn't fire\r\n\r\n // 3. Execute\r\n try {\r\n return await handler(req, res);\r\n } catch (e) {\r\n // Next.js Pages router usually handles errors internally, \r\n // but we ensure we catch sync errors here\r\n throw e;\r\n }\r\n });\r\n };\r\n};","import { client } from '../core/client';\r\nimport { SenzorOptions } from '../core/types';\r\n\r\n// We don't import Fastify types to keep zero-deps, but structure matches\r\nexport const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {\r\n\r\n // Init if options provided inline, otherwise assume global init\r\n if (options && options.apiKey) {\r\n client.init(options);\r\n }\r\n\r\n // Hook: On Request (Start Timer)\r\n fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {\r\n request.senzorStart = performance.now();\r\n next();\r\n });\r\n\r\n // Hook: On Response (End Timer & Track)\r\n fastify.addHook('onResponse', (request: any, reply: any, next: Function) => {\r\n const duration = performance.now() - (request.senzorStart || performance.now());\r\n\r\n // Fastify provides 'routerPath' (e.g. /user/:id)\r\n const route = request.routeOptions?.url || request.routerPath;\r\n\r\n client.track({\r\n method: request.method,\r\n route: route || 'UNKNOWN',\r\n path: request.raw.url || request.url,\r\n status: reply.statusCode,\r\n duration: duration,\r\n ip: request.ip,\r\n userAgent: request.headers['user-agent']\r\n });\r\n\r\n next();\r\n });\r\n\r\n done();\r\n};","import { client } from './core/client';\r\nimport { expressMiddleware } from './middleware/express';\r\nimport { wrapH3 } from './wrappers/h3';\r\nimport { wrapNextRoute, wrapNextPages } from './wrappers/next';\r\nimport { senzorPlugin } from './wrappers/fastify';\r\nimport { SenzorOptions } from './core/types';\r\n\r\nconst Senzor = {\r\n // Core\r\n init: (options: SenzorOptions) => client.init(options),\r\n flush: () => client.flush(),\r\n\r\n // Express / Connect\r\n requestHandler: expressMiddleware,\r\n\r\n // Next.js\r\n wrapNextRoute, // For App Router (Route Handlers)\r\n wrapNextPages, // For Pages Router (API Routes)\r\n\r\n // H3 / Nuxt / Nitro\r\n wrapH3,\r\n\r\n // Fastify\r\n fastifyPlugin: senzorPlugin\r\n};\r\n\r\nexport default Senzor;\r\nexport { Senzor };"],"mappings":"yPAEO,IAAMA,EAAN,KAAgB,CAIrB,YAAoBC,EAAuB,CAAvB,YAAAA,EAHpB,KAAQ,MAAe,CAAC,EACxB,KAAQ,MAA+B,KAGjC,OAAO,YAAgB,MACzB,KAAK,MAAQ,YAAY,IAAM,KAAK,MAAM,EAAGA,EAAO,eAAiB,GAAK,EACtE,KAAK,OAAS,OAAO,KAAK,MAAM,OAAU,YAC5C,KAAK,MAAM,MAAM,EAGvB,CAEO,IAAIC,EAAY,CACrB,KAAK,MAAM,KAAKA,CAAK,EACjB,KAAK,MAAM,SAAW,KAAK,OAAO,WAAa,MACjD,KAAK,MAAM,CAEf,CAEA,MAAa,OAAQ,CACnB,GAAI,KAAK,MAAM,SAAW,EAAG,OAE7B,IAAMC,EAAQ,CAAC,GAAG,KAAK,KAAK,EAC5B,KAAK,MAAQ,CAAC,EAEd,GAAI,CAEF,MAAM,MAAM,KAAK,OAAO,UAAY,wCAAyC,CAC3E,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,oBAAqB,KAAK,OAAO,MACnC,EACA,KAAM,KAAK,UAAUA,CAAK,EAC1B,UAAW,EACb,CAAC,EAEG,KAAK,OAAO,OAAO,QAAQ,IAAI,oBAAoBA,EAAM,MAAM,SAAS,CAC9E,OAASC,EAAK,CACR,KAAK,OAAO,OAAO,QAAQ,MAAM,4BAA6BA,CAAG,CAEvE,CACF,CACF,EC9CA,OAAS,qBAAAC,MAAyB,cAI3B,IAAMC,EAAU,IAAID,EAEdE,EAAU,CAGrB,IAAK,CAAIC,EAAoBC,IACpBH,EAAQ,IAAIE,EAAOC,CAAE,EAI9B,QAAS,IACAH,EAAQ,SAAS,EAI1B,QAAUI,GAAc,CACtB,IAAMC,EAAQL,EAAQ,SAAS,EAC3BK,GACFA,EAAM,MAAM,KAAKD,CAAI,CAEzB,CACF,ECtBA,OAAS,cAAAE,MAAkB,SCH3B,OAAOC,MAAU,OACjB,OAAOC,MAAW,QAClB,OAAS,OAAAC,MAAW,MAIpB,IAAMC,EAAU,CAACC,EAAaC,EAAoBC,IAA8C,CAC9F,GAAI,CAACF,EAAOC,CAAU,EAAG,OACzB,IAAME,EAAWH,EAAOC,CAAU,EAClCD,EAAOC,CAAU,EAAIC,EAAQC,CAAQ,CACvC,EAEaC,EAAkBC,GAAsB,CACnD,IAAMC,EAAa,IAAIC,EAAIF,CAAS,EAAE,SAEhCG,EAAkBL,GACf,YAAwBM,EAAa,CAE1C,IAAIC,EAAe,CAAC,EAChBC,EAAS,GAEb,GAAI,OAAOF,EAAK,CAAC,GAAM,UAAYA,EAAK,CAAC,YAAaF,EACpDI,EAASF,EAAK,CAAC,EAAE,SAAS,EAC1BC,EAAUD,EAAK,CAAC,GAAK,CAAC,MACjB,CACLC,EAAUD,EAAK,CAAC,GAAK,CAAC,EACtB,IAAMG,EAAWF,EAAQ,UAAY,QAC/BG,EAAOH,EAAQ,UAAYA,EAAQ,MAAQ,YAC3CI,EAAOJ,EAAQ,MAAQ,IAC7BC,EAAS,GAAGC,CAAQ,KAAKC,CAAI,GAAGC,CAAI,EACtC,CAGA,GAAIH,EAAO,SAASL,CAAU,GAAMI,EAAQ,UAAYA,EAAQ,SAAS,SAASJ,CAAU,EAC1F,OAAOH,EAAS,MAAM,KAAMM,CAAI,EAKlC,IAAMM,EAAQC,EAAQ,QAAQ,EAC9B,GAAI,CAACD,EACH,OAAOZ,EAAS,MAAM,KAAMM,CAAI,EAIlC,IAAMQ,GAAUP,EAAQ,QAAU,OAAO,YAAY,EAC/CQ,EAAY,YAAY,IAAI,EAAIH,EAAM,UACtCI,EAAe,YAAY,IAAI,EAG/BC,EAAMjB,EAAS,MAAM,KAAMM,CAAI,EAGrC,OAAAW,EAAI,GAAG,WAAaC,GAAa,CAE/BA,EAAI,GAAG,MAAO,IAAM,CAClB,IAAMC,EAAW,YAAY,IAAI,EAAIH,EAErCH,EAAQ,QAAQ,CACd,KAAM,GAAGC,CAAM,IAAI,IAAIV,EAAII,CAAM,EAAE,QAAQ,GAC3C,KAAM,OACN,UAAAO,EACA,SAAAI,EACA,OAAQD,EAAI,WACZ,KAAM,CACJ,IAAKV,EACL,OAAQM,CACV,CACF,CAAC,CACH,CAAC,CACH,CAAC,EAEDG,EAAI,GAAG,QAAUG,GAAe,CAC9B,IAAMD,EAAW,YAAY,IAAI,EAAIH,EACrCH,EAAQ,QAAQ,CACd,KAAM,GAAGC,CAAM,IAAIN,CAAM,GACzB,KAAM,OACN,UAAAO,EACA,SAAAI,EACA,OAAQ,IACR,KAAM,CAAE,MAAOC,EAAI,OAAQ,CAC7B,CAAC,CACH,CAAC,EAEMH,CACT,EAIFrB,EAAQyB,EAAM,UAAWhB,CAAc,EACvCT,EAAQyB,EAAM,MAAOhB,CAAc,EACnCT,EAAQ0B,EAAO,UAAWjB,CAAc,EACxCT,EAAQ0B,EAAO,MAAOjB,CAAc,CACtC,EC3FO,IAAMkB,EAAkB,IAAM,CACnC,GAAI,CAEF,IAAMC,EADU,EAAQ,SAAS,EACN,WAEX,CACd,OACA,UACA,YACA,aACA,YACA,aACA,YACA,aACA,YACA,gBACF,EAEQ,QAASC,GAAW,CAC1B,GAAI,CAACD,EAAW,UAAUC,CAAM,EAAG,OAEnC,IAAMC,EAAWF,EAAW,UAAUC,CAAM,EAE5CD,EAAW,UAAUC,CAAM,EAAI,YAAaE,EAAa,CACvD,IAAMC,EAAQC,EAAQ,QAAQ,EAE9B,GAAI,CAACD,EAAO,OAAOF,EAAS,MAAM,KAAMC,CAAI,EAE5C,IAAMG,EAAY,YAAY,IAAI,EAAIF,EAAM,UACtCG,EAAe,YAAY,IAAI,EAC/BC,EAAiB,KAAK,eAGtBC,EAAWC,GAAgB,CAC/B,IAAMC,EAAW,YAAY,IAAI,EAAIJ,EACrCF,EAAQ,QAAQ,CACd,KAAM,WAAWJ,CAAM,KAAKO,CAAc,IAC1C,KAAM,KACN,UAAAF,EACA,SAAAK,EACA,OAAQD,EAAM,IAAM,EACpB,KAAM,CACJ,WAAYF,EACZ,UAAWP,EACX,MAAOS,EAAMA,EAAI,QAAU,MAC7B,CACF,CAAC,CACH,EAEA,GAAI,CACF,IAAME,EAASV,EAAS,MAAM,KAAMC,CAAI,EAGxC,OAAIS,GAAU,OAAOA,EAAO,MAAS,WAC5BA,EAAO,KACXC,IAAeJ,EAAQ,EAAUI,GACjCH,GAAa,CAAE,MAAAD,EAAQC,CAAG,EAASA,CAAK,CAC3C,GAIFD,EAAQ,EACDG,EAET,OAASF,EAAU,CACjB,MAAAD,EAAQC,CAAG,EACLA,CACR,CACF,CACF,CAAC,CAEH,MAAY,CAEZ,CACF,ECzEO,IAAMI,EAAe,IAAM,CAChC,GAAI,CAEF,IAAMC,EAAK,EAAQ,IAAI,EACjBC,EAAgBD,EAAG,OAAO,UAAU,MAE1CA,EAAG,OAAO,UAAU,MAAQ,YAAaE,EAAa,CACpD,IAAMC,EAAQC,EAAQ,QAAQ,EAC9B,GAAI,CAACD,EAAO,OAAOF,EAAc,MAAM,KAAMC,CAAI,EAEjD,IAAMG,EAAY,YAAY,IAAI,EAAIF,EAAM,UACtCG,EAAe,YAAY,IAAI,EAG/BC,EAAM,OAAOL,EAAK,CAAC,GAAM,SAAWA,EAAK,CAAC,EAAIA,EAAK,CAAC,EAAE,KAGtDM,EAASP,EAAc,MAAM,KAAMC,CAAI,EAE7C,OAAIM,GAAU,OAAOA,EAAO,MAAS,WAC5BA,EAAO,KAAMC,GAAa,CAC/B,IAAMC,EAAW,YAAY,IAAI,EAAIJ,EACrC,OAAAF,EAAQ,QAAQ,CACd,KAAM,iBACN,KAAM,KACN,UAAAC,EACA,SAAAK,EACA,KAAM,CAAE,MAAOH,CAAI,CACrB,CAAC,EACME,CACT,CAAC,EAEID,CACT,CACF,MAAY,CAEZ,CACF,EHhCO,IAAMG,EAAN,KAAmB,CAAnB,cACL,KAAQ,UAA8B,KACtC,KAAQ,QAAgC,KACxC,KAAQ,eAAiB,GAElB,KAAKC,EAAwB,CAClC,GAAI,CAACA,EAAQ,OAAQ,CACnB,QAAQ,KAAK,yCAAyC,EACtD,MACF,CACA,KAAK,QAAUA,EACf,IAAMC,EAAWD,EAAQ,UAAY,wCAOrC,GALA,KAAK,UAAY,IAAIE,EAAU,CAC7B,GAAGF,EACH,SAAAC,CACF,CAAC,EAEG,CAAC,KAAK,eAAgB,CACxB,GAAI,CAAEE,EAAeF,CAAQ,CAAG,MAAY,CAAE,CAC9C,GAAI,CAAEG,EAAgB,CAAG,MAAY,CAAE,CACvC,GAAI,CAAEC,EAAa,CAAG,MAAY,CAAE,CAEpC,KAAK,eAAiB,GAClBL,EAAQ,OAAO,QAAQ,IAAI,uCAAuC,CACxE,CAEIA,EAAQ,OAAO,QAAQ,IAAI,sBAAsB,CACvD,CAEO,WAAcM,EAAoCC,EAAkB,CACzE,GAAI,CAAC,KAAK,UAAW,OAAOA,EAAK,EAEjC,IAAMC,EAAqB,CACzB,GAAIC,EAAW,EACf,UAAW,YAAY,IAAI,EAC3B,KAAMH,EACN,MAAO,CAAC,CACV,EAEA,OAAOI,EAAQ,IAAIF,EAAOD,CAAI,CAChC,CAEO,SAASI,EAAgBC,EAAiB,CAAC,EAAG,CACnD,IAAMJ,EAAQE,EAAQ,QAAQ,EAC9B,GAAI,CAACF,GAAS,CAAC,KAAK,UAAW,OAE/B,IAAMK,EAAW,YAAY,IAAI,EAAIL,EAAM,UAErCM,EAAU,CACd,QAASN,EAAM,GACf,GAAGA,EAAM,KACT,GAAGI,EACH,OAAAD,EACA,SAAAE,EACA,MAAOL,EAAM,MACb,UAAW,IAAI,KAAK,EAAE,YAAY,CACpC,EAEA,KAAK,UAAU,IAAIM,CAAO,CAC5B,CAEO,MAAMR,EAQV,CACD,GAAI,CAAC,KAAK,UAAW,OACrB,IAAMQ,EAAU,CACd,QAASL,EAAW,EACpB,GAAGH,EACH,MAAO,CAAC,EACR,UAAW,IAAI,KAAK,EAAE,YAAY,CACpC,EACA,KAAK,UAAU,IAAIQ,CAAO,CAC5B,CAEO,UAAUC,EAAcC,EAA8C,SAAU,CACrF,IAAMR,EAAQE,EAAQ,QAAQ,EAC9B,GAAI,CAACF,EAAO,MAAO,CAAE,IAAK,IAAM,CAAE,CAAE,EAEpC,IAAMS,EAAY,YAAY,IAAI,EAAIT,EAAM,UACtCU,EAAe,YAAY,IAAI,EAErC,MAAO,CACL,IAAK,CAACC,EAAYR,IAAoB,CACpC,IAAME,EAAW,YAAY,IAAI,EAAIK,EACrCR,EAAQ,QAAQ,CACd,KAAAK,EACA,KAAAC,EACA,UAAAC,EACA,SAAAJ,EACA,OAAAF,EACA,KAAAQ,CACF,CAAC,CACH,CACF,CACF,CAEA,MAAa,OAAQ,CACf,KAAK,WAAW,MAAM,KAAK,UAAU,MAAM,CACjD,CACF,EAEaC,EAAS,IAAIrB,EIlHnB,IAAMsB,EAAoB,IACxB,CAACC,EAAUC,EAAUC,IAAqB,CAE/CC,EAAO,WAAW,CAChB,OAAQH,EAAI,OACZ,KAAMA,EAAI,aAAeA,EAAI,IAC7B,GAAIA,EAAI,IAAMA,EAAI,QAAQ,cAC1B,UAAWA,EAAI,QAAQ,YAAY,CACrC,EAAG,IAAM,CAEPC,EAAI,KAAK,SAAU,IAAM,CACvB,GAAI,CACF,IAAIG,EAAQ,UACRJ,EAAI,OAASA,EAAI,MAAM,KACzBI,GAASJ,EAAI,SAAW,IAAMA,EAAI,MAAM,KAC/BC,EAAI,aAAe,IAC5BG,EAAQ,YAERA,EAAQJ,EAAI,MAAQ,WAGtBG,EAAO,SAASF,EAAI,WAAY,CAAE,MAAAG,CAAM,CAAC,CAC3C,MAAY,CAEZ,CACF,CAAC,EAEDF,EAAK,CACP,CAAC,CACH,EC1BK,IAAMG,EAAiBC,GACxB,CAACA,GAAQA,IAAS,IAAY,IAE3BA,EAEJ,QACC,+EACA,OACF,EAEC,QAAQ,mBAAoB,WAAW,EAEvC,QAAQ,mBAAoB,MAAM,EAElC,MAAM,GAAG,EAAE,CAAC,EAMJC,EAAW,CAACC,EAAUC,IAE7BD,EAAI,OAASA,EAAI,MAAM,MACjBA,EAAI,SAAW,IAAMA,EAAI,MAAM,KAIrCA,EAAI,SAAWA,EAAI,QAAQ,aACtBA,EAAI,QAAQ,aAAa,KAI9BA,EAAI,WACCA,EAAI,WAINH,EAAcI,CAAY,ECpC5B,IAAMC,EAAUC,GACbC,GAAe,CACrB,IAAMC,EAAMD,EAAM,KAAK,IACjBE,EAAOD,EAAI,aAAeA,EAAI,KAAO,IAG3C,OAAOE,EAAO,WAAW,CACvB,OAAQF,EAAI,QAAU,MACtB,KAAMC,EACN,GAAID,EAAI,QAAQ,iBAAiB,GAAKA,EAAI,QAAQ,cAClD,UAAWA,EAAI,QAAQ,YAAY,CACrC,EAAG,SAAY,CACb,GAAI,CACF,IAAMG,EAAW,MAAML,EAAQC,CAAK,EAGhCK,EAAS,IACb,OAAIL,EAAM,KAAK,IAAI,aAAYK,EAASL,EAAM,KAAK,IAAI,YAEnDI,GAAYA,EAAS,aAAYC,EAASD,EAAS,YAEvDD,EAAO,SAASE,EAAQ,CAAE,MAAOC,EAASN,EAAOE,CAAI,CAAE,CAAC,EACjDE,CACT,OAASG,EAAU,CACjB,IAAMF,EAASE,EAAI,YAAcA,EAAI,QAAU,IAC/C,MAAAJ,EAAO,SAASE,EAAQ,CAAE,MAAOC,EAASN,EAAOE,CAAI,CAAE,CAAC,EAClDK,CACR,CACF,CAAC,CACH,EC/BK,IAAMC,EAAiBC,GACrB,MAAOC,EAAoBC,IAAkB,CAElD,IAAMC,EAAMF,EAAI,IAAM,IAAI,IAAIA,EAAI,GAAG,EAAI,CAAE,SAAU,GAAI,EACnDG,EAASH,EAAI,QAAU,MACvBI,EAAKJ,EAAI,QAAQ,IAAMA,EAAI,QAAQ,IAAI,YAAY,EAAI,OACvDK,EAAKL,EAAI,QAAQ,IAAMA,EAAI,QAAQ,IAAI,iBAAiB,EAAI,OAGlE,OAAOM,EAAO,WAAW,CACvB,OAAAH,EACA,KAAMD,EAAI,SACV,UAAWE,EACX,GAAIC,CACN,EAAG,SAAY,CACb,GAAI,CACF,IAAME,EAAW,MAAMR,EAAQC,EAAKC,CAAO,EACrCO,EAASD,GAAU,QAAU,IAEnC,OAAAD,EAAO,SAASE,EAAQ,CAAE,MAAOC,EAAcP,EAAI,QAAQ,CAAE,CAAC,EACvDK,CACT,OAASG,EAAU,CACjB,MAAAJ,EAAO,SAAS,IAAK,CAAE,MAAOG,EAAcP,EAAI,QAAQ,CAAE,CAAC,EACrDQ,CACR,CACF,CAAC,CACH,EAIWC,EAAiBZ,GACrB,MAAOC,EAAUY,IAAa,CACnC,IAAMC,EAAOb,EAAI,IAAMA,EAAI,IAAI,MAAM,GAAG,EAAE,CAAC,EAAI,IAG/C,OAAOM,EAAO,WAAW,CACvB,OAAQN,EAAI,QAAU,MACtB,KAAMa,EACN,UAAWb,EAAI,QAAQ,YAAY,EACnC,GAAIA,EAAI,QAAQ,iBAAiB,GAAKA,EAAI,QAAQ,aACpD,EAAG,SAAY,CAGb,IAAMc,EAAO,IAAM,CACjBR,EAAO,SAASM,EAAI,YAAc,IAAK,CAAE,MAAOH,EAAcI,CAAI,CAAE,CAAC,CACvE,EAEAD,EAAI,KAAK,SAAUE,CAAI,EACvBF,EAAI,KAAK,QAASE,CAAI,EAGtB,GAAI,CACF,OAAO,MAAMf,EAAQC,EAAKY,CAAG,CAC/B,OAASG,EAAG,CAGV,MAAMA,CACR,CACF,CAAC,CACH,EC3DK,IAAMC,EAAe,CAACC,EAAcC,EAAwBC,IAAmB,CAGhFD,GAAWA,EAAQ,QACrBE,EAAO,KAAKF,CAAO,EAIrBD,EAAQ,QAAQ,YAAa,CAACI,EAAcC,EAAYC,IAAmB,CACzEF,EAAQ,YAAc,YAAY,IAAI,EACtCE,EAAK,CACP,CAAC,EAGDN,EAAQ,QAAQ,aAAc,CAACI,EAAcC,EAAYC,IAAmB,CAC1E,IAAMC,EAAW,YAAY,IAAI,GAAKH,EAAQ,aAAe,YAAY,IAAI,GAGvEI,EAAQJ,EAAQ,cAAc,KAAOA,EAAQ,WAEnDD,EAAO,MAAM,CACX,OAAQC,EAAQ,OAChB,MAAOI,GAAS,UAChB,KAAMJ,EAAQ,IAAI,KAAOA,EAAQ,IACjC,OAAQC,EAAM,WACd,SAAUE,EACV,GAAIH,EAAQ,GACZ,UAAWA,EAAQ,QAAQ,YAAY,CACzC,CAAC,EAEDE,EAAK,CACP,CAAC,EAEDJ,EAAK,CACP,EC/BA,IAAMO,EAAS,CAEb,KAAOC,GAA2BC,EAAO,KAAKD,CAAO,EACrD,MAAO,IAAMC,EAAO,MAAM,EAG1B,eAAgBC,EAGhB,cAAAC,EACA,cAAAC,EAGA,OAAAC,EAGA,cAAeC,CACjB,EAEOC,GAAQR","names":["Transport","config","trace","batch","err","AsyncLocalStorage","storage","Context","trace","fn","span","store","randomUUID","http","https","URL","shimmer","module","methodName","wrapper","original","instrumentHttp","ingestUrl","ingestHost","URL","requestWrapper","args","options","urlStr","protocol","host","path","trace","Context","method","startTime","spanStartAbs","req","res","duration","err","http","https","instrumentMongo","Collection","method","original","args","trace","Context","startTime","spanStartAbs","collectionName","endSpan","err","duration","result","res","instrumentPg","pg","originalQuery","args","trace","Context","startTime","spanStartAbs","sql","result","res","duration","SenzorClient","options","endpoint","Transport","instrumentHttp","instrumentMongo","instrumentPg","data","next","trace","randomUUID","Context","status","extraData","duration","payload","name","type","startTime","spanStartAbs","meta","client","expressMiddleware","req","res","next","client","route","normalizePath","path","getRoute","req","fallbackPath","wrapH3","handler","event","req","path","client","response","status","getRoute","err","wrapNextRoute","handler","req","context","url","method","ua","ip","client","response","status","normalizePath","err","wrapNextPages","res","path","done","e","senzorPlugin","fastify","options","done","client","request","reply","next","duration","route","Senzor","options","client","expressMiddleware","wrapNextRoute","wrapNextPages","wrapH3","senzorPlugin","index_default"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senzops/apm-node",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Universal APM SDK for Senzor",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",