@victor-studio/monitor 0.4.0 → 0.5.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/adapters/bullmq/bullmq.cjs +85 -0
- package/dist/adapters/bullmq/bullmq.cjs.map +1 -0
- package/dist/adapters/bullmq/bullmq.d.cts +176 -0
- package/dist/adapters/bullmq/bullmq.d.ts +176 -0
- package/dist/adapters/bullmq/bullmq.js +82 -0
- package/dist/adapters/bullmq/bullmq.js.map +1 -0
- package/dist/adapters/gemini/gemini.cjs +65 -0
- package/dist/adapters/gemini/gemini.cjs.map +1 -0
- package/dist/adapters/gemini/gemini.d.cts +173 -0
- package/dist/adapters/gemini/gemini.d.ts +173 -0
- package/dist/adapters/gemini/gemini.js +62 -0
- package/dist/adapters/gemini/gemini.js.map +1 -0
- package/dist/adapters/index.cjs +56 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +154 -0
- package/dist/adapters/index.d.ts +154 -0
- package/dist/adapters/index.js +53 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/neon/neon.cjs +36 -0
- package/dist/adapters/neon/neon.cjs.map +1 -0
- package/dist/adapters/neon/neon.d.cts +151 -0
- package/dist/adapters/neon/neon.d.ts +151 -0
- package/dist/adapters/neon/neon.js +34 -0
- package/dist/adapters/neon/neon.js.map +1 -0
- package/dist/adapters/railway/railway.cjs +20 -0
- package/dist/adapters/railway/railway.cjs.map +1 -0
- package/dist/adapters/railway/railway.d.cts +23 -0
- package/dist/adapters/railway/railway.d.ts +23 -0
- package/dist/adapters/railway/railway.js +18 -0
- package/dist/adapters/railway/railway.js.map +1 -0
- package/dist/adapters/resend/resend.cjs +36 -0
- package/dist/adapters/resend/resend.cjs.map +1 -0
- package/dist/adapters/resend/resend.d.cts +157 -0
- package/dist/adapters/resend/resend.d.ts +157 -0
- package/dist/adapters/resend/resend.js +34 -0
- package/dist/adapters/resend/resend.js.map +1 -0
- package/dist/adapters/upstash/upstash.cjs +39 -0
- package/dist/adapters/upstash/upstash.cjs.map +1 -0
- package/dist/adapters/upstash/upstash.d.cts +150 -0
- package/dist/adapters/upstash/upstash.d.ts +150 -0
- package/dist/adapters/upstash/upstash.js +37 -0
- package/dist/adapters/upstash/upstash.js.map +1 -0
- package/dist/adapters/vercel/vercel.cjs +18 -0
- package/dist/adapters/vercel/vercel.cjs.map +1 -0
- package/dist/adapters/vercel/vercel.d.cts +23 -0
- package/dist/adapters/vercel/vercel.d.ts +23 -0
- package/dist/adapters/vercel/vercel.js +16 -0
- package/dist/adapters/vercel/vercel.js.map +1 -0
- package/dist/deploys/index.cjs +53 -0
- package/dist/deploys/index.cjs.map +1 -0
- package/dist/deploys/index.d.cts +174 -0
- package/dist/deploys/index.d.ts +174 -0
- package/dist/deploys/index.js +50 -0
- package/dist/deploys/index.js.map +1 -0
- package/package.json +46 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/adapters/core.ts
|
|
4
|
+
async function withTiming(monitor, adapter, operation, fn, meta) {
|
|
5
|
+
const start = performance.now();
|
|
6
|
+
try {
|
|
7
|
+
const result = await fn();
|
|
8
|
+
monitor.trackAdapter({
|
|
9
|
+
adapter,
|
|
10
|
+
operation,
|
|
11
|
+
durationMs: Math.round(performance.now() - start),
|
|
12
|
+
success: true,
|
|
13
|
+
meta
|
|
14
|
+
});
|
|
15
|
+
return result;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
monitor.trackAdapter({
|
|
18
|
+
adapter,
|
|
19
|
+
operation,
|
|
20
|
+
durationMs: Math.round(performance.now() - start),
|
|
21
|
+
success: false,
|
|
22
|
+
error: error instanceof Error ? error.message : String(error),
|
|
23
|
+
meta
|
|
24
|
+
});
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/adapters/bullmq.ts
|
|
30
|
+
function monitorJob(monitor, queue, jobName, fn, meta) {
|
|
31
|
+
return withTiming(monitor, "bullmq", "process", fn, {
|
|
32
|
+
queue,
|
|
33
|
+
jobName,
|
|
34
|
+
...meta
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function monitorWorker(monitor, worker, queueName) {
|
|
38
|
+
const onCompleted = (...args) => {
|
|
39
|
+
const job = args[0];
|
|
40
|
+
const durationMs = job?.processedOn && job?.finishedOn ? job.finishedOn - job.processedOn : 0;
|
|
41
|
+
monitor.trackAdapter({
|
|
42
|
+
adapter: "bullmq",
|
|
43
|
+
operation: "completed",
|
|
44
|
+
durationMs,
|
|
45
|
+
success: true,
|
|
46
|
+
meta: { queue: queueName, jobName: job?.name ?? "unknown" }
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const onFailed = (...args) => {
|
|
50
|
+
const job = args[0];
|
|
51
|
+
const err = args[1];
|
|
52
|
+
monitor.trackAdapter({
|
|
53
|
+
adapter: "bullmq",
|
|
54
|
+
operation: "failed",
|
|
55
|
+
durationMs: 0,
|
|
56
|
+
success: false,
|
|
57
|
+
error: err?.message ?? "unknown error",
|
|
58
|
+
meta: { queue: queueName, jobName: job?.name ?? "unknown" }
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const onStalled = (...args) => {
|
|
62
|
+
const jobId = args[0];
|
|
63
|
+
monitor.trackAdapter({
|
|
64
|
+
adapter: "bullmq",
|
|
65
|
+
operation: "stalled",
|
|
66
|
+
durationMs: 0,
|
|
67
|
+
success: false,
|
|
68
|
+
error: "job stalled",
|
|
69
|
+
meta: { queue: queueName, jobId: jobId ?? "unknown" }
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
worker.on("completed", onCompleted);
|
|
73
|
+
worker.on("failed", onFailed);
|
|
74
|
+
worker.on("stalled", onStalled);
|
|
75
|
+
return () => {
|
|
76
|
+
worker.off("completed", onCompleted);
|
|
77
|
+
worker.off("failed", onFailed);
|
|
78
|
+
worker.off("stalled", onStalled);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
exports.monitorJob = monitorJob;
|
|
83
|
+
exports.monitorWorker = monitorWorker;
|
|
84
|
+
//# sourceMappingURL=bullmq.cjs.map
|
|
85
|
+
//# sourceMappingURL=bullmq.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/core.ts","../../../src/adapters/bullmq.ts"],"names":[],"mappings":"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","file":"bullmq.cjs","sourcesContent":["// Adapter core — utilitário genérico de instrumentação\n\nimport type { MonitorClient } from '../core/client'\nimport type { AdapterData } from '../types'\n\n/**\n * Mede a duração de uma operação assíncrona e reporta ao monitor.\n * Utilitário base usado por todos os adapters.\n *\n * @example\n * ```ts\n * const result = await withTiming(monitor, 'neon', 'query', () =>\n * db.select().from(users),\n * )\n * ```\n */\nexport async function withTiming<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n const start = performance.now()\n\n try {\n const result = await fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error // nunca engolir erros\n }\n}\n\n/**\n * Versão sync do withTiming para operações síncronas.\n */\nexport function withTimingSync<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => T,\n meta?: Record<string, string>,\n): T {\n const start = performance.now()\n\n try {\n const result = fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error\n }\n}\n","// Adapter BullMQ — instrumentação de jobs e filas\n\nimport type { MonitorClient } from '../core/client'\nimport { withTiming } from './core'\n\n/**\n * Instrumenta o processamento de um job BullMQ com timing.\n *\n * @example\n * ```ts\n * import { monitorJob } from '@victor-studio/monitor/adapters/bullmq'\n *\n * worker.on('completed', (job) => {\n * // Para tracking manual de jobs já processados\n * })\n *\n * // Ou wrap o processamento inteiro:\n * const result = await monitorJob(monitor, 'email-queue', 'send-welcome', () =>\n * processJob(jobData),\n * )\n * ```\n */\nexport function monitorJob<T>(\n monitor: MonitorClient,\n queue: string,\n jobName: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n return withTiming(monitor, 'bullmq', 'process', fn, {\n queue,\n jobName,\n ...meta,\n })\n}\n\n/**\n * Registra event listeners no Worker do BullMQ para tracking automático.\n * Retorna função de cleanup para remover os listeners.\n *\n * @example\n * ```ts\n * import { monitorWorker } from '@victor-studio/monitor/adapters/bullmq'\n * import { Worker } from 'bullmq'\n *\n * const worker = new Worker('email-queue', processor, { connection })\n * const cleanup = monitorWorker(monitor, worker, 'email-queue')\n *\n * // Quando quiser parar:\n * cleanup()\n * ```\n */\nexport function monitorWorker(\n monitor: MonitorClient,\n worker: {\n on: (event: string, listener: (...args: unknown[]) => void) => void\n off: (event: string, listener: (...args: unknown[]) => void) => void\n },\n queueName: string,\n): () => void {\n const onCompleted = (...args: unknown[]) => {\n const job = args[0] as { name?: string; processedOn?: number; finishedOn?: number } | undefined\n const durationMs =\n job?.processedOn && job?.finishedOn ? job.finishedOn - job.processedOn : 0\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'completed',\n durationMs,\n success: true,\n meta: { queue: queueName, jobName: job?.name ?? 'unknown' },\n })\n }\n\n const onFailed = (...args: unknown[]) => {\n const job = args[0] as { name?: string; processedOn?: number; finishedOn?: number } | undefined\n const err = args[1] as Error | undefined\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'failed',\n durationMs: 0,\n success: false,\n error: err?.message ?? 'unknown error',\n meta: { queue: queueName, jobName: job?.name ?? 'unknown' },\n })\n }\n\n const onStalled = (...args: unknown[]) => {\n const jobId = args[0] as string | undefined\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'stalled',\n durationMs: 0,\n success: false,\n error: 'job stalled',\n meta: { queue: queueName, jobId: jobId ?? 'unknown' },\n })\n }\n\n worker.on('completed', onCompleted)\n worker.on('failed', onFailed)\n worker.on('stalled', onStalled)\n\n return () => {\n worker.off('completed', onCompleted)\n worker.off('failed', onFailed)\n worker.off('stalled', onStalled)\n }\n}\n"]}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
interface HeartbeatData {
|
|
2
|
+
status: 'online' | 'offline';
|
|
3
|
+
latencyMs: number;
|
|
4
|
+
}
|
|
5
|
+
interface RequestData {
|
|
6
|
+
method: string;
|
|
7
|
+
route: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
responseTimeMs: number;
|
|
10
|
+
userAgent?: string;
|
|
11
|
+
region?: string;
|
|
12
|
+
}
|
|
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;
|
|
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;
|
|
78
|
+
|
|
79
|
+
interface MonitorConfig {
|
|
80
|
+
/** ID do projeto no Nuvio */
|
|
81
|
+
projectId: string;
|
|
82
|
+
/** API key gerada no Nuvio */
|
|
83
|
+
apiKey: string;
|
|
84
|
+
/** URL do endpoint de ingest */
|
|
85
|
+
endpoint: string;
|
|
86
|
+
/** Intervalo do heartbeat em ms (default: 60000) */
|
|
87
|
+
heartbeatInterval?: number;
|
|
88
|
+
/** Intervalo do flush do buffer em ms (default: 30000) */
|
|
89
|
+
flushInterval?: number;
|
|
90
|
+
/** Timeout do fetch em ms (default: 10000) */
|
|
91
|
+
timeout?: number;
|
|
92
|
+
/** Max tentativas de retry (default: 3) */
|
|
93
|
+
maxRetries?: number;
|
|
94
|
+
/** Desabilitar em desenvolvimento (default: true) */
|
|
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;
|
|
106
|
+
}
|
|
107
|
+
declare class MonitorClient {
|
|
108
|
+
readonly config: MonitorConfig;
|
|
109
|
+
private readonly collector;
|
|
110
|
+
private readonly logger;
|
|
111
|
+
private heartbeatTimer;
|
|
112
|
+
private globalHandlersCleanup;
|
|
113
|
+
private active;
|
|
114
|
+
constructor(config: MonitorConfig);
|
|
115
|
+
/** Inicia o monitoring (heartbeat + collector) */
|
|
116
|
+
start(): void;
|
|
117
|
+
/** Para o monitoring */
|
|
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;
|
|
123
|
+
/** Registra um evento de request HTTP (usado pelo middleware) */
|
|
124
|
+
trackRequest(data: RequestData): void;
|
|
125
|
+
/** Registra um Web Vital (usado pelo MonitorScript) */
|
|
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;
|
|
133
|
+
private startHeartbeat;
|
|
134
|
+
private sendHeartbeat;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Instrumenta o processamento de um job BullMQ com timing.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { monitorJob } from '@victor-studio/monitor/adapters/bullmq'
|
|
143
|
+
*
|
|
144
|
+
* worker.on('completed', (job) => {
|
|
145
|
+
* // Para tracking manual de jobs já processados
|
|
146
|
+
* })
|
|
147
|
+
*
|
|
148
|
+
* // Ou wrap o processamento inteiro:
|
|
149
|
+
* const result = await monitorJob(monitor, 'email-queue', 'send-welcome', () =>
|
|
150
|
+
* processJob(jobData),
|
|
151
|
+
* )
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
declare function monitorJob<T>(monitor: MonitorClient, queue: string, jobName: string, fn: () => Promise<T>, meta?: Record<string, string>): Promise<T>;
|
|
155
|
+
/**
|
|
156
|
+
* Registra event listeners no Worker do BullMQ para tracking automático.
|
|
157
|
+
* Retorna função de cleanup para remover os listeners.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* import { monitorWorker } from '@victor-studio/monitor/adapters/bullmq'
|
|
162
|
+
* import { Worker } from 'bullmq'
|
|
163
|
+
*
|
|
164
|
+
* const worker = new Worker('email-queue', processor, { connection })
|
|
165
|
+
* const cleanup = monitorWorker(monitor, worker, 'email-queue')
|
|
166
|
+
*
|
|
167
|
+
* // Quando quiser parar:
|
|
168
|
+
* cleanup()
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
declare function monitorWorker(monitor: MonitorClient, worker: {
|
|
172
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
173
|
+
off: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
174
|
+
}, queueName: string): () => void;
|
|
175
|
+
|
|
176
|
+
export { monitorJob, monitorWorker };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
interface HeartbeatData {
|
|
2
|
+
status: 'online' | 'offline';
|
|
3
|
+
latencyMs: number;
|
|
4
|
+
}
|
|
5
|
+
interface RequestData {
|
|
6
|
+
method: string;
|
|
7
|
+
route: string;
|
|
8
|
+
statusCode: number;
|
|
9
|
+
responseTimeMs: number;
|
|
10
|
+
userAgent?: string;
|
|
11
|
+
region?: string;
|
|
12
|
+
}
|
|
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;
|
|
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;
|
|
78
|
+
|
|
79
|
+
interface MonitorConfig {
|
|
80
|
+
/** ID do projeto no Nuvio */
|
|
81
|
+
projectId: string;
|
|
82
|
+
/** API key gerada no Nuvio */
|
|
83
|
+
apiKey: string;
|
|
84
|
+
/** URL do endpoint de ingest */
|
|
85
|
+
endpoint: string;
|
|
86
|
+
/** Intervalo do heartbeat em ms (default: 60000) */
|
|
87
|
+
heartbeatInterval?: number;
|
|
88
|
+
/** Intervalo do flush do buffer em ms (default: 30000) */
|
|
89
|
+
flushInterval?: number;
|
|
90
|
+
/** Timeout do fetch em ms (default: 10000) */
|
|
91
|
+
timeout?: number;
|
|
92
|
+
/** Max tentativas de retry (default: 3) */
|
|
93
|
+
maxRetries?: number;
|
|
94
|
+
/** Desabilitar em desenvolvimento (default: true) */
|
|
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;
|
|
106
|
+
}
|
|
107
|
+
declare class MonitorClient {
|
|
108
|
+
readonly config: MonitorConfig;
|
|
109
|
+
private readonly collector;
|
|
110
|
+
private readonly logger;
|
|
111
|
+
private heartbeatTimer;
|
|
112
|
+
private globalHandlersCleanup;
|
|
113
|
+
private active;
|
|
114
|
+
constructor(config: MonitorConfig);
|
|
115
|
+
/** Inicia o monitoring (heartbeat + collector) */
|
|
116
|
+
start(): void;
|
|
117
|
+
/** Para o monitoring */
|
|
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;
|
|
123
|
+
/** Registra um evento de request HTTP (usado pelo middleware) */
|
|
124
|
+
trackRequest(data: RequestData): void;
|
|
125
|
+
/** Registra um Web Vital (usado pelo MonitorScript) */
|
|
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;
|
|
133
|
+
private startHeartbeat;
|
|
134
|
+
private sendHeartbeat;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Instrumenta o processamento de um job BullMQ com timing.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* import { monitorJob } from '@victor-studio/monitor/adapters/bullmq'
|
|
143
|
+
*
|
|
144
|
+
* worker.on('completed', (job) => {
|
|
145
|
+
* // Para tracking manual de jobs já processados
|
|
146
|
+
* })
|
|
147
|
+
*
|
|
148
|
+
* // Ou wrap o processamento inteiro:
|
|
149
|
+
* const result = await monitorJob(monitor, 'email-queue', 'send-welcome', () =>
|
|
150
|
+
* processJob(jobData),
|
|
151
|
+
* )
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
declare function monitorJob<T>(monitor: MonitorClient, queue: string, jobName: string, fn: () => Promise<T>, meta?: Record<string, string>): Promise<T>;
|
|
155
|
+
/**
|
|
156
|
+
* Registra event listeners no Worker do BullMQ para tracking automático.
|
|
157
|
+
* Retorna função de cleanup para remover os listeners.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* import { monitorWorker } from '@victor-studio/monitor/adapters/bullmq'
|
|
162
|
+
* import { Worker } from 'bullmq'
|
|
163
|
+
*
|
|
164
|
+
* const worker = new Worker('email-queue', processor, { connection })
|
|
165
|
+
* const cleanup = monitorWorker(monitor, worker, 'email-queue')
|
|
166
|
+
*
|
|
167
|
+
* // Quando quiser parar:
|
|
168
|
+
* cleanup()
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
declare function monitorWorker(monitor: MonitorClient, worker: {
|
|
172
|
+
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
173
|
+
off: (event: string, listener: (...args: unknown[]) => void) => void;
|
|
174
|
+
}, queueName: string): () => void;
|
|
175
|
+
|
|
176
|
+
export { monitorJob, monitorWorker };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/adapters/core.ts
|
|
2
|
+
async function withTiming(monitor, adapter, operation, fn, meta) {
|
|
3
|
+
const start = performance.now();
|
|
4
|
+
try {
|
|
5
|
+
const result = await fn();
|
|
6
|
+
monitor.trackAdapter({
|
|
7
|
+
adapter,
|
|
8
|
+
operation,
|
|
9
|
+
durationMs: Math.round(performance.now() - start),
|
|
10
|
+
success: true,
|
|
11
|
+
meta
|
|
12
|
+
});
|
|
13
|
+
return result;
|
|
14
|
+
} catch (error) {
|
|
15
|
+
monitor.trackAdapter({
|
|
16
|
+
adapter,
|
|
17
|
+
operation,
|
|
18
|
+
durationMs: Math.round(performance.now() - start),
|
|
19
|
+
success: false,
|
|
20
|
+
error: error instanceof Error ? error.message : String(error),
|
|
21
|
+
meta
|
|
22
|
+
});
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/adapters/bullmq.ts
|
|
28
|
+
function monitorJob(monitor, queue, jobName, fn, meta) {
|
|
29
|
+
return withTiming(monitor, "bullmq", "process", fn, {
|
|
30
|
+
queue,
|
|
31
|
+
jobName,
|
|
32
|
+
...meta
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function monitorWorker(monitor, worker, queueName) {
|
|
36
|
+
const onCompleted = (...args) => {
|
|
37
|
+
const job = args[0];
|
|
38
|
+
const durationMs = job?.processedOn && job?.finishedOn ? job.finishedOn - job.processedOn : 0;
|
|
39
|
+
monitor.trackAdapter({
|
|
40
|
+
adapter: "bullmq",
|
|
41
|
+
operation: "completed",
|
|
42
|
+
durationMs,
|
|
43
|
+
success: true,
|
|
44
|
+
meta: { queue: queueName, jobName: job?.name ?? "unknown" }
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const onFailed = (...args) => {
|
|
48
|
+
const job = args[0];
|
|
49
|
+
const err = args[1];
|
|
50
|
+
monitor.trackAdapter({
|
|
51
|
+
adapter: "bullmq",
|
|
52
|
+
operation: "failed",
|
|
53
|
+
durationMs: 0,
|
|
54
|
+
success: false,
|
|
55
|
+
error: err?.message ?? "unknown error",
|
|
56
|
+
meta: { queue: queueName, jobName: job?.name ?? "unknown" }
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const onStalled = (...args) => {
|
|
60
|
+
const jobId = args[0];
|
|
61
|
+
monitor.trackAdapter({
|
|
62
|
+
adapter: "bullmq",
|
|
63
|
+
operation: "stalled",
|
|
64
|
+
durationMs: 0,
|
|
65
|
+
success: false,
|
|
66
|
+
error: "job stalled",
|
|
67
|
+
meta: { queue: queueName, jobId: jobId ?? "unknown" }
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
worker.on("completed", onCompleted);
|
|
71
|
+
worker.on("failed", onFailed);
|
|
72
|
+
worker.on("stalled", onStalled);
|
|
73
|
+
return () => {
|
|
74
|
+
worker.off("completed", onCompleted);
|
|
75
|
+
worker.off("failed", onFailed);
|
|
76
|
+
worker.off("stalled", onStalled);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { monitorJob, monitorWorker };
|
|
81
|
+
//# sourceMappingURL=bullmq.js.map
|
|
82
|
+
//# sourceMappingURL=bullmq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/core.ts","../../../src/adapters/bullmq.ts"],"names":[],"mappings":"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","file":"bullmq.js","sourcesContent":["// Adapter core — utilitário genérico de instrumentação\n\nimport type { MonitorClient } from '../core/client'\nimport type { AdapterData } from '../types'\n\n/**\n * Mede a duração de uma operação assíncrona e reporta ao monitor.\n * Utilitário base usado por todos os adapters.\n *\n * @example\n * ```ts\n * const result = await withTiming(monitor, 'neon', 'query', () =>\n * db.select().from(users),\n * )\n * ```\n */\nexport async function withTiming<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n const start = performance.now()\n\n try {\n const result = await fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error // nunca engolir erros\n }\n}\n\n/**\n * Versão sync do withTiming para operações síncronas.\n */\nexport function withTimingSync<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => T,\n meta?: Record<string, string>,\n): T {\n const start = performance.now()\n\n try {\n const result = fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error\n }\n}\n","// Adapter BullMQ — instrumentação de jobs e filas\n\nimport type { MonitorClient } from '../core/client'\nimport { withTiming } from './core'\n\n/**\n * Instrumenta o processamento de um job BullMQ com timing.\n *\n * @example\n * ```ts\n * import { monitorJob } from '@victor-studio/monitor/adapters/bullmq'\n *\n * worker.on('completed', (job) => {\n * // Para tracking manual de jobs já processados\n * })\n *\n * // Ou wrap o processamento inteiro:\n * const result = await monitorJob(monitor, 'email-queue', 'send-welcome', () =>\n * processJob(jobData),\n * )\n * ```\n */\nexport function monitorJob<T>(\n monitor: MonitorClient,\n queue: string,\n jobName: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n return withTiming(monitor, 'bullmq', 'process', fn, {\n queue,\n jobName,\n ...meta,\n })\n}\n\n/**\n * Registra event listeners no Worker do BullMQ para tracking automático.\n * Retorna função de cleanup para remover os listeners.\n *\n * @example\n * ```ts\n * import { monitorWorker } from '@victor-studio/monitor/adapters/bullmq'\n * import { Worker } from 'bullmq'\n *\n * const worker = new Worker('email-queue', processor, { connection })\n * const cleanup = monitorWorker(monitor, worker, 'email-queue')\n *\n * // Quando quiser parar:\n * cleanup()\n * ```\n */\nexport function monitorWorker(\n monitor: MonitorClient,\n worker: {\n on: (event: string, listener: (...args: unknown[]) => void) => void\n off: (event: string, listener: (...args: unknown[]) => void) => void\n },\n queueName: string,\n): () => void {\n const onCompleted = (...args: unknown[]) => {\n const job = args[0] as { name?: string; processedOn?: number; finishedOn?: number } | undefined\n const durationMs =\n job?.processedOn && job?.finishedOn ? job.finishedOn - job.processedOn : 0\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'completed',\n durationMs,\n success: true,\n meta: { queue: queueName, jobName: job?.name ?? 'unknown' },\n })\n }\n\n const onFailed = (...args: unknown[]) => {\n const job = args[0] as { name?: string; processedOn?: number; finishedOn?: number } | undefined\n const err = args[1] as Error | undefined\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'failed',\n durationMs: 0,\n success: false,\n error: err?.message ?? 'unknown error',\n meta: { queue: queueName, jobName: job?.name ?? 'unknown' },\n })\n }\n\n const onStalled = (...args: unknown[]) => {\n const jobId = args[0] as string | undefined\n\n monitor.trackAdapter({\n adapter: 'bullmq',\n operation: 'stalled',\n durationMs: 0,\n success: false,\n error: 'job stalled',\n meta: { queue: queueName, jobId: jobId ?? 'unknown' },\n })\n }\n\n worker.on('completed', onCompleted)\n worker.on('failed', onFailed)\n worker.on('stalled', onStalled)\n\n return () => {\n worker.off('completed', onCompleted)\n worker.off('failed', onFailed)\n worker.off('stalled', onStalled)\n }\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/adapters/core.ts
|
|
4
|
+
async function withTiming(monitor, adapter, operation, fn, meta) {
|
|
5
|
+
const start = performance.now();
|
|
6
|
+
try {
|
|
7
|
+
const result = await fn();
|
|
8
|
+
monitor.trackAdapter({
|
|
9
|
+
adapter,
|
|
10
|
+
operation,
|
|
11
|
+
durationMs: Math.round(performance.now() - start),
|
|
12
|
+
success: true,
|
|
13
|
+
meta
|
|
14
|
+
});
|
|
15
|
+
return result;
|
|
16
|
+
} catch (error) {
|
|
17
|
+
monitor.trackAdapter({
|
|
18
|
+
adapter,
|
|
19
|
+
operation,
|
|
20
|
+
durationMs: Math.round(performance.now() - start),
|
|
21
|
+
success: false,
|
|
22
|
+
error: error instanceof Error ? error.message : String(error),
|
|
23
|
+
meta
|
|
24
|
+
});
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/adapters/gemini.ts
|
|
30
|
+
function monitorAI(monitor, operation, fn, meta) {
|
|
31
|
+
return withTiming(monitor, "gemini", operation, fn, meta);
|
|
32
|
+
}
|
|
33
|
+
async function monitorAIWithTokens(monitor, operation, fn, meta) {
|
|
34
|
+
const start = performance.now();
|
|
35
|
+
try {
|
|
36
|
+
const { result, inputTokens, outputTokens } = await fn();
|
|
37
|
+
monitor.trackAdapter({
|
|
38
|
+
adapter: "gemini",
|
|
39
|
+
operation,
|
|
40
|
+
durationMs: Math.round(performance.now() - start),
|
|
41
|
+
success: true,
|
|
42
|
+
meta: {
|
|
43
|
+
...meta,
|
|
44
|
+
...inputTokens !== void 0 ? { inputTokens: String(inputTokens) } : {},
|
|
45
|
+
...outputTokens !== void 0 ? { outputTokens: String(outputTokens) } : {}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return result;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
monitor.trackAdapter({
|
|
51
|
+
adapter: "gemini",
|
|
52
|
+
operation,
|
|
53
|
+
durationMs: Math.round(performance.now() - start),
|
|
54
|
+
success: false,
|
|
55
|
+
error: error instanceof Error ? error.message : String(error),
|
|
56
|
+
meta
|
|
57
|
+
});
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exports.monitorAI = monitorAI;
|
|
63
|
+
exports.monitorAIWithTokens = monitorAIWithTokens;
|
|
64
|
+
//# sourceMappingURL=gemini.cjs.map
|
|
65
|
+
//# sourceMappingURL=gemini.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/core.ts","../../../src/adapters/gemini.ts"],"names":[],"mappings":";;;AAgBA,eAAsB,UAAA,CACpB,OAAA,EACA,OAAA,EACA,SAAA,EACA,IACA,IAAA,EACY;AACZ,EAAA,MAAM,KAAA,GAAQ,YAAY,GAAA,EAAI;AAE9B,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,MAAM,EAAA,EAAG;AAExB,IAAA,OAAA,CAAQ,YAAA,CAAa;AAAA,MACnB,OAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAY,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,KAAQ,KAAK,CAAA;AAAA,MAChD,OAAA,EAAS,IAAA;AAAA,MACT;AAAA,KACqB,CAAA;AAEvB,IAAA,OAAO,MAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,YAAA,CAAa;AAAA,MACnB,OAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAY,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,KAAQ,KAAK,CAAA;AAAA,MAChD,OAAA,EAAS,KAAA;AAAA,MACT,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAAA,MAC5D;AAAA,KACqB,CAAA;AAEvB,IAAA,MAAM,KAAA;AAAA,EACR;AACF;;;AC/BO,SAAS,SAAA,CACd,OAAA,EACA,SAAA,EACA,EAAA,EACA,IAAA,EACY;AACZ,EAAA,OAAO,UAAA,CAAW,OAAA,EAAS,QAAA,EAAU,SAAA,EAAW,IAAI,IAAI,CAAA;AAC1D;AAkBA,eAAsB,mBAAA,CACpB,OAAA,EACA,SAAA,EACA,EAAA,EACA,IAAA,EACY;AACZ,EAAA,MAAM,KAAA,GAAQ,YAAY,GAAA,EAAI;AAE9B,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,YAAA,EAAa,GAAI,MAAM,EAAA,EAAG;AAEvD,IAAA,OAAA,CAAQ,YAAA,CAAa;AAAA,MACnB,OAAA,EAAS,QAAA;AAAA,MACT,SAAA;AAAA,MACA,YAAY,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,KAAQ,KAAK,CAAA;AAAA,MAChD,OAAA,EAAS,IAAA;AAAA,MACT,IAAA,EAAM;AAAA,QACJ,GAAG,IAAA;AAAA,QACH,GAAI,gBAAgB,KAAA,CAAA,GAAY,EAAE,aAAa,MAAA,CAAO,WAAW,CAAA,EAAE,GAAI,EAAC;AAAA,QACxE,GAAI,iBAAiB,KAAA,CAAA,GAAY,EAAE,cAAc,MAAA,CAAO,YAAY,CAAA,EAAE,GAAI;AAAC;AAC7E,KACD,CAAA;AAED,IAAA,OAAO,MAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,OAAA,CAAQ,YAAA,CAAa;AAAA,MACnB,OAAA,EAAS,QAAA;AAAA,MACT,SAAA;AAAA,MACA,YAAY,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,KAAQ,KAAK,CAAA;AAAA,MAChD,OAAA,EAAS,KAAA;AAAA,MACT,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAAA,MAC5D;AAAA,KACD,CAAA;AAED,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"gemini.cjs","sourcesContent":["// Adapter core — utilitário genérico de instrumentação\n\nimport type { MonitorClient } from '../core/client'\nimport type { AdapterData } from '../types'\n\n/**\n * Mede a duração de uma operação assíncrona e reporta ao monitor.\n * Utilitário base usado por todos os adapters.\n *\n * @example\n * ```ts\n * const result = await withTiming(monitor, 'neon', 'query', () =>\n * db.select().from(users),\n * )\n * ```\n */\nexport async function withTiming<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n const start = performance.now()\n\n try {\n const result = await fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error // nunca engolir erros\n }\n}\n\n/**\n * Versão sync do withTiming para operações síncronas.\n */\nexport function withTimingSync<T>(\n monitor: MonitorClient,\n adapter: string,\n operation: string,\n fn: () => T,\n meta?: Record<string, string>,\n): T {\n const start = performance.now()\n\n try {\n const result = fn()\n\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta,\n } satisfies AdapterData)\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter,\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n } satisfies AdapterData)\n\n throw error\n }\n}\n","// Adapter Gemini — instrumentação de chamadas AI\n\nimport type { MonitorClient } from '../core/client'\nimport { withTiming } from './core'\n\n/**\n * Instrumenta uma chamada ao Gemini API com timing e token tracking.\n *\n * @example\n * ```ts\n * import { monitorAI } from '@victor-studio/monitor/adapters/gemini'\n *\n * const result = await monitorAI(monitor, 'generate', () =>\n * model.generateContent(prompt),\n * { model: 'gemini-2.5-flash' },\n * )\n * ```\n */\nexport function monitorAI<T>(\n monitor: MonitorClient,\n operation: string,\n fn: () => Promise<T>,\n meta?: Record<string, string>,\n): Promise<T> {\n return withTiming(monitor, 'gemini', operation, fn, meta)\n}\n\n/**\n * Instrumenta uma chamada AI e extrai token usage do resultado.\n * Específico para o formato de resposta do Google Generative AI SDK.\n *\n * @example\n * ```ts\n * const result = await monitorAIWithTokens(monitor, 'generate', async () => {\n * const res = await model.generateContent(prompt)\n * return {\n * result: res,\n * inputTokens: res.response.usageMetadata?.promptTokenCount,\n * outputTokens: res.response.usageMetadata?.candidatesTokenCount,\n * }\n * }, { model: 'gemini-2.5-flash' })\n * ```\n */\nexport async function monitorAIWithTokens<T>(\n monitor: MonitorClient,\n operation: string,\n fn: () => Promise<{ result: T; inputTokens?: number; outputTokens?: number }>,\n meta?: Record<string, string>,\n): Promise<T> {\n const start = performance.now()\n\n try {\n const { result, inputTokens, outputTokens } = await fn()\n\n monitor.trackAdapter({\n adapter: 'gemini',\n operation,\n durationMs: Math.round(performance.now() - start),\n success: true,\n meta: {\n ...meta,\n ...(inputTokens !== undefined ? { inputTokens: String(inputTokens) } : {}),\n ...(outputTokens !== undefined ? { outputTokens: String(outputTokens) } : {}),\n },\n })\n\n return result\n } catch (error) {\n monitor.trackAdapter({\n adapter: 'gemini',\n operation,\n durationMs: Math.round(performance.now() - start),\n success: false,\n error: error instanceof Error ? error.message : String(error),\n meta,\n })\n\n throw error\n }\n}\n"]}
|