flarewatch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,277 @@
1
+ # 🔥 flarewatch
2
+
3
+ > Lightweight error tracking, performance monitoring and custom logging for React + Vite apps.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/flarewatch.svg)](https://www.npmjs.com/package/flarewatch)
6
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/flarewatch)](https://bundlephobia.com/package/flarewatch)
7
+ [![license](https://img.shields.io/npm/l/flarewatch.svg)](LICENSE)
8
+
9
+ ---
10
+
11
+ ## Funcionalidades
12
+
13
+ - ✅ Captura de erros de render React (`ErrorBoundary` integrado)
14
+ - ✅ Erros JS globais (`window.onerror`)
15
+ - ✅ Promises rejeitadas sem `.catch()`
16
+ - ✅ Erros de API (fetch wrapper)
17
+ - ✅ Web Vitals: LCP, FID, CLS, FCP, TTFB — com severidade automática
18
+ - ✅ Logs customizados: `info`, `warn`, `error`, `debug`
19
+ - ✅ Transports plugáveis (HTTP, Console, ou crie o seu)
20
+ - ✅ Batch + retry + fallback offline (localStorage)
21
+ - ✅ `beforeSend` hook para filtrar/enriquecer eventos
22
+ - ✅ TypeScript first — 100% tipado
23
+
24
+ ---
25
+
26
+ ## Instalação
27
+
28
+ ```bash
29
+ npm install flarewatch
30
+ # ou
31
+ pnpm add flarewatch
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Setup rápido
37
+
38
+ ### 1. Modo `init()` — entry point do Vite
39
+
40
+ ```ts
41
+ // src/main.ts
42
+ import { init, httpTransport, consoleTransport } from "flarewatch";
43
+
44
+ init({
45
+ transports: [
46
+ httpTransport({ url: "https://seu-backend.com/api/errors" }),
47
+ consoleTransport({ minLevel: "warning" }), // só em dev
48
+ ],
49
+ defaultContext: {
50
+ appVersion: import.meta.env.VITE_APP_VERSION,
51
+ environment: import.meta.env.MODE,
52
+ },
53
+ debug: import.meta.env.DEV,
54
+ });
55
+ ```
56
+
57
+ ### 2. Modo `<FlarewatchProvider>` — com React
58
+
59
+ ```tsx
60
+ // src/main.tsx
61
+ import React from "react";
62
+ import ReactDOM from "react-dom/client";
63
+ import { FlarewatchProvider } from "flarewatch/react";
64
+ import { httpTransport, consoleTransport } from "flarewatch";
65
+ import App from "./App";
66
+
67
+ ReactDOM.createRoot(document.getElementById("root")!).render(
68
+ <FlarewatchProvider
69
+ config={{
70
+ transports: [
71
+ httpTransport({ url: "/api/errors" }),
72
+ consoleTransport(),
73
+ ],
74
+ }}
75
+ fallback={<div>Algo deu errado.</div>}
76
+ >
77
+ <App />
78
+ </FlarewatchProvider>
79
+ );
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Logs customizados
85
+
86
+ ```tsx
87
+ import { useFlarewatch } from "flarewatch/react";
88
+
89
+ function MyComponent() {
90
+ const fw = useFlarewatch();
91
+
92
+ const handleCheckout = async () => {
93
+ fw.info("Checkout iniciado", { cartItems: 3 });
94
+
95
+ try {
96
+ await processPayment();
97
+ fw.info("Pagamento aprovado");
98
+ } catch (err) {
99
+ fw.error(err as Error, { step: "payment" });
100
+ }
101
+ };
102
+ }
103
+ ```
104
+
105
+ Sem Provider (após `init()`):
106
+
107
+ ```ts
108
+ import { getClient } from "flarewatch";
109
+
110
+ const fw = getClient();
111
+ fw.warn("Cache expirado", { key: "user-preferences" });
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Fetch com captura automática
117
+
118
+ ```tsx
119
+ import { useFlarewatchFetch } from "flarewatch/react";
120
+
121
+ function UsersList() {
122
+ const apiFetch = useFlarewatchFetch();
123
+
124
+ useEffect(() => {
125
+ // erros HTTP 4xx/5xx e falhas de rede chegam automático no backend
126
+ apiFetch("/api/users")
127
+ .then(r => r.json())
128
+ .then(setUsers);
129
+ }, []);
130
+ }
131
+ ```
132
+
133
+ Ou sem hooks:
134
+
135
+ ```ts
136
+ import { getClient } from "flarewatch";
137
+
138
+ const fw = getClient();
139
+ const res = await fw.fetch("/api/products");
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Transports
145
+
146
+ ### `httpTransport(options)`
147
+
148
+ | Opção | Tipo | Padrão | Descrição |
149
+ |---|---|---|---|
150
+ | `url` | `string` | — | **Obrigatório.** Endpoint que recebe os eventos |
151
+ | `headers` | `object` | `{}` | Headers extras (Authorization, etc) |
152
+ | `batchMs` | `number` | `2000` | Debounce em ms antes de enviar o batch |
153
+ | `batchSize` | `number` | `20` | Força envio quando atingir N eventos |
154
+ | `timeout` | `number` | `8000` | Timeout da requisição em ms |
155
+ | `retries` | `number` | `3` | Tentativas com backoff exponencial |
156
+ | `offlineFallback` | `boolean` | `true` | Salva no localStorage quando offline |
157
+
158
+ ### `consoleTransport(options)`
159
+
160
+ | Opção | Tipo | Padrão | Descrição |
161
+ |---|---|---|---|
162
+ | `minLevel` | `Severity` | `"debug"` | Nível mínimo para exibir |
163
+ | `verbose` | `boolean` | `false` | Exibe o objeto completo do evento |
164
+
165
+ ### Criar transport customizado
166
+
167
+ ```ts
168
+ import type { Transport } from "flarewatch";
169
+
170
+ const datadogTransport: Transport = {
171
+ name: "datadog",
172
+ send(event) {
173
+ DD_LOGS.logger.log(event.message, event.context, event.severity);
174
+ },
175
+ };
176
+
177
+ init({ transports: [datadogTransport] });
178
+ ```
179
+
180
+ ---
181
+
182
+ ## `beforeSend` — filtrar ou enriquecer eventos
183
+
184
+ ```ts
185
+ init({
186
+ transports: [...],
187
+ beforeSend(event) {
188
+ // descartar erros de extensões de browser
189
+ if (event.stack?.includes("chrome-extension://")) return false;
190
+
191
+ // enriquecer com dados do usuário logado
192
+ return {
193
+ ...event,
194
+ context: {
195
+ ...event.context,
196
+ userId: store.getState().user.id,
197
+ },
198
+ };
199
+ },
200
+ });
201
+ ```
202
+
203
+ ---
204
+
205
+ ## Configuração completa
206
+
207
+ ```ts
208
+ init({
209
+ transports: [
210
+ httpTransport({
211
+ url: "/api/errors",
212
+ headers: { Authorization: `Bearer ${token}` },
213
+ batchMs: 3000,
214
+ batchSize: 30,
215
+ retries: 3,
216
+ }),
217
+ consoleTransport({ minLevel: "warning", verbose: true }),
218
+ ],
219
+ captureGlobalErrors: true,
220
+ captureUnhandledRejections: true,
221
+ capturePerformance: true,
222
+ minLogLevel: "info",
223
+ defaultContext: {
224
+ appVersion: "1.2.3",
225
+ environment: "production",
226
+ userId: getCurrentUser()?.id,
227
+ },
228
+ beforeSend(event) {
229
+ if (event.type.startsWith("perf.") && event.severity === "info") return false;
230
+ return event;
231
+ },
232
+ debug: false,
233
+ });
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Payload enviado ao backend
239
+
240
+ ```json
241
+ {
242
+ "id": "fw-lq3k2-abc12",
243
+ "type": "error.api",
244
+ "severity": "error",
245
+ "message": "HTTP 500 Internal Server Error",
246
+ "stack": "Error: HTTP 500...",
247
+ "url": "https://seuapp.com/dashboard",
248
+ "userAgent": "Mozilla/5.0 ...",
249
+ "timestamp": "2026-03-23T14:00:00.000Z",
250
+ "sessionDuration": 42,
251
+ "context": {
252
+ "endpoint": "/api/users",
253
+ "status": 500,
254
+ "method": "GET",
255
+ "appVersion": "1.2.3",
256
+ "userId": "user-456"
257
+ }
258
+ }
259
+ ```
260
+
261
+ ---
262
+
263
+ ## Web Vitals — thresholds de severidade
264
+
265
+ | Métrica | `info` (bom) | `warning` (precisa melhorar) | `error` (ruim) |
266
+ |---|---|---|---|
267
+ | LCP | ≤ 2500ms | ≤ 4000ms | > 4000ms |
268
+ | FID | ≤ 100ms | ≤ 300ms | > 300ms |
269
+ | CLS | ≤ 0.1 | ≤ 0.25 | > 0.25 |
270
+ | FCP | ≤ 1800ms | ≤ 3000ms | > 3000ms |
271
+ | TTFB | ≤ 800ms | ≤ 1800ms | > 1800ms |
272
+
273
+ ---
274
+
275
+ ## License
276
+
277
+ MIT
@@ -0,0 +1,106 @@
1
+ type Severity = "fatal" | "error" | "warning" | "info" | "debug";
2
+ type EventType = "error.render" | "error.global" | "error.promise" | "error.api" | "log.info" | "log.warn" | "log.error" | "log.debug" | "perf.lcp" | "perf.fid" | "perf.cls" | "perf.ttfb" | "perf.fcp";
3
+ interface FlarewatchEvent {
4
+ /** Identificador único do evento */
5
+ id: string;
6
+ /** Tipo estruturado do evento */
7
+ type: EventType;
8
+ /** Severidade */
9
+ severity: Severity;
10
+ /** Mensagem legível */
11
+ message: string;
12
+ /** Stack trace (quando disponível) */
13
+ stack?: string;
14
+ /** URL onde ocorreu */
15
+ url: string;
16
+ /** User agent */
17
+ userAgent: string;
18
+ /** ISO timestamp */
19
+ timestamp: string;
20
+ /** Duração da sessão em segundos */
21
+ sessionDuration: number;
22
+ /** Dados extras livres */
23
+ context?: Record<string, unknown>;
24
+ }
25
+ interface Transport {
26
+ /** Nome identificador do transport */
27
+ name: string;
28
+ /**
29
+ * Chamado para cada evento capturado.
30
+ * Pode ser async — erros internos são silenciados.
31
+ */
32
+ send(event: FlarewatchEvent): void | Promise<void>;
33
+ }
34
+ interface FlarewatchConfig {
35
+ /**
36
+ * Lista de transports ativos.
37
+ * Use `httpTransport()`, `consoleTransport()` ou crie o seu.
38
+ */
39
+ transports: Transport[];
40
+ /**
41
+ * Capturar erros JS globais (window.onerror).
42
+ * @default true
43
+ */
44
+ captureGlobalErrors?: boolean;
45
+ /**
46
+ * Capturar promises rejeitadas sem .catch().
47
+ * @default true
48
+ */
49
+ captureUnhandledRejections?: boolean;
50
+ /**
51
+ * Capturar métricas de performance via PerformanceObserver (LCP, FID, CLS, FCP, TTFB).
52
+ * @default true
53
+ */
54
+ capturePerformance?: boolean;
55
+ /**
56
+ * Nível mínimo de log para capturar.
57
+ * @default "debug"
58
+ */
59
+ minLogLevel?: Severity;
60
+ /**
61
+ * Dados extras incluídos em todos os eventos (ex: userId, appVersion).
62
+ */
63
+ defaultContext?: Record<string, unknown>;
64
+ /**
65
+ * Hook chamado antes de enviar — retorne false para descartar o evento.
66
+ */
67
+ beforeSend?: (event: FlarewatchEvent) => FlarewatchEvent | false;
68
+ /**
69
+ * Ativa logs de debug da própria lib no console.
70
+ * @default false
71
+ */
72
+ debug?: boolean;
73
+ }
74
+
75
+ declare class FlarewatchClient {
76
+ private config;
77
+ private sessionStart;
78
+ private initialized;
79
+ constructor(config: FlarewatchConfig);
80
+ init(): void;
81
+ destroy(): void;
82
+ log(message: string, context?: Record<string, unknown>): void;
83
+ info(message: string, context?: Record<string, unknown>): void;
84
+ warn(message: string, context?: Record<string, unknown>): void;
85
+ error(message: string | Error, context?: Record<string, unknown>): void;
86
+ debug(message: string, context?: Record<string, unknown>): void;
87
+ captureRenderError(error: Error, componentStack: string): void;
88
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
89
+ capture(type: EventType, severity: Severity, message: string, context?: Record<string, unknown>): void;
90
+ private dispatch;
91
+ private onError;
92
+ private onUnhandledRejection;
93
+ private attachGlobalErrors;
94
+ private attachUnhandledRejections;
95
+ private attachPerformance;
96
+ private observeMetric;
97
+ private observePaintAndNav;
98
+ private lcpSeverity;
99
+ private clsSeverity;
100
+ private fidSeverity;
101
+ private fcpSeverity;
102
+ private ttfbSeverity;
103
+ private debugLog;
104
+ }
105
+
106
+ export { type EventType as E, FlarewatchClient as F, type Severity as S, type Transport as T, type FlarewatchConfig as a, type FlarewatchEvent as b };
@@ -0,0 +1,106 @@
1
+ type Severity = "fatal" | "error" | "warning" | "info" | "debug";
2
+ type EventType = "error.render" | "error.global" | "error.promise" | "error.api" | "log.info" | "log.warn" | "log.error" | "log.debug" | "perf.lcp" | "perf.fid" | "perf.cls" | "perf.ttfb" | "perf.fcp";
3
+ interface FlarewatchEvent {
4
+ /** Identificador único do evento */
5
+ id: string;
6
+ /** Tipo estruturado do evento */
7
+ type: EventType;
8
+ /** Severidade */
9
+ severity: Severity;
10
+ /** Mensagem legível */
11
+ message: string;
12
+ /** Stack trace (quando disponível) */
13
+ stack?: string;
14
+ /** URL onde ocorreu */
15
+ url: string;
16
+ /** User agent */
17
+ userAgent: string;
18
+ /** ISO timestamp */
19
+ timestamp: string;
20
+ /** Duração da sessão em segundos */
21
+ sessionDuration: number;
22
+ /** Dados extras livres */
23
+ context?: Record<string, unknown>;
24
+ }
25
+ interface Transport {
26
+ /** Nome identificador do transport */
27
+ name: string;
28
+ /**
29
+ * Chamado para cada evento capturado.
30
+ * Pode ser async — erros internos são silenciados.
31
+ */
32
+ send(event: FlarewatchEvent): void | Promise<void>;
33
+ }
34
+ interface FlarewatchConfig {
35
+ /**
36
+ * Lista de transports ativos.
37
+ * Use `httpTransport()`, `consoleTransport()` ou crie o seu.
38
+ */
39
+ transports: Transport[];
40
+ /**
41
+ * Capturar erros JS globais (window.onerror).
42
+ * @default true
43
+ */
44
+ captureGlobalErrors?: boolean;
45
+ /**
46
+ * Capturar promises rejeitadas sem .catch().
47
+ * @default true
48
+ */
49
+ captureUnhandledRejections?: boolean;
50
+ /**
51
+ * Capturar métricas de performance via PerformanceObserver (LCP, FID, CLS, FCP, TTFB).
52
+ * @default true
53
+ */
54
+ capturePerformance?: boolean;
55
+ /**
56
+ * Nível mínimo de log para capturar.
57
+ * @default "debug"
58
+ */
59
+ minLogLevel?: Severity;
60
+ /**
61
+ * Dados extras incluídos em todos os eventos (ex: userId, appVersion).
62
+ */
63
+ defaultContext?: Record<string, unknown>;
64
+ /**
65
+ * Hook chamado antes de enviar — retorne false para descartar o evento.
66
+ */
67
+ beforeSend?: (event: FlarewatchEvent) => FlarewatchEvent | false;
68
+ /**
69
+ * Ativa logs de debug da própria lib no console.
70
+ * @default false
71
+ */
72
+ debug?: boolean;
73
+ }
74
+
75
+ declare class FlarewatchClient {
76
+ private config;
77
+ private sessionStart;
78
+ private initialized;
79
+ constructor(config: FlarewatchConfig);
80
+ init(): void;
81
+ destroy(): void;
82
+ log(message: string, context?: Record<string, unknown>): void;
83
+ info(message: string, context?: Record<string, unknown>): void;
84
+ warn(message: string, context?: Record<string, unknown>): void;
85
+ error(message: string | Error, context?: Record<string, unknown>): void;
86
+ debug(message: string, context?: Record<string, unknown>): void;
87
+ captureRenderError(error: Error, componentStack: string): void;
88
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
89
+ capture(type: EventType, severity: Severity, message: string, context?: Record<string, unknown>): void;
90
+ private dispatch;
91
+ private onError;
92
+ private onUnhandledRejection;
93
+ private attachGlobalErrors;
94
+ private attachUnhandledRejections;
95
+ private attachPerformance;
96
+ private observeMetric;
97
+ private observePaintAndNav;
98
+ private lcpSeverity;
99
+ private clsSeverity;
100
+ private fidSeverity;
101
+ private fcpSeverity;
102
+ private ttfbSeverity;
103
+ private debugLog;
104
+ }
105
+
106
+ export { type EventType as E, FlarewatchClient as F, type Severity as S, type Transport as T, type FlarewatchConfig as a, type FlarewatchEvent as b };