@victor-studio/monitor 0.2.0 → 0.4.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.cjs CHANGED
@@ -164,6 +164,91 @@ function isDev() {
164
164
  return false;
165
165
  }
166
166
 
167
+ // src/errors/normalize.ts
168
+ function generateGroupingKey(error) {
169
+ const name = error.name || "Error";
170
+ const message = stripDynamicValues(error.message);
171
+ const frames = extractTopFrames(error.stack, 3);
172
+ const raw = `${name}:${message}:${frames.join("|")}`;
173
+ return simpleHash(raw);
174
+ }
175
+ function normalizeStack(stack) {
176
+ if (!stack) return void 0;
177
+ return stack.split("\n").map((line) => {
178
+ let normalized = line.replace(/\(?\/?(?:[\w.-]+\/)+/g, "");
179
+ normalized = normalized.replace(/\?[^):\s]+/g, "");
180
+ return normalized;
181
+ }).join("\n");
182
+ }
183
+ function extractTopFrames(stack, count) {
184
+ if (!stack) return [];
185
+ return stack.split("\n").filter((line) => line.includes("at ") || line.includes("@")).slice(0, count).map((line) => line.trim());
186
+ }
187
+ function stripDynamicValues(message) {
188
+ return message.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<uuid>").replace(/\b\d{4,}\b/g, "<n>").replace(/["'][^"']{32,}["']/g, "<token>");
189
+ }
190
+ function simpleHash(str) {
191
+ let hash = 0;
192
+ for (let i = 0; i < str.length; i++) {
193
+ const char = str.charCodeAt(i);
194
+ hash = (hash << 5) - hash + char | 0;
195
+ }
196
+ return (hash >>> 0).toString(16).padStart(8, "0");
197
+ }
198
+
199
+ // src/errors/handler.ts
200
+ function captureError(monitor, error, context) {
201
+ const normalized = toError(error);
202
+ const data = {
203
+ type: "caught",
204
+ message: normalized.message,
205
+ stack: normalizeStack(normalized.stack),
206
+ groupingKey: generateGroupingKey(normalized),
207
+ route: context?.route,
208
+ method: context?.method,
209
+ statusCode: context?.statusCode,
210
+ extra: context?.extra
211
+ };
212
+ monitor.captureError(data);
213
+ }
214
+ function setupGlobalHandlers(monitor) {
215
+ if (typeof window === "undefined") return () => {
216
+ };
217
+ const onError = (event) => {
218
+ const error = event.error ?? new Error(event.message);
219
+ const normalized = toError(error);
220
+ monitor.captureError({
221
+ type: "unhandled",
222
+ message: normalized.message,
223
+ stack: normalizeStack(normalized.stack),
224
+ groupingKey: generateGroupingKey(normalized),
225
+ route: window.location.pathname
226
+ });
227
+ };
228
+ const onRejection = (event) => {
229
+ const error = event.reason ?? new Error("Unhandled promise rejection");
230
+ const normalized = toError(error);
231
+ monitor.captureError({
232
+ type: "promise",
233
+ message: normalized.message,
234
+ stack: normalizeStack(normalized.stack),
235
+ groupingKey: generateGroupingKey(normalized),
236
+ route: window.location.pathname
237
+ });
238
+ };
239
+ window.addEventListener("error", onError);
240
+ window.addEventListener("unhandledrejection", onRejection);
241
+ return () => {
242
+ window.removeEventListener("error", onError);
243
+ window.removeEventListener("unhandledrejection", onRejection);
244
+ };
245
+ }
246
+ function toError(value) {
247
+ if (value instanceof Error) return value;
248
+ if (typeof value === "string") return new Error(value);
249
+ return new Error(String(value));
250
+ }
251
+
167
252
  // src/core/logger.ts
168
253
  var PREFIX = "[monitor]";
