@kb-labs/telemetry-client 2.13.0 → 2.14.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.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../Users/kirillbaranov/Desktop/kb-labs-workspace/core/telemetry-client/src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/telemetry-client\n * Lightweight telemetry client for KB Labs platform.\n *\n * Zero runtime dependencies. Works in Node.js and browser.\n * Batches events and flushes to the platform ingestion endpoint.\n *\n * @example\n * ```typescript\n * import { KBTelemetry } from '@kb-labs/telemetry-client';\n *\n * const telemetry = new KBTelemetry({\n * endpoint: 'http://localhost:4000',\n * apiKey: 'your-token',\n * source: 'my-product',\n * });\n *\n * telemetry.event('user.signup', { plan: 'pro' });\n * telemetry.event('api.request', { method: 'POST', path: '/checkout', durationMs: 142 });\n *\n * // Flush on shutdown (automatic in Node.js via process handlers)\n * await telemetry.flush();\n * ```\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────\n\nexport interface KBTelemetryOptions {\n /** Platform Gateway endpoint (e.g., \"http://localhost:4000\") */\n endpoint: string;\n /** API key / Bearer token for authentication */\n apiKey: string;\n /** Source product name — identifies who sent the event */\n source: string;\n /** Max events per batch (default: 50) */\n batchSize?: number;\n /** Auto-flush interval in ms (default: 5000). Set 0 to disable. */\n flushIntervalMs?: number;\n /** Max retry attempts for failed flushes (default: 2) */\n maxRetries?: number;\n /** Custom tags applied to all events from this client */\n defaultTags?: Record<string, string>;\n /** Called on flush errors (default: console.error) */\n onError?: (error: Error, events: TelemetryEvent[]) => void;\n}\n\nexport interface TelemetryEvent {\n source: string;\n type: string;\n timestamp?: string;\n payload?: Record<string, unknown>;\n tags?: Record<string, string>;\n}\n\ninterface IngestResponse {\n accepted: number;\n rejected: number;\n errors?: Array<{ index: number; message: string }>;\n}\n\n// ── Client ────────────────────────────────────────────────────────────────\n\nexport class KBTelemetry {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly source: string;\n private readonly batchSize: number;\n private readonly maxRetries: number;\n private readonly defaultTags: Record<string, string>;\n private readonly onError: (error: Error, events: TelemetryEvent[]) => void;\n\n private buffer: TelemetryEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(options: KBTelemetryOptions) {\n this.endpoint = options.endpoint.replace(/\\/+$/, '');\n this.apiKey = options.apiKey;\n this.source = options.source;\n this.batchSize = options.batchSize ?? 50;\n this.maxRetries = options.maxRetries ?? 2;\n this.defaultTags = options.defaultTags ?? {};\n this.onError = options.onError ?? ((err) => console.error('[KBTelemetry]', err.message));\n\n const interval = options.flushIntervalMs ?? 5000;\n if (interval > 0) {\n this.flushTimer = setInterval(() => { void this.flush(); }, interval);\n // Unref so the timer doesn't keep the process alive\n if (typeof this.flushTimer === 'object' && 'unref' in this.flushTimer) {\n this.flushTimer.unref();\n }\n }\n\n // Auto-flush on process exit (Node.js only)\n if (typeof process !== 'undefined' && process.on) {\n const exitHandler = () => { void this.shutdown(); };\n process.on('beforeExit', exitHandler);\n process.on('SIGTERM', exitHandler);\n process.on('SIGINT', exitHandler);\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────────────\n\n /** Track a named event with optional payload and tags. */\n event(type: string, payload?: Record<string, unknown>, tags?: Record<string, string>): void {\n this.buffer.push({\n source: this.source,\n type,\n timestamp: new Date().toISOString(),\n payload,\n tags: { ...this.defaultTags, ...tags },\n });\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n /** Convenience: track a metric value. */\n metric(name: string, value: number, tags?: Record<string, string>): void {\n this.event('metric', { name, value }, tags);\n }\n\n /** Convenience: track a log entry. */\n log(level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: Record<string, unknown>): void {\n this.event('log', { level, message, ...data });\n }\n\n /** Flush buffered events to the platform. */\n async flush(): Promise<void> {\n if (this.buffer.length === 0 || this.flushing) {return;}\n\n this.flushing = true;\n const events = this.buffer.splice(0, this.batchSize);\n\n try {\n await this.sendWithRetry(events);\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)), events);\n } finally {\n this.flushing = false;\n }\n\n // If buffer still has events, flush again\n if (this.buffer.length > 0) {\n void this.flush();\n }\n }\n\n /** Flush all remaining events and stop the timer. */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Flush remaining in batches\n while (this.buffer.length > 0) {\n await this.flush();\n }\n }\n\n // ── Internal ────────────────────────────────────────────────────────────\n\n private async sendWithRetry(events: TelemetryEvent[]): Promise<IngestResponse> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n return await this.send(events);\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n // Retry only on network/server errors, not client errors\n if (attempt < this.maxRetries) {\n await sleep(Math.min(1000 * 2 ** attempt, 5000));\n }\n }\n }\n\n throw lastError ?? new Error('All retry attempts failed');\n }\n\n private async send(events: TelemetryEvent[]): Promise<IngestResponse> {\n const url = `${this.endpoint}/telemetry/v1/ingest`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Telemetry ingest failed: ${response.status} ${text}`);\n }\n\n return (await response.json()) as IngestResponse;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => { setTimeout(resolve, ms); });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/telemetry-client\n * Lightweight telemetry client for KB Labs platform.\n *\n * Zero runtime dependencies. Works in Node.js and browser.\n * Batches events and flushes to the platform ingestion endpoint.\n *\n * @example\n * ```typescript\n * import { KBTelemetry } from '@kb-labs/telemetry-client';\n *\n * const telemetry = new KBTelemetry({\n * endpoint: 'http://localhost:4000',\n * apiKey: 'your-token',\n * source: 'my-product',\n * });\n *\n * telemetry.event('user.signup', { plan: 'pro' });\n * telemetry.event('api.request', { method: 'POST', path: '/checkout', durationMs: 142 });\n *\n * // Flush on shutdown (automatic in Node.js via process handlers)\n * await telemetry.flush();\n * ```\n */\n\n// ── Types ─────────────────────────────────────────────────────────────────\n\nexport interface KBTelemetryOptions {\n /** Platform Gateway endpoint (e.g., \"http://localhost:4000\") */\n endpoint: string;\n /** API key / Bearer token for authentication */\n apiKey: string;\n /** Source product name — identifies who sent the event */\n source: string;\n /** Max events per batch (default: 50) */\n batchSize?: number;\n /** Auto-flush interval in ms (default: 5000). Set 0 to disable. */\n flushIntervalMs?: number;\n /** Max retry attempts for failed flushes (default: 2) */\n maxRetries?: number;\n /** Custom tags applied to all events from this client */\n defaultTags?: Record<string, string>;\n /** Called on flush errors (default: console.error) */\n onError?: (error: Error, events: TelemetryEvent[]) => void;\n}\n\nexport interface TelemetryEvent {\n source: string;\n type: string;\n timestamp?: string;\n payload?: Record<string, unknown>;\n tags?: Record<string, string>;\n}\n\ninterface IngestResponse {\n accepted: number;\n rejected: number;\n errors?: Array<{ index: number; message: string }>;\n}\n\n// ── Client ────────────────────────────────────────────────────────────────\n\nexport class KBTelemetry {\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly source: string;\n private readonly batchSize: number;\n private readonly maxRetries: number;\n private readonly defaultTags: Record<string, string>;\n private readonly onError: (error: Error, events: TelemetryEvent[]) => void;\n\n private buffer: TelemetryEvent[] = [];\n private flushTimer: ReturnType<typeof setInterval> | null = null;\n private flushing = false;\n\n constructor(options: KBTelemetryOptions) {\n this.endpoint = options.endpoint.replace(/\\/+$/, '');\n this.apiKey = options.apiKey;\n this.source = options.source;\n this.batchSize = options.batchSize ?? 50;\n this.maxRetries = options.maxRetries ?? 2;\n this.defaultTags = options.defaultTags ?? {};\n this.onError = options.onError ?? ((err) => console.error('[KBTelemetry]', err.message));\n\n const interval = options.flushIntervalMs ?? 5000;\n if (interval > 0) {\n this.flushTimer = setInterval(() => { void this.flush(); }, interval);\n // Unref so the timer doesn't keep the process alive\n if (typeof this.flushTimer === 'object' && 'unref' in this.flushTimer) {\n this.flushTimer.unref();\n }\n }\n\n // Auto-flush on process exit (Node.js only)\n if (typeof process !== 'undefined' && process.on) {\n const exitHandler = () => { void this.shutdown(); };\n process.on('beforeExit', exitHandler);\n process.on('SIGTERM', exitHandler);\n process.on('SIGINT', exitHandler);\n }\n }\n\n // ── Public API ──────────────────────────────────────────────────────────\n\n /** Track a named event with optional payload and tags. */\n event(type: string, payload?: Record<string, unknown>, tags?: Record<string, string>): void {\n this.buffer.push({\n source: this.source,\n type,\n timestamp: new Date().toISOString(),\n payload,\n tags: { ...this.defaultTags, ...tags },\n });\n\n if (this.buffer.length >= this.batchSize) {\n void this.flush();\n }\n }\n\n /** Convenience: track a metric value. */\n metric(name: string, value: number, tags?: Record<string, string>): void {\n this.event('metric', { name, value }, tags);\n }\n\n /** Convenience: track a log entry. */\n log(level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: Record<string, unknown>): void {\n this.event('log', { level, message, ...data });\n }\n\n /** Flush buffered events to the platform. */\n async flush(): Promise<void> {\n if (this.buffer.length === 0 || this.flushing) {return;}\n\n this.flushing = true;\n const events = this.buffer.splice(0, this.batchSize);\n\n try {\n await this.sendWithRetry(events);\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)), events);\n } finally {\n this.flushing = false;\n }\n\n // If buffer still has events, flush again\n if (this.buffer.length > 0) {\n void this.flush();\n }\n }\n\n /** Flush all remaining events and stop the timer. */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Flush remaining in batches\n while (this.buffer.length > 0) {\n await this.flush();\n }\n }\n\n // ── Internal ────────────────────────────────────────────────────────────\n\n private async sendWithRetry(events: TelemetryEvent[]): Promise<IngestResponse> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n return await this.send(events);\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n // Retry only on network/server errors, not client errors\n if (attempt < this.maxRetries) {\n await sleep(Math.min(1000 * 2 ** attempt, 5000));\n }\n }\n }\n\n throw lastError ?? new Error('All retry attempts failed');\n }\n\n private async send(events: TelemetryEvent[]): Promise<IngestResponse> {\n const url = `${this.endpoint}/telemetry/v1/ingest`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`Telemetry ingest failed: ${response.status} ${text}`);\n }\n\n return (await response.json()) as IngestResponse;\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => { setTimeout(resolve, ms); });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"tsup": "^8.4.0",
|
|
7
7
|
"typescript": "^5.7.0",
|
|
8
8
|
"vitest": "^3.2.4",
|
|
9
|
-
"@kb-labs/devkit": "2.
|
|
9
|
+
"@kb-labs/devkit": "2.14.0"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
12
|
"node": ">=20.0.0",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"name": "@kb-labs/telemetry-client",
|
|
27
27
|
"type": "module",
|
|
28
28
|
"types": "./dist/index.d.ts",
|
|
29
|
-
"version": "2.
|
|
29
|
+
"version": "2.14.0",
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsup",
|
|
32
32
|
"clean": "rimraf dist",
|