@victor-studio/monitor 0.1.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/errors/index.cjs +93 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +183 -0
- package/dist/errors/index.d.ts +183 -0
- package/dist/errors/index.js +88 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.cjs +277 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -39
- package/dist/index.d.ts +144 -39
- package/dist/index.js +276 -37
- package/dist/index.js.map +1 -1
- package/dist/next/index.cjs +31 -12
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +115 -45
- package/dist/next/index.d.ts +115 -45
- package/dist/next/index.js +31 -12
- package/dist/next/index.js.map +1 -1
- package/dist/react/index.cjs +20 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +104 -38
- package/dist/react/index.d.ts +104 -38
- package/dist/react/index.js +20 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +17 -7
package/dist/react/index.d.cts
CHANGED
|
@@ -1,70 +1,137 @@
|
|
|
1
|
-
interface
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
timestamp: string;
|
|
1
|
+
interface HeartbeatData {
|
|
2
|
+
status: 'online' | 'offline';
|
|
3
|
+
latencyMs: number;
|
|
5
4
|
}
|
|
6
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
interface RequestData {
|
|
6
|
+
method: string;
|
|
7
|
+
route: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
responseTimeMs: number;
|
|
10
|
+
userAgent?: string;
|
|
11
|
+
region?: string;
|
|
9
12
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
interface VitalData {
|
|
14
|
+
name: 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB';
|
|
15
|
+
value: number;
|
|
16
|
+
rating: 'good' | 'needs-improvement' | 'poor';
|
|
17
|
+
page?: string;
|
|
18
|
+
deviceType?: string;
|
|
19
|
+
browser?: string;
|
|
20
|
+
}
|
|
21
|
+
interface ErrorData {
|
|
22
|
+
type: 'unhandled' | 'caught' | 'promise';
|
|
23
|
+
message: string;
|
|
24
|
+
stack?: string;
|
|
25
|
+
groupingKey: string;
|
|
26
|
+
route?: string;
|
|
27
|
+
method?: string;
|
|
28
|
+
statusCode?: number;
|
|
29
|
+
environment?: string;
|
|
30
|
+
commitHash?: string;
|
|
31
|
+
extra?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
interface DeployData {
|
|
34
|
+
commitHash: string;
|
|
35
|
+
branch?: string;
|
|
36
|
+
author?: string;
|
|
37
|
+
status: 'started' | 'succeeded' | 'failed';
|
|
38
|
+
buildDurationMs?: number;
|
|
39
|
+
url?: string;
|
|
40
|
+
environment?: string;
|
|
41
|
+
provider?: string;
|
|
21
42
|
}
|
|
43
|
+
interface AdapterData {
|
|
44
|
+
adapter: string;
|
|
45
|
+
operation: string;
|
|
46
|
+
durationMs: number;
|
|
47
|
+
success: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
meta?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
type MonitorEvent = {
|
|
52
|
+
type: 'heartbeat';
|
|
53
|
+
data: HeartbeatData;
|
|
54
|
+
timestamp: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'request';
|
|
57
|
+
data: RequestData;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'vital';
|
|
61
|
+
data: VitalData;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'error';
|
|
65
|
+
data: ErrorData;
|
|
66
|
+
timestamp: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'deploy';
|
|
69
|
+
data: DeployData;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'adapter';
|
|
73
|
+
data: AdapterData;
|
|
74
|
+
timestamp: string;
|
|
75
|
+
};
|
|
76
|
+
/** Hook para filtrar/modificar eventos antes do envio */
|
|
77
|
+
type BeforeSendHook = (event: MonitorEvent) => MonitorEvent | null;
|
|
22
78
|
|
|
23
79
|
interface MonitorConfig {
|
|
24
80
|
/** ID do projeto no Nuvio */
|
|
25
81
|
projectId: string;
|
|
26
82
|
/** API key gerada no Nuvio */
|
|
27
83
|
apiKey: string;
|
|
28
|
-
/** URL do endpoint de ingest
|
|
84
|
+
/** URL do endpoint de ingest */
|
|
29
85
|
endpoint: string;
|
|
30
86
|
/** Intervalo do heartbeat em ms (default: 60000) */
|
|
31
87
|
heartbeatInterval?: number;
|
|
32
88
|
/** Intervalo do flush do buffer em ms (default: 30000) */
|
|
33
89
|
flushInterval?: number;
|
|
90
|
+
/** Timeout do fetch em ms (default: 10000) */
|
|
91
|
+
timeout?: number;
|
|
92
|
+
/** Max tentativas de retry (default: 3) */
|
|
93
|
+
maxRetries?: number;
|
|
34
94
|
/** Desabilitar em desenvolvimento (default: true) */
|
|
35
95
|
disableInDev?: boolean;
|
|
96
|
+
/** Taxa de amostragem 0.0-1.0 (default: 1.0). Heartbeats nunca são amostrados */
|
|
97
|
+
sampleRate?: number;
|
|
98
|
+
/** Hook chamado antes de enfileirar cada evento. Retorna null para dropar */
|
|
99
|
+
beforeSend?: BeforeSendHook;
|
|
100
|
+
/** Habilitar logs de debug no console (default: false) */
|
|
101
|
+
debug?: boolean;
|
|
102
|
+
/** Capturar erros globais automaticamente — window.onerror (default: false) */
|
|
103
|
+
captureErrors?: boolean;
|
|
104
|
+
/** Capturar unhandled promise rejections automaticamente (default: false) */
|
|
105
|
+
captureUnhandledRejections?: boolean;
|
|
36
106
|
}
|
|
37
107
|
declare class MonitorClient {
|
|
38
108
|
readonly config: MonitorConfig;
|
|
39
|
-
readonly collector
|
|
109
|
+
private readonly collector;
|
|
110
|
+
private readonly logger;
|
|
40
111
|
private heartbeatTimer;
|
|
112
|
+
private globalHandlersCleanup;
|
|
41
113
|
private active;
|
|
42
114
|
constructor(config: MonitorConfig);
|
|
43
115
|
/** Inicia o monitoring (heartbeat + collector) */
|
|
44
116
|
start(): void;
|
|
45
117
|
/** Para o monitoring */
|
|
46
118
|
stop(): void;
|
|
119
|
+
/** Força um flush imediato do buffer */
|
|
120
|
+
flush(): void;
|
|
121
|
+
/** Verifica se o monitoring está ativo */
|
|
122
|
+
get isActive(): boolean;
|
|
47
123
|
/** Registra um evento de request HTTP (usado pelo middleware) */
|
|
48
|
-
trackRequest(data:
|
|
49
|
-
method: string;
|
|
50
|
-
route: string;
|
|
51
|
-
statusCode: number;
|
|
52
|
-
responseTimeMs: number;
|
|
53
|
-
userAgent?: string;
|
|
54
|
-
region?: string;
|
|
55
|
-
}): void;
|
|
124
|
+
trackRequest(data: RequestData): void;
|
|
56
125
|
/** Registra um Web Vital (usado pelo MonitorScript) */
|
|
57
|
-
trackVital(data:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}): void;
|
|
126
|
+
trackVital(data: VitalData): void;
|
|
127
|
+
/** Registra um evento de adapter (DB, cache, AI, queue, email) */
|
|
128
|
+
trackAdapter(data: AdapterData): void;
|
|
129
|
+
/** Registra um erro capturado */
|
|
130
|
+
captureError(data: ErrorData): void;
|
|
131
|
+
/** Registra um evento de deploy */
|
|
132
|
+
trackDeploy(data: DeployData): void;
|
|
65
133
|
private startHeartbeat;
|
|
66
134
|
private sendHeartbeat;
|
|
67
|
-
private isDev;
|
|
68
135
|
}
|
|
69
136
|
|
|
70
137
|
interface MonitorScriptProps {
|
|
@@ -76,7 +143,6 @@ interface MonitorScriptProps {
|
|
|
76
143
|
*
|
|
77
144
|
* @example
|
|
78
145
|
* ```tsx
|
|
79
|
-
* // app/layout.tsx
|
|
80
146
|
* import { MonitorScript } from '@victor-studio/monitor/react'
|
|
81
147
|
* import { monitor } from '@/lib/monitor'
|
|
82
148
|
*
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,70 +1,137 @@
|
|
|
1
|
-
interface
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
timestamp: string;
|
|
1
|
+
interface HeartbeatData {
|
|
2
|
+
status: 'online' | 'offline';
|
|
3
|
+
latencyMs: number;
|
|
5
4
|
}
|
|
6
|
-
interface
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
interface RequestData {
|
|
6
|
+
method: string;
|
|
7
|
+
route: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
responseTimeMs: number;
|
|
10
|
+
userAgent?: string;
|
|
11
|
+
region?: string;
|
|
9
12
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
interface VitalData {
|
|
14
|
+
name: 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB';
|
|
15
|
+
value: number;
|
|
16
|
+
rating: 'good' | 'needs-improvement' | 'poor';
|
|
17
|
+
page?: string;
|
|
18
|
+
deviceType?: string;
|
|
19
|
+
browser?: string;
|
|
20
|
+
}
|
|
21
|
+
interface ErrorData {
|
|
22
|
+
type: 'unhandled' | 'caught' | 'promise';
|
|
23
|
+
message: string;
|
|
24
|
+
stack?: string;
|
|
25
|
+
groupingKey: string;
|
|
26
|
+
route?: string;
|
|
27
|
+
method?: string;
|
|
28
|
+
statusCode?: number;
|
|
29
|
+
environment?: string;
|
|
30
|
+
commitHash?: string;
|
|
31
|
+
extra?: Record<string, string>;
|
|
32
|
+
}
|
|
33
|
+
interface DeployData {
|
|
34
|
+
commitHash: string;
|
|
35
|
+
branch?: string;
|
|
36
|
+
author?: string;
|
|
37
|
+
status: 'started' | 'succeeded' | 'failed';
|
|
38
|
+
buildDurationMs?: number;
|
|
39
|
+
url?: string;
|
|
40
|
+
environment?: string;
|
|
41
|
+
provider?: string;
|
|
21
42
|
}
|
|
43
|
+
interface AdapterData {
|
|
44
|
+
adapter: string;
|
|
45
|
+
operation: string;
|
|
46
|
+
durationMs: number;
|
|
47
|
+
success: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
meta?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
type MonitorEvent = {
|
|
52
|
+
type: 'heartbeat';
|
|
53
|
+
data: HeartbeatData;
|
|
54
|
+
timestamp: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'request';
|
|
57
|
+
data: RequestData;
|
|
58
|
+
timestamp: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'vital';
|
|
61
|
+
data: VitalData;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'error';
|
|
65
|
+
data: ErrorData;
|
|
66
|
+
timestamp: string;
|
|
67
|
+
} | {
|
|
68
|
+
type: 'deploy';
|
|
69
|
+
data: DeployData;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
} | {
|
|
72
|
+
type: 'adapter';
|
|
73
|
+
data: AdapterData;
|
|
74
|
+
timestamp: string;
|
|
75
|
+
};
|
|
76
|
+
/** Hook para filtrar/modificar eventos antes do envio */
|
|
77
|
+
type BeforeSendHook = (event: MonitorEvent) => MonitorEvent | null;
|
|
22
78
|
|
|
23
79
|
interface MonitorConfig {
|
|
24
80
|
/** ID do projeto no Nuvio */
|
|
25
81
|
projectId: string;
|
|
26
82
|
/** API key gerada no Nuvio */
|
|
27
83
|
apiKey: string;
|
|
28
|
-
/** URL do endpoint de ingest
|
|
84
|
+
/** URL do endpoint de ingest */
|
|
29
85
|
endpoint: string;
|
|
30
86
|
/** Intervalo do heartbeat em ms (default: 60000) */
|
|
31
87
|
heartbeatInterval?: number;
|
|
32
88
|
/** Intervalo do flush do buffer em ms (default: 30000) */
|
|
33
89
|
flushInterval?: number;
|
|
90
|
+
/** Timeout do fetch em ms (default: 10000) */
|
|
91
|
+
timeout?: number;
|
|
92
|
+
/** Max tentativas de retry (default: 3) */
|
|
93
|
+
maxRetries?: number;
|
|
34
94
|
/** Desabilitar em desenvolvimento (default: true) */
|
|
35
95
|
disableInDev?: boolean;
|
|
96
|
+
/** Taxa de amostragem 0.0-1.0 (default: 1.0). Heartbeats nunca são amostrados */
|
|
97
|
+
sampleRate?: number;
|
|
98
|
+
/** Hook chamado antes de enfileirar cada evento. Retorna null para dropar */
|
|
99
|
+
beforeSend?: BeforeSendHook;
|
|
100
|
+
/** Habilitar logs de debug no console (default: false) */
|
|
101
|
+
debug?: boolean;
|
|
102
|
+
/** Capturar erros globais automaticamente — window.onerror (default: false) */
|
|
103
|
+
captureErrors?: boolean;
|
|
104
|
+
/** Capturar unhandled promise rejections automaticamente (default: false) */
|
|
105
|
+
captureUnhandledRejections?: boolean;
|
|
36
106
|
}
|
|
37
107
|
declare class MonitorClient {
|
|
38
108
|
readonly config: MonitorConfig;
|
|
39
|
-
readonly collector
|
|
109
|
+
private readonly collector;
|
|
110
|
+
private readonly logger;
|
|
40
111
|
private heartbeatTimer;
|
|
112
|
+
private globalHandlersCleanup;
|
|
41
113
|
private active;
|
|
42
114
|
constructor(config: MonitorConfig);
|
|
43
115
|
/** Inicia o monitoring (heartbeat + collector) */
|
|
44
116
|
start(): void;
|
|
45
117
|
/** Para o monitoring */
|
|
46
118
|
stop(): void;
|
|
119
|
+
/** Força um flush imediato do buffer */
|
|
120
|
+
flush(): void;
|
|
121
|
+
/** Verifica se o monitoring está ativo */
|
|
122
|
+
get isActive(): boolean;
|
|
47
123
|
/** Registra um evento de request HTTP (usado pelo middleware) */
|
|
48
|
-
trackRequest(data:
|
|
49
|
-
method: string;
|
|
50
|
-
route: string;
|
|
51
|
-
statusCode: number;
|
|
52
|
-
responseTimeMs: number;
|
|
53
|
-
userAgent?: string;
|
|
54
|
-
region?: string;
|
|
55
|
-
}): void;
|
|
124
|
+
trackRequest(data: RequestData): void;
|
|
56
125
|
/** Registra um Web Vital (usado pelo MonitorScript) */
|
|
57
|
-
trackVital(data:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}): void;
|
|
126
|
+
trackVital(data: VitalData): void;
|
|
127
|
+
/** Registra um evento de adapter (DB, cache, AI, queue, email) */
|
|
128
|
+
trackAdapter(data: AdapterData): void;
|
|
129
|
+
/** Registra um erro capturado */
|
|
130
|
+
captureError(data: ErrorData): void;
|
|
131
|
+
/** Registra um evento de deploy */
|
|
132
|
+
trackDeploy(data: DeployData): void;
|
|
65
133
|
private startHeartbeat;
|
|
66
134
|
private sendHeartbeat;
|
|
67
|
-
private isDev;
|
|
68
135
|
}
|
|
69
136
|
|
|
70
137
|
interface MonitorScriptProps {
|
|
@@ -76,7 +143,6 @@ interface MonitorScriptProps {
|
|
|
76
143
|
*
|
|
77
144
|
* @example
|
|
78
145
|
* ```tsx
|
|
79
|
-
* // app/layout.tsx
|
|
80
146
|
* import { MonitorScript } from '@victor-studio/monitor/react'
|
|
81
147
|
* import { monitor } from '@/lib/monitor'
|
|
82
148
|
*
|
package/dist/react/index.js
CHANGED
|
@@ -3,6 +3,10 @@ import { useEffect } from 'react';
|
|
|
3
3
|
// src/react/monitor-script.tsx
|
|
4
4
|
function detectDeviceType() {
|
|
5
5
|
if (typeof window === "undefined") return "unknown";
|
|
6
|
+
const uaData = navigator.userAgentData;
|
|
7
|
+
if (uaData) {
|
|
8
|
+
return uaData.mobile ? "mobile" : "desktop";
|
|
9
|
+
}
|
|
6
10
|
const ua = navigator.userAgent;
|
|
7
11
|
if (/Mobile|Android/i.test(ua)) return "mobile";
|
|
8
12
|
if (/Tablet|iPad/i.test(ua)) return "tablet";
|
|
@@ -10,20 +14,32 @@ function detectDeviceType() {
|
|
|
10
14
|
}
|
|
11
15
|
function detectBrowser() {
|
|
12
16
|
if (typeof window === "undefined") return "unknown";
|
|
17
|
+
const uaData = navigator.userAgentData;
|
|
18
|
+
if (uaData?.brands?.length) {
|
|
19
|
+
const dominated = uaData.brands.find(
|
|
20
|
+
(b) => !b.brand.startsWith("Not") && !b.brand.startsWith("Chromium")
|
|
21
|
+
);
|
|
22
|
+
if (dominated) return dominated.brand;
|
|
23
|
+
}
|
|
13
24
|
const ua = navigator.userAgent;
|
|
14
25
|
if (ua.includes("Firefox")) return "Firefox";
|
|
15
26
|
if (ua.includes("Edg/")) return "Edge";
|
|
27
|
+
if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
|
|
28
|
+
if (ua.includes("SamsungBrowser")) return "Samsung Browser";
|
|
29
|
+
if (ua.includes("Brave")) return "Brave";
|
|
16
30
|
if (ua.includes("Chrome")) return "Chrome";
|
|
17
31
|
if (ua.includes("Safari")) return "Safari";
|
|
18
32
|
return "other";
|
|
19
33
|
}
|
|
20
34
|
function MonitorScript({ monitor }) {
|
|
21
35
|
useEffect(() => {
|
|
36
|
+
let cancelled = false;
|
|
22
37
|
import('web-vitals').then(({ onLCP, onINP, onCLS, onFCP, onTTFB }) => {
|
|
23
|
-
|
|
38
|
+
if (cancelled) return;
|
|
24
39
|
const deviceType = detectDeviceType();
|
|
25
40
|
const browser = detectBrowser();
|
|
26
41
|
const report = (name, value, rating) => {
|
|
42
|
+
const page = typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
27
43
|
monitor.trackVital({ name, value, rating, page, deviceType, browser });
|
|
28
44
|
};
|
|
29
45
|
onLCP((metric) => report("LCP", metric.value, metric.rating));
|
|
@@ -32,6 +48,9 @@ function MonitorScript({ monitor }) {
|
|
|
32
48
|
onFCP((metric) => report("FCP", metric.value, metric.rating));
|
|
33
49
|
onTTFB((metric) => report("TTFB", metric.value, metric.rating));
|
|
34
50
|
});
|
|
51
|
+
return () => {
|
|
52
|
+
cancelled = true;
|
|
53
|
+
};
|
|
35
54
|
}, [monitor]);
|
|
36
55
|
return null;
|
|
37
56
|
}
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/monitor-script.tsx"],"names":[],"mappings":";;;AAWA,SAAS,gBAAA,GAA2B;AAClC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,SAAA;
|
|
1
|
+
{"version":3,"sources":["../../src/react/monitor-script.tsx"],"names":[],"mappings":";;;AAWA,SAAS,gBAAA,GAA2B;AAClC,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,SAAA;AAG1C,EAAA,MAAM,SAAU,SAAA,CAAmE,aAAA;AACnF,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,OAAO,MAAA,CAAO,SAAS,QAAA,GAAW,SAAA;AAAA,EACpC;AAGA,EAAA,MAAM,KAAK,SAAA,CAAU,SAAA;AACrB,EAAA,IAAI,iBAAA,CAAkB,IAAA,CAAK,EAAE,CAAA,EAAG,OAAO,QAAA;AACvC,EAAA,IAAI,cAAA,CAAe,IAAA,CAAK,EAAE,CAAA,EAAG,OAAO,QAAA;AACpC,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,aAAA,GAAwB;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,SAAA;AAG1C,EAAA,MAAM,SAAU,SAAA,CACb,aAAA;AACH,EAAA,IAAI,MAAA,EAAQ,QAAQ,MAAA,EAAQ;AAC1B,IAAA,MAAM,SAAA,GAAY,OAAO,MAAA,CAAO,IAAA;AAAA,MAC9B,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,KAAA,CAAM,UAAA,CAAW,KAAK,CAAA,IAAK,CAAC,CAAA,CAAE,KAAA,CAAM,UAAA,CAAW,UAAU;AAAA,KACrE;AACA,IAAA,IAAI,SAAA,SAAkB,SAAA,CAAU,KAAA;AAAA,EAClC;AAGA,EAAA,MAAM,KAAK,SAAA,CAAU,SAAA;AACrB,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,SAAS,CAAA,EAAG,OAAO,SAAA;AACnC,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,MAAA;AAChC,EAAA,IAAI,EAAA,CAAG,SAAS,MAAM,CAAA,IAAK,GAAG,QAAA,CAAS,OAAO,GAAG,OAAO,OAAA;AACxD,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,gBAAgB,CAAA,EAAG,OAAO,iBAAA;AAC1C,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,OAAO,CAAA,EAAG,OAAO,OAAA;AACjC,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,QAAA;AAClC,EAAA,IAAI,EAAA,CAAG,QAAA,CAAS,QAAQ,CAAA,EAAG,OAAO,QAAA;AAClC,EAAA,OAAO,OAAA;AACT;AAuBO,SAAS,aAAA,CAAc,EAAE,OAAA,EAAQ,EAAuB;AAC7D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAGhB,IAAA,OAAO,YAAY,CAAA,CAAE,IAAA,CAAK,CAAC,EAAE,OAAO,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,EAAO,KAAM;AACpE,MAAA,IAAI,SAAA,EAAW;AAEf,MAAA,MAAM,aAAa,gBAAA,EAAiB;AACpC,MAAA,MAAM,UAAU,aAAA,EAAc;AAE9B,MAAA,MAAM,MAAA,GAAS,CACb,IAAA,EACA,KAAA,EACA,MAAA,KACG;AAEH,QAAA,MAAM,OAAO,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,MAAA;AACxE,QAAA,OAAA,CAAQ,UAAA,CAAW,EAAE,IAAA,EAAM,KAAA,EAAO,QAAQ,IAAA,EAAM,UAAA,EAAY,SAAS,CAAA;AAAA,MACvE,CAAA;AAEA,MAAA,KAAA,CAAM,CAAC,WAAW,MAAA,CAAO,KAAA,EAAO,OAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAC,CAAA;AAC5D,MAAA,KAAA,CAAM,CAAC,WAAW,MAAA,CAAO,KAAA,EAAO,OAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAC,CAAA;AAC5D,MAAA,KAAA,CAAM,CAAC,WAAW,MAAA,CAAO,KAAA,EAAO,OAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAC,CAAA;AAC5D,MAAA,KAAA,CAAM,CAAC,WAAW,MAAA,CAAO,KAAA,EAAO,OAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAC,CAAA;AAC5D,MAAA,MAAA,CAAO,CAAC,WAAW,MAAA,CAAO,MAAA,EAAQ,OAAO,KAAA,EAAO,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,IAChE,CAAC,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AAAA,IACd,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,OAAO,IAAA;AACT","file":"index.js","sourcesContent":["// MonitorScript — componente React que coleta Web Vitals\n\n'use client'\n\nimport { useEffect } from 'react'\nimport type { MonitorClient } from '../core/client'\n\ninterface MonitorScriptProps {\n monitor: MonitorClient\n}\n\nfunction detectDeviceType(): string {\n if (typeof window === 'undefined') return 'unknown'\n\n // Preferir User-Agent Client Hints (API moderna)\n const uaData = (navigator as Navigator & { userAgentData?: { mobile?: boolean } }).userAgentData\n if (uaData) {\n return uaData.mobile ? 'mobile' : 'desktop'\n }\n\n // Fallback para UA string\n const ua = navigator.userAgent\n if (/Mobile|Android/i.test(ua)) return 'mobile'\n if (/Tablet|iPad/i.test(ua)) return 'tablet'\n return 'desktop'\n}\n\nfunction detectBrowser(): string {\n if (typeof window === 'undefined') return 'unknown'\n\n // Preferir User-Agent Client Hints\n const uaData = (navigator as Navigator & { userAgentData?: { brands?: { brand: string }[] } })\n .userAgentData\n if (uaData?.brands?.length) {\n const dominated = uaData.brands.find(\n (b) => !b.brand.startsWith('Not') && !b.brand.startsWith('Chromium'),\n )\n if (dominated) return dominated.brand\n }\n\n // Fallback para UA string (ordem importa — Chrome inclui Safari no UA)\n const ua = navigator.userAgent\n if (ua.includes('Firefox')) return 'Firefox'\n if (ua.includes('Edg/')) return 'Edge'\n if (ua.includes('OPR/') || ua.includes('Opera')) return 'Opera'\n if (ua.includes('SamsungBrowser')) return 'Samsung Browser'\n if (ua.includes('Brave')) return 'Brave'\n if (ua.includes('Chrome')) return 'Chrome'\n if (ua.includes('Safari')) return 'Safari'\n return 'other'\n}\n\n/**\n * Componente React que coleta Web Vitals automaticamente.\n * Coloque no root layout da aplicação.\n *\n * @example\n * ```tsx\n * import { MonitorScript } from '@victor-studio/monitor/react'\n * import { monitor } from '@/lib/monitor'\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * {children}\n * <MonitorScript monitor={monitor} />\n * </body>\n * </html>\n * )\n * }\n * ```\n */\nexport function MonitorScript({ monitor }: MonitorScriptProps) {\n useEffect(() => {\n let cancelled = false\n\n // Import dinâmico pra não aumentar o bundle\n import('web-vitals').then(({ onLCP, onINP, onCLS, onFCP, onTTFB }) => {\n if (cancelled) return\n\n const deviceType = detectDeviceType()\n const browser = detectBrowser()\n\n const report = (\n name: 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB',\n value: number,\n rating: 'good' | 'needs-improvement' | 'poor',\n ) => {\n // Captura page no momento do report, não no mount (fix SPA navigation)\n const page = typeof window !== 'undefined' ? window.location.pathname : undefined\n monitor.trackVital({ name, value, rating, page, deviceType, browser })\n }\n\n onLCP((metric) => report('LCP', metric.value, metric.rating))\n onINP((metric) => report('INP', metric.value, metric.rating))\n onCLS((metric) => report('CLS', metric.value, metric.rating))\n onFCP((metric) => report('FCP', metric.value, metric.rating))\n onTTFB((metric) => report('TTFB', metric.value, metric.rating))\n })\n\n return () => {\n cancelled = true\n }\n }, [monitor])\n\n return null\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@victor-studio/monitor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Lightweight monitoring SDK for Nuvio-managed projects",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"main": "./dist/index.cjs",
|
|
7
8
|
"module": "./dist/index.js",
|
|
8
9
|
"types": "./dist/index.d.ts",
|
|
@@ -21,6 +22,11 @@
|
|
|
21
22
|
"types": "./dist/react/index.d.ts",
|
|
22
23
|
"import": "./dist/react/index.js",
|
|
23
24
|
"require": "./dist/react/index.cjs"
|
|
25
|
+
},
|
|
26
|
+
"./errors": {
|
|
27
|
+
"types": "./dist/errors/index.d.ts",
|
|
28
|
+
"import": "./dist/errors/index.js",
|
|
29
|
+
"require": "./dist/errors/index.cjs"
|
|
24
30
|
}
|
|
25
31
|
},
|
|
26
32
|
"files": [
|
|
@@ -34,7 +40,8 @@
|
|
|
34
40
|
},
|
|
35
41
|
"peerDependencies": {
|
|
36
42
|
"next": ">=14",
|
|
37
|
-
"react": ">=18"
|
|
43
|
+
"react": ">=18",
|
|
44
|
+
"web-vitals": ">=4"
|
|
38
45
|
},
|
|
39
46
|
"peerDependenciesMeta": {
|
|
40
47
|
"next": {
|
|
@@ -42,15 +49,16 @@
|
|
|
42
49
|
},
|
|
43
50
|
"react": {
|
|
44
51
|
"optional": true
|
|
52
|
+
},
|
|
53
|
+
"web-vitals": {
|
|
54
|
+
"optional": true
|
|
45
55
|
}
|
|
46
56
|
},
|
|
47
57
|
"devDependencies": {
|
|
48
58
|
"@types/node": "^25.6.0",
|
|
49
|
-
"@types/react": "
|
|
59
|
+
"@types/react": ">=18",
|
|
50
60
|
"tsup": "^8.0.0",
|
|
51
|
-
"typescript": "^5.9.0"
|
|
52
|
-
},
|
|
53
|
-
"dependencies": {
|
|
61
|
+
"typescript": "^5.9.0",
|
|
54
62
|
"web-vitals": "^4.2.0"
|
|
55
63
|
},
|
|
56
64
|
"keywords": [
|
|
@@ -58,7 +66,9 @@
|
|
|
58
66
|
"sdk",
|
|
59
67
|
"web-vitals",
|
|
60
68
|
"uptime",
|
|
61
|
-
"performance"
|
|
69
|
+
"performance",
|
|
70
|
+
"heartbeat",
|
|
71
|
+
"apm"
|
|
62
72
|
],
|
|
63
73
|
"author": "Victor Studio",
|
|
64
74
|
"license": "MIT",
|