169
254
  function createLogger(debug) {
@@ -185,6 +270,7 @@ var MonitorClient = class {
185
270
  collector;
186
271
  logger;
187
272
  heartbeatTimer = null;
273
+ globalHandlersCleanup = null;
188
274
  active = false;
189
275
  constructor(config) {
190
276
  this.config = config;
@@ -214,6 +300,10 @@ var MonitorClient = class {
214
300
  this.active = true;
215
301
  this.collector.start();
216
302
  this.startHeartbeat();
303
+ if (this.config.captureErrors || this.config.captureUnhandledRejections) {
304
+ this.globalHandlersCleanup = setupGlobalHandlers(this);
305
+ this.logger.debug("global error handlers registered");
306
+ }
217
307
  this.logger.debug("started", `endpoint=${this.config.endpoint}`);
218
308
  }
219
309
  /** Para o monitoring */
@@ -225,6 +315,10 @@ var MonitorClient = class {
225
315
  clearInterval(this.heartbeatTimer);
226
316
  this.heartbeatTimer = null;
227
317
  }
318
+ if (this.globalHandlersCleanup) {
319
+ this.globalHandlersCleanup();
320
+ this.globalHandlersCleanup = null;
321
+ }
228
322
  this.logger.debug("stopped");
229
323
  }
230
324
  /** Força um flush imediato do buffer */
@@ -303,6 +397,8 @@ function createMonitor(config) {
303
397
  }
304
398
 
305
399
  exports.MonitorClient = MonitorClient;
400
+ exports.captureError = captureError;
306
401
  exports.createMonitor = createMonitor;
402
+ exports.setupGlobalHandlers = setupGlobalHandlers;
307
403
  //# sourceMappingURL=index.cjs.map
308
404
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/transport.ts","../src/core/collector.ts","../src/core/env.ts","../src/core/logger.ts","../src/core/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Transport — envia batch de eventos pro backend Nuvio\n\nimport type { MonitorEvent } from '../types'\nimport type { Logger } from './logger'\n\nexport interface TransportConfig {\n endpoint: string\n apiKey: string\n /** Timeout do fetch em ms (default: 10000) */\n timeout?: number\n /** Max tentativas de retry (default: 3) */\n maxRetries?: number\n}\n\ninterface SendOptions {\n /** Usar sendBeacon em vez de fetch (para page unload) */\n useBeacon?: boolean\n}\n\nconst DEFAULT_TIMEOUT = 10_000\nconst DEFAULT_MAX_RETRIES = 3\nconst MAX_PAYLOAD_BYTES = 60_000 // 60KB — abaixo do limite de 64KB do keepalive\n\n/**\n * Envia um batch de eventos para o endpoint.\n * Usa Authorization header, retry com backoff, e sendBeacon como fallback.\n */\nexport async function sendBatch(\n config: TransportConfig,\n events: MonitorEvent[],\n logger: Logger,\n options?: SendOptions,\n): Promise<boolean> {\n if (events.length === 0) return true\n\n const payload = JSON.stringify({ events })\n\n // Se é unload, usa sendBeacon (mais confiável que fetch no page close)\n if (options?.useBeacon && typeof navigator !== 'undefined' && navigator.sendBeacon) {\n // sendBeacon não suporta headers customizados — inclui apiKey no body\n const beaconPayload = JSON.stringify({ apiKey: config.apiKey, events })\n const beaconBlob = new Blob([beaconPayload], { type: 'application/json' })\n const sent = navigator.sendBeacon(config.endpoint, beaconBlob)\n logger.debug('sendBeacon', sent ? 'queued' : 'failed', `${events.length} events`)\n return sent\n }\n\n // Dividir em batches menores se payload é muito grande\n const payloadSize = new Blob([payload]).size\n if (payloadSize > MAX_PAYLOAD_BYTES && events.length > 1) {\n const mid = Math.floor(events.length / 2)\n const [first, second] = await Promise.all([\n sendBatch(config, events.slice(0, mid), logger),\n sendBatch(config, events.slice(mid), logger),\n ])\n return first && second\n }\n\n return sendWithRetry(config, payload, logger)\n}\n\nasync function sendWithRetry(\n config: TransportConfig,\n payload: string,\n logger: Logger,\n): Promise<boolean> {\n const maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeout)\n\n const response = await fetch(config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: payload,\n signal: controller.signal,\n keepalive: attempt === 0, // keepalive só na primeira tentativa\n })\n\n clearTimeout(timer)\n\n if (response.ok) {\n logger.debug('sent', `attempt ${attempt + 1}`, response.status)\n return true\n }\n\n // 4xx — erro do cliente, não faz retry (apiKey inválida, payload mal formado, etc.)\n if (response.status >= 400 && response.status < 500) {\n logger.warn('client error', response.status, '— no retry')\n return false\n }\n\n // 5xx — erro do servidor, faz retry\n logger.warn('server error', response.status, `attempt ${attempt + 1}/${maxRetries + 1}`)\n } catch (err) {\n // Network error ou timeout — faz retry\n const message = err instanceof Error ? err.message : 'unknown error'\n logger.warn('network error', message, `attempt ${attempt + 1}/${maxRetries + 1}`)\n }\n\n // Esperar antes do retry (exponential backoff com jitter)\n if (attempt < maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt), 16_000)\n const jitter = Math.random() * 1000\n await sleep(delay + jitter)\n }\n }\n\n logger.error('all retries exhausted, dropping batch')\n return false\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","// Collector — acumula eventos em buffer e envia em batch\n\nimport type { MonitorEvent, MonitorEventInput, BeforeSendHook } from '../types'\nimport { sendBatch, type TransportConfig } from './transport'\nimport type { Logger } from './logger'\n\nconst DEFAULT_FLUSH_INTERVAL = 30_000 // 30 segundos\nconst MAX_BUFFER_SIZE = 50\n\nexport class Collector {\n private buffer: MonitorEvent[] = []\n private timer: ReturnType<typeof setInterval> | null = null\n private config: TransportConfig\n private flushInterval: number\n private logger: Logger\n private beforeSend: BeforeSendHook | undefined\n private sampleRate: number\n private visibilityHandler: (() => void) | null = null\n\n constructor(\n config: TransportConfig,\n logger: Logger,\n options?: {\n flushInterval?: number\n beforeSend?: BeforeSendHook\n sampleRate?: number\n },\n ) {\n this.config = config\n this.logger = logger\n this.flushInterval = options?.flushInterval ?? DEFAULT_FLUSH_INTERVAL\n this.beforeSend = options?.beforeSend\n this.sampleRate = options?.sampleRate ?? 1.0\n }\n\n start() {\n if (this.timer) return\n\n this.timer = setInterval(() => {\n this.flush()\n }, this.flushInterval)\n\n // Flush quando a página for fechada (browser) — usa sendBeacon\n if (typeof window !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'hidden') {\n this.flush(true)\n }\n }\n window.addEventListener('visibilitychange', this.visibilityHandler)\n }\n }\n\n stop() {\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n // Remover listener para evitar memory leak\n if (this.visibilityHandler && typeof window !== 'undefined') {\n window.removeEventListener('visibilitychange', this.visibilityHandler)\n this.visibilityHandler = null\n }\n\n // Flush final\n this.flush()\n }\n\n push(input: MonitorEventInput) {\n // Sampling — heartbeats nunca são amostrados\n if (input.type !== 'heartbeat' && this.sampleRate < 1.0) {\n if (Math.random() >= this.sampleRate) {\n this.logger.debug('sampled out', input.type)\n return\n }\n }\n\n const event: MonitorEvent = {\n ...input,\n timestamp: new Date().toISOString(),\n } as MonitorEvent\n\n // beforeSend hook — permite filtrar ou modificar eventos\n if (this.beforeSend) {\n const result = this.beforeSend(event)\n if (result === null) {\n this.logger.debug('dropped by beforeSend', event.type)\n return\n }\n // Usa o evento possivelmente modificado\n this.buffer.push(result)\n } else {\n this.buffer.push(event)\n }\n\n this.logger.debug('buffered', event.type, `(${this.buffer.length}/${MAX_BUFFER_SIZE})`)\n\n // Se buffer cheio, flush imediato\n if (this.buffer.length >= MAX_BUFFER_SIZE) {\n this.flush()\n }\n }\n\n /** Flush manual — exposto via MonitorClient.flush() */\n flush(useBeacon = false) {\n if (this.buffer.length === 0) return\n\n const events = [...this.buffer]\n this.buffer = []\n\n this.logger.debug('flushing', events.length, 'events', useBeacon ? '(beacon)' : '')\n\n // Fire-and-forget — não bloqueia o app\n sendBatch(this.config, events, this.logger, { useBeacon }).catch(() => {\n // Silencioso — transport já loga erros via logger\n })\n }\n\n /** Número de eventos no buffer (para debug/testes) */\n get pending(): number {\n return this.buffer.length\n }\n}\n","// Detecção de ambiente — centraliza lógica de dev/server\n\nconst LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '0.0.0.0', '[::1]'])\n\n/** Detecta se estamos em ambiente de desenvolvimento */\nexport function isDev(): boolean {\n // Node.js / bundler env\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') return true\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') return true\n\n // Browser\n if (typeof window !== 'undefined' && LOCAL_HOSTNAMES.has(window.location?.hostname)) return true\n\n return false\n}\n\n/** Detecta se estamos rodando no servidor (Node.js) */\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\n/** Detecta se estamos no browser */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined'\n}\n","// Logger — debug mode com prefixo [monitor]\n\nconst PREFIX = '[monitor]'\n\nexport interface Logger {\n debug(...args: unknown[]): void\n warn(...args: unknown[]): void\n error(...args: unknown[]): void\n}\n\n/** Cria logger que loga no console quando debug=true, noop caso contrário */\nexport function createLogger(debug: boolean): Logger {\n if (!debug) {\n return { debug: noop, warn: noop, error: noop }\n }\n\n return {\n debug: (...args: unknown[]) => console.debug(PREFIX, ...args),\n warn: (...args: unknown[]) => console.warn(PREFIX, ...args),\n error: (...args: unknown[]) => console.error(PREFIX, ...args),\n }\n}\n\nfunction noop() {\n // silencioso\n}\n","// MonitorClient — instância principal do SDK\n\nimport type {\n HeartbeatData,\n RequestData,\n VitalData,\n AdapterData,\n ErrorData,\n DeployData,\n BeforeSendHook,\n} from '../types'\nimport { Collector } from './collector'\nimport { isDev } from './env'\nimport { createLogger, type Logger } from './logger'\n\nexport interface MonitorConfig {\n /** ID do projeto no Nuvio */\n projectId: string\n /** API key gerada no Nuvio */\n apiKey: string\n /** URL do endpoint de ingest */\n endpoint: string\n /** Intervalo do heartbeat em ms (default: 60000) */\n heartbeatInterval?: number\n /** Intervalo do flush do buffer em ms (default: 30000) */\n flushInterval?: number\n /** Timeout do fetch em ms (default: 10000) */\n timeout?: number\n /** Max tentativas de retry (default: 3) */\n maxRetries?: number\n /** Desabilitar em desenvolvimento (default: true) */\n disableInDev?: boolean\n /** Taxa de amostragem 0.0-1.0 (default: 1.0). Heartbeats nunca são amostrados */\n sampleRate?: number\n /** Hook chamado antes de enfileirar cada evento. Retorna null para dropar */\n beforeSend?: BeforeSendHook\n /** Habilitar logs de debug no console (default: false) */\n debug?: boolean\n}\n\nexport class MonitorClient {\n readonly config: MonitorConfig\n private readonly collector: Collector\n private readonly logger: Logger\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n private active = false\n\n constructor(config: MonitorConfig) {\n this.config = config\n this.logger = createLogger(config.debug ?? false)\n this.collector = new Collector(\n {\n endpoint: config.endpoint,\n apiKey: config.apiKey,\n timeout: config.timeout,\n maxRetries: config.maxRetries,\n },\n this.logger,\n {\n flushInterval: config.flushInterval,\n beforeSend: config.beforeSend,\n sampleRate: config.sampleRate,\n },\n )\n }\n\n /** Inicia o monitoring (heartbeat + collector) */\n start() {\n // Não roda em dev por padrão\n if (this.config.disableInDev !== false && isDev()) {\n this.logger.debug('disabled in dev — call start() with disableInDev: false to override')\n return\n }\n\n if (this.active) return\n\n this.active = true\n this.collector.start()\n this.startHeartbeat()\n this.logger.debug('started', `endpoint=${this.config.endpoint}`)\n }\n\n /** Para o monitoring */\n stop() {\n if (!this.active) return\n\n this.active = false\n this.collector.stop()\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n\n this.logger.debug('stopped')\n }\n\n /** Força um flush imediato do buffer */\n flush() {\n this.collector.flush()\n }\n\n /** Verifica se o monitoring está ativo */\n get isActive(): boolean {\n return this.active\n }\n\n /** Registra um evento de request HTTP (usado pelo middleware) */\n trackRequest(data: RequestData) {\n if (!this.active) return\n this.collector.push({ type: 'request', data })\n }\n\n /** Registra um Web Vital (usado pelo MonitorScript) */\n trackVital(data: VitalData) {\n if (!this.active) return\n this.collector.push({ type: 'vital', data })\n }\n\n /** Registra um evento de adapter (DB, cache, AI, queue, email) */\n trackAdapter(data: AdapterData) {\n if (!this.active) return\n this.collector.push({ type: 'adapter', data })\n }\n\n /** Registra um erro capturado */\n captureError(data: ErrorData) {\n if (!this.active) return\n this.collector.push({ type: 'error', data })\n }\n\n /** Registra um evento de deploy */\n trackDeploy(data: DeployData) {\n if (!this.active) return\n this.collector.push({ type: 'deploy', data })\n }\n\n // ─── Private ────────────────────────────────────────\n\n private startHeartbeat() {\n const interval = this.config.heartbeatInterval ?? 60_000\n\n // Primeiro heartbeat imediato\n this.sendHeartbeat()\n\n this.heartbeatTimer = setInterval(() => {\n this.sendHeartbeat()\n }, interval)\n }\n\n private async sendHeartbeat() {\n const start = performance.now()\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'HEAD',\n cache: 'no-store',\n })\n\n const latencyMs = Math.round(performance.now() - start)\n\n this.collector.push({\n type: 'heartbeat',\n data: {\n status: response.ok ? 'online' : 'offline',\n latencyMs,\n } satisfies HeartbeatData,\n })\n } catch {\n // Network error — o endpoint não respondeu, reporta offline\n this.collector.push({\n type: 'heartbeat',\n data: {\n status: 'offline',\n latencyMs: Math.round(performance.now() - start),\n } satisfies HeartbeatData,\n })\n }\n }\n}\n","// @victor-studio/monitor — SDK de monitoramento\n\nimport { MonitorClient, type MonitorConfig } from './core/client'\n\nexport { MonitorClient, type MonitorConfig } from './core/client'\nexport type {\n MonitorEvent,\n MonitorEventInput,\n MonitorEventType,\n BeforeSendHook,\n HeartbeatData,\n RequestData,\n VitalData,\n ErrorData,\n DeployData,\n AdapterData,\n} from './types'\n\n/**\n * Cria e inicia uma instância do monitor.\n *\n * @example\n * ```ts\n * import { createMonitor } from '@victor-studio/monitor'\n *\n * export const monitor = createMonitor({\n * projectId: 'uuid-do-projeto',\n * apiKey: 'nuvio_mon_xxxxxxxx',\n * endpoint: 'https://nuvio.app/api/monitor/ingest',\n * })\n * ```\n */\nexport function createMonitor(config: MonitorConfig): MonitorClient {\n const client = new MonitorClient(config)\n client.start()\n return client\n}\n"]}
1
+ {"version":3,"sources":["../src/core/transport.ts","../src/core/collector.ts","../src/core/env.ts","../src/errors/normalize.ts","../src/errors/handler.ts","../src/core/logger.ts","../src/core/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Transport — envia batch de eventos pro backend Nuvio\n\nimport type { MonitorEvent } from '../types'\nimport type { Logger } from './logger'\n\nexport interface TransportConfig {\n endpoint: string\n apiKey: string\n /** Timeout do fetch em ms (default: 10000) */\n timeout?: number\n /** Max tentativas de retry (default: 3) */\n maxRetries?: number\n}\n\ninterface SendOptions {\n /** Usar sendBeacon em vez de fetch (para page unload) */\n useBeacon?: boolean\n}\n\nconst DEFAULT_TIMEOUT = 10_000\nconst DEFAULT_MAX_RETRIES = 3\nconst MAX_PAYLOAD_BYTES = 60_000 // 60KB — abaixo do limite de 64KB do keepalive\n\n/**\n * Envia um batch de eventos para o endpoint.\n * Usa Authorization header, retry com backoff, e sendBeacon como fallback.\n */\nexport async function sendBatch(\n config: TransportConfig,\n events: MonitorEvent[],\n logger: Logger,\n options?: SendOptions,\n): Promise<boolean> {\n if (events.length === 0) return true\n\n const payload = JSON.stringify({ events })\n\n // Se é unload, usa sendBeacon (mais confiável que fetch no page close)\n if (options?.useBeacon && typeof navigator !== 'undefined' && navigator.sendBeacon) {\n // sendBeacon não suporta headers customizados — inclui apiKey no body\n const beaconPayload = JSON.stringify({ apiKey: config.apiKey, events })\n const beaconBlob = new Blob([beaconPayload], { type: 'application/json' })\n const sent = navigator.sendBeacon(config.endpoint, beaconBlob)\n logger.debug('sendBeacon', sent ? 'queued' : 'failed', `${events.length} events`)\n return sent\n }\n\n // Dividir em batches menores se payload é muito grande\n const payloadSize = new Blob([payload]).size\n if (payloadSize > MAX_PAYLOAD_BYTES && events.length > 1) {\n const mid = Math.floor(events.length / 2)\n const [first, second] = await Promise.all([\n sendBatch(config, events.slice(0, mid), logger),\n sendBatch(config, events.slice(mid), logger),\n ])\n return first && second\n }\n\n return sendWithRetry(config, payload, logger)\n}\n\nasync function sendWithRetry(\n config: TransportConfig,\n payload: string,\n logger: Logger,\n): Promise<boolean> {\n const maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES\n const timeout = config.timeout ?? DEFAULT_TIMEOUT\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeout)\n\n const response = await fetch(config.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${config.apiKey}`,\n },\n body: payload,\n signal: controller.signal,\n keepalive: attempt === 0, // keepalive só na primeira tentativa\n })\n\n clearTimeout(timer)\n\n if (response.ok) {\n logger.debug('sent', `attempt ${attempt + 1}`, response.status)\n return true\n }\n\n // 4xx — erro do cliente, não faz retry (apiKey inválida, payload mal formado, etc.)\n if (response.status >= 400 && response.status < 500) {\n logger.warn('client error', response.status, '— no retry')\n return false\n }\n\n // 5xx — erro do servidor, faz retry\n logger.warn('server error', response.status, `attempt ${attempt + 1}/${maxRetries + 1}`)\n } catch (err) {\n // Network error ou timeout — faz retry\n const message = err instanceof Error ? err.message : 'unknown error'\n logger.warn('network error', message, `attempt ${attempt + 1}/${maxRetries + 1}`)\n }\n\n // Esperar antes do retry (exponential backoff com jitter)\n if (attempt < maxRetries) {\n const delay = Math.min(1000 * Math.pow(2, attempt), 16_000)\n const jitter = Math.random() * 1000\n await sleep(delay + jitter)\n }\n }\n\n logger.error('all retries exhausted, dropping batch')\n return false\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n","// Collector — acumula eventos em buffer e envia em batch\n\nimport type { MonitorEvent, MonitorEventInput, BeforeSendHook } from '../types'\nimport { sendBatch, type TransportConfig } from './transport'\nimport type { Logger } from './logger'\n\nconst DEFAULT_FLUSH_INTERVAL = 30_000 // 30 segundos\nconst MAX_BUFFER_SIZE = 50\n\nexport class Collector {\n private buffer: MonitorEvent[] = []\n private timer: ReturnType<typeof setInterval> | null = null\n private config: TransportConfig\n private flushInterval: number\n private logger: Logger\n private beforeSend: BeforeSendHook | undefined\n private sampleRate: number\n private visibilityHandler: (() => void) | null = null\n\n constructor(\n config: TransportConfig,\n logger: Logger,\n options?: {\n flushInterval?: number\n beforeSend?: BeforeSendHook\n sampleRate?: number\n },\n ) {\n this.config = config\n this.logger = logger\n this.flushInterval = options?.flushInterval ?? DEFAULT_FLUSH_INTERVAL\n this.beforeSend = options?.beforeSend\n this.sampleRate = options?.sampleRate ?? 1.0\n }\n\n start() {\n if (this.timer) return\n\n this.timer = setInterval(() => {\n this.flush()\n }, this.flushInterval)\n\n // Flush quando a página for fechada (browser) — usa sendBeacon\n if (typeof window !== 'undefined') {\n this.visibilityHandler = () => {\n if (document.visibilityState === 'hidden') {\n this.flush(true)\n }\n }\n window.addEventListener('visibilitychange', this.visibilityHandler)\n }\n }\n\n stop() {\n if (this.timer) {\n clearInterval(this.timer)\n this.timer = null\n }\n\n // Remover listener para evitar memory leak\n if (this.visibilityHandler && typeof window !== 'undefined') {\n window.removeEventListener('visibilitychange', this.visibilityHandler)\n this.visibilityHandler = null\n }\n\n // Flush final\n this.flush()\n }\n\n push(input: MonitorEventInput) {\n // Sampling — heartbeats nunca são amostrados\n if (input.type !== 'heartbeat' && this.sampleRate < 1.0) {\n if (Math.random() >= this.sampleRate) {\n this.logger.debug('sampled out', input.type)\n return\n }\n }\n\n const event: MonitorEvent = {\n ...input,\n timestamp: new Date().toISOString(),\n } as MonitorEvent\n\n // beforeSend hook — permite filtrar ou modificar eventos\n if (this.beforeSend) {\n const result = this.beforeSend(event)\n if (result === null) {\n this.logger.debug('dropped by beforeSend', event.type)\n return\n }\n // Usa o evento possivelmente modificado\n this.buffer.push(result)\n } else {\n this.buffer.push(event)\n }\n\n this.logger.debug('buffered', event.type, `(${this.buffer.length}/${MAX_BUFFER_SIZE})`)\n\n // Se buffer cheio, flush imediato\n if (this.buffer.length >= MAX_BUFFER_SIZE) {\n this.flush()\n }\n }\n\n /** Flush manual — exposto via MonitorClient.flush() */\n flush(useBeacon = false) {\n if (this.buffer.length === 0) return\n\n const events = [...this.buffer]\n this.buffer = []\n\n this.logger.debug('flushing', events.length, 'events', useBeacon ? '(beacon)' : '')\n\n // Fire-and-forget — não bloqueia o app\n sendBatch(this.config, events, this.logger, { useBeacon }).catch(() => {\n // Silencioso — transport já loga erros via logger\n })\n }\n\n /** Número de eventos no buffer (para debug/testes) */\n get pending(): number {\n return this.buffer.length\n }\n}\n","// Detecção de ambiente — centraliza lógica de dev/server\n\nconst LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '0.0.0.0', '[::1]'])\n\n/** Detecta se estamos em ambiente de desenvolvimento */\nexport function isDev(): boolean {\n // Node.js / bundler env\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') return true\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'test') return true\n\n // Browser\n if (typeof window !== 'undefined' && LOCAL_HOSTNAMES.has(window.location?.hostname)) return true\n\n return false\n}\n\n/** Detecta se estamos rodando no servidor (Node.js) */\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\n/** Detecta se estamos no browser */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined'\n}\n","// Normalização de erros — stack cleanup e grouping key\n\n/**\n * Gera uma chave de agrupamento a partir do erro.\n * Erros com o mesmo groupingKey são agrupados no dashboard.\n * Usa: nome do erro + mensagem + 3 primeiros frames do stack.\n */\nexport function generateGroupingKey(error: Error): string {\n const name = error.name || 'Error'\n const message = stripDynamicValues(error.message)\n const frames = extractTopFrames(error.stack, 3)\n\n const raw = `${name}:${message}:${frames.join('|')}`\n return simpleHash(raw)\n}\n\n/**\n * Normaliza stack trace — remove caminhos absolutos e queries dinâmicas.\n */\nexport function normalizeStack(stack: string | undefined): string | undefined {\n if (!stack) return undefined\n\n return stack\n .split('\\n')\n .map((line) => {\n // Remover caminhos absolutos do filesystem\n let normalized = line.replace(/\\(?\\/?(?:[\\w.-]+\\/)+/g, '')\n // Remover query strings e hashes de URLs\n normalized = normalized.replace(/\\?[^):\\s]+/g, '')\n return normalized\n })\n .join('\\n')\n}\n\n// ─── Helpers internos ───────────────────────────────\n\n/** Extrai as N primeiras linhas significativas do stack */\nfunction extractTopFrames(stack: string | undefined, count: number): string[] {\n if (!stack) return []\n\n return stack\n .split('\\n')\n .filter((line) => line.includes('at ') || line.includes('@'))\n .slice(0, count)\n .map((line) => line.trim())\n}\n\n/** Remove valores dinâmicos da mensagem (UUIDs, números, tokens) */\nfunction stripDynamicValues(message: string): string {\n return message\n .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '<uuid>')\n .replace(/\\b\\d{4,}\\b/g, '<n>')\n .replace(/[\"'][^\"']{32,}[\"']/g, '<token>')\n}\n\n/** Hash simples para gerar groupingKey determinístico */\nfunction simpleHash(str: string): string {\n let hash = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hash = ((hash << 5) - hash + char) | 0\n }\n // Converter para hex positivo\n return (hash >>> 0).toString(16).padStart(8, '0')\n}\n","// Error handler — captura de erros manual e automática\n\nimport type { MonitorClient } from '../core/client'\nimport type { ErrorData } from '../types'\nimport { generateGroupingKey, normalizeStack } from './normalize'\n\ninterface CaptureContext {\n route?: string\n method?: string\n statusCode?: number\n extra?: Record<string, string>\n}\n\n/**\n * Captura um erro e envia pro monitor.\n *\n * @example\n * ```ts\n * try {\n * await riskyOperation()\n * } catch (err) {\n * captureError(monitor, err, { route: '/api/users' })\n * }\n * ```\n */\nexport function captureError(\n monitor: MonitorClient,\n error: unknown,\n context?: CaptureContext,\n): void {\n const normalized = toError(error)\n const data: ErrorData = {\n type: 'caught',\n message: normalized.message,\n stack: normalizeStack(normalized.stack),\n groupingKey: generateGroupingKey(normalized),\n route: context?.route,\n method: context?.method,\n statusCode: context?.statusCode,\n extra: context?.extra,\n }\n\n monitor.captureError(data)\n}\n\n/**\n * Registra handlers globais para erros não capturados.\n * Só funciona no browser — no servidor, NÃO interceptamos uncaughtException\n * (perigoso para um SDK de monitoring).\n *\n * @returns Função de cleanup que remove os handlers\n *\n * @example\n * ```ts\n * const cleanup = setupGlobalHandlers(monitor)\n * // Quando quiser parar:\n * cleanup()\n * ```\n */\nexport function setupGlobalHandlers(monitor: MonitorClient): () => void {\n if (typeof window === 'undefined') return () => {}\n\n const onError = (event: ErrorEvent) => {\n const error = event.error ?? new Error(event.message)\n const normalized = toError(error)\n\n monitor.captureError({\n type: 'unhandled',\n message: normalized.message,\n stack: normalizeStack(normalized.stack),\n groupingKey: generateGroupingKey(normalized),\n route: window.location.pathname,\n })\n }\n\n const onRejection = (event: PromiseRejectionEvent) => {\n const error = event.reason ?? new Error('Unhandled promise rejection')\n const normalized = toError(error)\n\n monitor.captureError({\n type: 'promise',\n message: normalized.message,\n stack: normalizeStack(normalized.stack),\n groupingKey: generateGroupingKey(normalized),\n route: window.location.pathname,\n })\n }\n\n window.addEventListener('error', onError)\n window.addEventListener('unhandledrejection', onRejection)\n\n return () => {\n window.removeEventListener('error', onError)\n window.removeEventListener('unhandledrejection', onRejection)\n }\n}\n\n/** Converte qualquer valor em Error */\nfunction toError(value: unknown): Error {\n if (value instanceof Error) return value\n if (typeof value === 'string') return new Error(value)\n return new Error(String(value))\n}\n","// Logger — debug mode com prefixo [monitor]\n\nconst PREFIX = '[monitor]'\n\nexport interface Logger {\n debug(...args: unknown[]): void\n warn(...args: unknown[]): void\n error(...args: unknown[]): void\n}\n\n/** Cria logger que loga no console quando debug=true, noop caso contrário */\nexport function createLogger(debug: boolean): Logger {\n if (!debug) {\n return { debug: noop, warn: noop, error: noop }\n }\n\n return {\n debug: (...args: unknown[]) => console.debug(PREFIX, ...args),\n warn: (...args: unknown[]) => console.warn(PREFIX, ...args),\n error: (...args: unknown[]) => console.error(PREFIX, ...args),\n }\n}\n\nfunction noop() {\n // silencioso\n}\n","// MonitorClient — instância principal do SDK\n\nimport type {\n HeartbeatData,\n RequestData,\n VitalData,\n AdapterData,\n ErrorData,\n DeployData,\n BeforeSendHook,\n} from '../types'\nimport { Collector } from './collector'\nimport { isDev } from './env'\nimport { setupGlobalHandlers } from '../errors/handler'\nimport { createLogger, type Logger } from './logger'\n\nexport interface MonitorConfig {\n /** ID do projeto no Nuvio */\n projectId: string\n /** API key gerada no Nuvio */\n apiKey: string\n /** URL do endpoint de ingest */\n endpoint: string\n /** Intervalo do heartbeat em ms (default: 60000) */\n heartbeatInterval?: number\n /** Intervalo do flush do buffer em ms (default: 30000) */\n flushInterval?: number\n /** Timeout do fetch em ms (default: 10000) */\n timeout?: number\n /** Max tentativas de retry (default: 3) */\n maxRetries?: number\n /** Desabilitar em desenvolvimento (default: true) */\n disableInDev?: boolean\n /** Taxa de amostragem 0.0-1.0 (default: 1.0). Heartbeats nunca são amostrados */\n sampleRate?: number\n /** Hook chamado antes de enfileirar cada evento. Retorna null para dropar */\n beforeSend?: BeforeSendHook\n /** Habilitar logs de debug no console (default: false) */\n debug?: boolean\n /** Capturar erros globais automaticamente — window.onerror (default: false) */\n captureErrors?: boolean\n /** Capturar unhandled promise rejections automaticamente (default: false) */\n captureUnhandledRejections?: boolean\n}\n\nexport class MonitorClient {\n readonly config: MonitorConfig\n private readonly collector: Collector\n private readonly logger: Logger\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n private globalHandlersCleanup: (() => void) | null = null\n private active = false\n\n constructor(config: MonitorConfig) {\n this.config = config\n this.logger = createLogger(config.debug ?? false)\n this.collector = new Collector(\n {\n endpoint: config.endpoint,\n apiKey: config.apiKey,\n timeout: config.timeout,\n maxRetries: config.maxRetries,\n },\n this.logger,\n {\n flushInterval: config.flushInterval,\n beforeSend: config.beforeSend,\n sampleRate: config.sampleRate,\n },\n )\n }\n\n /** Inicia o monitoring (heartbeat + collector) */\n start() {\n // Não roda em dev por padrão\n if (this.config.disableInDev !== false && isDev()) {\n this.logger.debug('disabled in dev — call start() with disableInDev: false to override')\n return\n }\n\n if (this.active) return\n\n this.active = true\n this.collector.start()\n this.startHeartbeat()\n\n // Error tracking automático (browser only)\n if (this.config.captureErrors || this.config.captureUnhandledRejections) {\n this.globalHandlersCleanup = setupGlobalHandlers(this)\n this.logger.debug('global error handlers registered')\n }\n\n this.logger.debug('started', `endpoint=${this.config.endpoint}`)\n }\n\n /** Para o monitoring */\n stop() {\n if (!this.active) return\n\n this.active = false\n this.collector.stop()\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n\n if (this.globalHandlersCleanup) {\n this.globalHandlersCleanup()\n this.globalHandlersCleanup = null\n }\n\n this.logger.debug('stopped')\n }\n\n /** Força um flush imediato do buffer */\n flush() {\n this.collector.flush()\n }\n\n /** Verifica se o monitoring está ativo */\n get isActive(): boolean {\n return this.active\n }\n\n /** Registra um evento de request HTTP (usado pelo middleware) */\n trackRequest(data: RequestData) {\n if (!this.active) return\n this.collector.push({ type: 'request', data })\n }\n\n /** Registra um Web Vital (usado pelo MonitorScript) */\n trackVital(data: VitalData) {\n if (!this.active) return\n this.collector.push({ type: 'vital', data })\n }\n\n /** Registra um evento de adapter (DB, cache, AI, queue, email) */\n trackAdapter(data: AdapterData) {\n if (!this.active) return\n this.collector.push({ type: 'adapter', data })\n }\n\n /** Registra um erro capturado */\n captureError(data: ErrorData) {\n if (!this.active) return\n this.collector.push({ type: 'error', data })\n }\n\n /** Registra um evento de deploy */\n trackDeploy(data: DeployData) {\n if (!this.active) return\n this.collector.push({ type: 'deploy', data })\n }\n\n // ─── Private ────────────────────────────────────────\n\n private startHeartbeat() {\n const interval = this.config.heartbeatInterval ?? 60_000\n\n // Primeiro heartbeat imediato\n this.sendHeartbeat()\n\n this.heartbeatTimer = setInterval(() => {\n this.sendHeartbeat()\n }, interval)\n }\n\n private async sendHeartbeat() {\n const start = performance.now()\n\n try {\n const response = await fetch(this.config.endpoint, {\n method: 'HEAD',\n cache: 'no-store',\n })\n\n const latencyMs = Math.round(performance.now() - start)\n\n this.collector.push({\n type: 'heartbeat',\n data: {\n status: response.ok ? 'online' : 'offline',\n latencyMs,\n } satisfies HeartbeatData,\n })\n } catch {\n // Network error — o endpoint não respondeu, reporta offline\n this.collector.push({\n type: 'heartbeat',\n data: {\n status: 'offline',\n latencyMs: Math.round(performance.now() - start),\n } satisfies HeartbeatData,\n })\n }\n }\n}\n","// @victor-studio/monitor — SDK de monitoramento\n\nimport { MonitorClient, type MonitorConfig } from './core/client'\n\nexport { MonitorClient, type MonitorConfig } from './core/client'\nexport type {\n MonitorEvent,\n MonitorEventInput,\n MonitorEventType,\n BeforeSendHook,\n HeartbeatData,\n RequestData,\n VitalData,\n ErrorData,\n DeployData,\n AdapterData,\n} from './types'\n\nexport { captureError, setupGlobalHandlers } from './errors'\n\n/**\n * Cria e inicia uma instância do monitor.\n *\n * @example\n * ```ts\n * import { createMonitor } from '@victor-studio/monitor'\n *\n * export const monitor = createMonitor({\n * projectId: 'uuid-do-projeto',\n * apiKey: 'nuvio_mon_xxxxxxxx',\n * endpoint: 'https://nuvio.app/api/monitor/ingest',\n * })\n * ```\n */\nexport function createMonitor(config: MonitorConfig): MonitorClient {\n const client = new MonitorClient(config)\n client.start()\n return client\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -102,12 +102,17 @@ interface MonitorConfig {
102
102
  beforeSend?: BeforeSendHook;
103
103
  /** Habilitar logs de debug no console (default: false) */
104
104
  debug?: boolean;
105
+ /** Capturar erros globais automaticamente — window.onerror (default: false) */
106
+ captureErrors?: boolean;
107
+ /** Capturar unhandled promise rejections automaticamente (default: false) */
108
+ captureUnhandledRejections?: boolean;
105
109
  }
106
110
  declare class MonitorClient {
107
111
  readonly config: MonitorConfig;
108
112
  private readonly collector;
109
113
  private readonly logger;
110
114
  private heartbeatTimer;
115
+ private globalHandlersCleanup;
111
116
  private active;
112
117
  constructor(config: MonitorConfig);
113
118
  /** Inicia o monitoring (heartbeat + collector) */
@@ -132,6 +137,41 @@ declare class MonitorClient {
132
137
  private sendHeartbeat;
133
138
  }
134
139
 
140
+ interface CaptureContext {
141
+ route?: string;
142
+ method?: string;
143
+ statusCode?: number;
144
+ extra?: Record<string, string>;
145
+ }
146
+ /**
147
+ * Captura um erro e envia pro monitor.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * try {
152
+ * await riskyOperation()
153
+ * } catch (err) {
154
+ * captureError(monitor, err, { route: '/api/users' })
155
+ * }
156
+ * ```
157
+ */
158
+ declare function captureError(monitor: MonitorClient, error: unknown, context?: CaptureContext): void;
159
+ /**
160
+ * Registra handlers globais para erros não capturados.
161
+ * Só funciona no browser — no servidor, NÃO interceptamos uncaughtException
162
+ * (perigoso para um SDK de monitoring).
163
+ *
164
+ * @returns Função de cleanup que remove os handlers
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const cleanup = setupGlobalHandlers(monitor)
169
+ * // Quando quiser parar:
170
+ * cleanup()
171
+ * ```
172
+ */
173
+ declare function setupGlobalHandlers(monitor: MonitorClient): () => void;
174
+
135
175
  /**
136
176
  * Cria e inicia uma instância do monitor.
137
177
  *
@@ -148,4 +188,4 @@ declare class MonitorClient {
148
188
  */
149
189
  declare function createMonitor(config: MonitorConfig): MonitorClient;
150
190
 
151
- export { type AdapterData, type BeforeSendHook, type DeployData, type ErrorData, type HeartbeatData, MonitorClient, type MonitorConfig, type MonitorEvent, type MonitorEventInput, type MonitorEventType, type RequestData, type VitalData, createMonitor };
191
+ export { type AdapterData, type BeforeSendHook, type DeployData, type ErrorData, type HeartbeatData, MonitorClient, type MonitorConfig, type MonitorEvent, type MonitorEventInput, type MonitorEventType, type RequestData, type VitalData, captureError, createMonitor, setupGlobalHandlers };
package/dist/index.d.ts CHANGED
@@ -102,12 +102,17 @@ interface MonitorConfig {
102
102
  beforeSend?: BeforeSendHook;
103
103
  /** Habilitar logs de debug no console (default: false) */
104
104
  debug?: boolean;
105
+ /** Capturar erros globais automaticamente — window.onerror (default: false) */
106
+ captureErrors?: boolean;
107
+ /** Capturar unhandled promise rejections automaticamente (default: false) */
108
+ captureUnhandledRejections?: boolean;
105
109
  }
106
110
  declare class MonitorClient {
107
111
  readonly config: MonitorConfig;
108
112
  private readonly collector;
109
113
  private readonly logger;
110
114
  private heartbeatTimer;
115
+ private globalHandlersCleanup;
111
116
  private active;
112
117
  constructor(config: MonitorConfig);
113
118
  /** Inicia o monitoring (heartbeat + collector) */
@@ -132,6 +137,41 @@ declare class MonitorClient {
132
137
  private sendHeartbeat;
133
138
  }
134
139
 
140
+ interface CaptureContext {
141
+ route?: string;
142
+ method?: string;
143
+ statusCode?: number;
144
+ extra?: Record<string, string>;
145
+ }
146
+ /**
147
+ * Captura um erro e envia pro monitor.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * try {
152
+ * await riskyOperation()
153
+ * } catch (err) {
154
+ * captureError(monitor, err, { route: '/api/users' })
155
+ * }
156
+ * ```
157
+ */
158
+ declare function captureError(monitor: MonitorClient, error: unknown, context?: CaptureContext): void;
159
+ /**
160
+ * Registra handlers globais para erros não capturados.
161
+ * Só funciona no browser — no servidor, NÃO interceptamos uncaughtException
162
+ * (perigoso para um SDK de monitoring).
163
+ *
164
+ * @returns Função de cleanup que remove os handlers
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const cleanup = setupGlobalHandlers(monitor)
169
+ * // Quando quiser parar:
170
+ * cleanup()
171
+ * ```
172
+ */
173
+ declare function setupGlobalHandlers(monitor: MonitorClient): () => void;
174
+
135
175
  /**
136
176
  * Cria e inicia uma instância do monitor.
137
177
  *
@@ -148,4 +188,4 @@ declare class MonitorClient {
148
188
  */
149
189
  declare function createMonitor(config: MonitorConfig): MonitorClient;
150
190
 
151
- export { type AdapterData, type BeforeSendHook, type DeployData, type ErrorData, type HeartbeatData, MonitorClient, type MonitorConfig, type MonitorEvent, type MonitorEventInput, type MonitorEventType, type RequestData, type VitalData, createMonitor };
191
+ export { type AdapterData, type BeforeSendHook, type DeployData, type ErrorData, type HeartbeatData, MonitorClient, type MonitorConfig, type MonitorEvent, type MonitorEventInput, type MonitorEventType, type RequestData, type VitalData, captureError, createMonitor, setupGlobalHandlers };
package/dist/index.js CHANGED
@@ -162,6 +162,91 @@ function isDev() {
162
162
  return false;
163
163
  }
164
164
 
165
+ // src/errors/normalize.ts
166
+ function generateGroupingKey(error) {
167
+ const name = error.name || "Error";
168
+ const message = stripDynamicValues(error.message);
169
+ const frames = extractTopFrames(error.stack, 3);
170
+ const raw = `${name}:${message}:${frames.join("|")}`;
171
+ return simpleHash(raw);
172
+ }
173
+ function normalizeStack(stack) {
174
+ if (!stack) return void 0;
175
+ return stack.split("\n").map((line) => {
176
+ let normalized = line.replace(/\(?\/?(?:[\w.-]+\/)+/g, "");
177
+ normalized = normalized.replace(/\?[^):\s]+/g, "");
178
+ return normalized;
179
+ }).join("\n");
180
+ }
181
+ function extractTopFrames(stack, count) {
182
+ if (!stack) return [];
183
+ return stack.split("\n").filter((line) => line.includes("at ") || line.includes("@")).slice(0, count).map((line) => line.trim());
184
+ }
185
+ function stripDynamicValues(message) {
186
+ return message.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, "<uuid>").replace(/\b\d{4,}\b/g, "<n>").replace(/["'][^"']{32,}["']/g, "<token>");
187
+ }
188
+ function simpleHash(str) {
189
+ let hash = 0;
190
+ for (let i = 0; i < str.length; i++) {
191
+ const char = str.charCodeAt(i);
192
+ hash = (hash << 5) - hash + char | 0;
193
+ }
194
+ return (hash >>> 0).toString(16).padStart(8, "0");
195
+ }
196
+
197
+ // src/errors/handler.ts
198
+ function captureError(monitor, error, context) {
199
+ const normalized = toError(error);
200
+ const data = {
201
+ type: "caught",
202
+ message: normalized.message,
203
+ stack: normalizeStack(normalized.stack),
204
+ groupingKey: generateGroupingKey(normalized),
205
+ route: context?.route,
206
+ method: context?.method,
207
+ statusCode: context?.statusCode,
208
+ extra: context?.extra
209
+ };
210
+ monitor.captureError(data);
211
+ }
212
+ function setupGlobalHandlers(monitor) {
213
+ if (typeof window === "undefined") return () => {
214
+ };
215
+ const onError = (event) => {
216
+ const error = event.error ?? new Error(event.message);
217
+ const normalized = toError(error);
218
+ monitor.captureError({
219
+ type: "unhandled",
220
+ message: normalized.message,
221
+ stack: normalizeStack(normalized.stack),
222
+ groupingKey: generateGroupingKey(normalized),
223
+ route: window.location.pathname
224
+ });
225
+ };
226
+ const onRejection = (event) => {
227
+ const error = event.reason ?? new Error("Unhandled promise rejection");
228
+ const normalized = toError(error);
229
+ monitor.captureError({
230
+ type: "promise",
231
+ message: normalized.message,
232
+ stack: normalizeStack(normalized.stack),
233
+ groupingKey: generateGroupingKey(normalized),
234
+ route: window.location.pathname
235
+ });
236
+ };
237
+ window.addEventListener("error", onError);
238
+ window.addEventListener("unhandledrejection", onRejection);
239
+ return () => {
240
+ window.removeEventListener("error", onError);
241
+ window.removeEventListener("unhandledrejection", onRejection);
242
+ };
243
+ }
244
+ function toError(value) {
245
+ if (value instanceof Error) return value;
246
+ if (typeof value === "string") return new Error(value);
247
+ return new Error(String(value));
248
+ }
249
+
165
250
  // src/core/logger.ts
166
251
  var PREFIX = "[monitor]";
167
252
  function createLogger(debug) {
@@ -183,6 +268,7 @@ var MonitorClient = class {
183
268
  collector;
184
269
  logger;
185
270
  heartbeatTimer = null;
271
+ globalHandlersCleanup = null;
186
272
  active = false;
187
273
  constructor(config) {
188
274
  this.config = config;
@@ -212,6 +298,10 @@ var MonitorClient = class {
212
298
  this.active = true;
213
299
  this.collector.start();
214
300
  this.startHeartbeat();
301
+ if (this.config.captureErrors || this.config.captureUnhandledRejections) {
302
+ this.globalHandlersCleanup = setupGlobalHandlers(this);
303
+ this.logger.debug("global error handlers registered");
304
+ }
215
305
  this.logger.debug("started", `endpoint=${this.config.endpoint}`);
216
306
  }
217
307
  /** Para o monitoring */
@@ -223,6 +313,10 @@ var MonitorClient = class {
223
313
  clearInterval(this.heartbeatTimer);
224
314
  this.heartbeatTimer = null;
225
315
  }
316
+ if (this.globalHandlersCleanup) {
317
+ this.globalHandlersCleanup();
318
+ this.globalHandlersCleanup = null;
319
+ }
226
320
  this.logger.debug("stopped");
227
321
  }
228
322
  /** Força um flush imediato do buffer */
@@ -300,6 +394,6 @@ function createMonitor(config) {
300
394
  return client;
301
395
  }
302
396
 
303
- export { MonitorClient, createMonitor };
397
+ export { MonitorClient, captureError, createMonitor, setupGlobalHandlers };
304
398
  //# sourceMappingURL=index.js.map
305
399
  //# sourceMappingURL=index.js.map