logwell 0.1.0 → 0.1.3
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 +10 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/Divkix/Logwell/main/static/banner.png" alt="Logwell" width="600" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://www.npmjs.com/package/logwell"><img src="https://img.shields.io/npm/v/logwell.svg" alt="npm version" /></a>
|
|
7
|
+
<a href="https://www.npmjs.com/package/logwell"><img src="https://img.shields.io/npm/dm/logwell.svg" alt="npm downloads" /></a>
|
|
8
|
+
<a href="https://github.com/Divkix/Logwell/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/logwell.svg" alt="license" /></a>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
1
11
|
# logwell
|
|
2
12
|
|
|
3
13
|
Official TypeScript SDK for [Logwell](https://github.com/divkix/logwell) - a self-hosted logging platform with real-time streaming and full-text search.
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var i=class r extends Error{constructor(t,n,o,h=false){super(t);this.code=n;this.statusCode=o;this.retryable=h;this.name="LogwellError",Error.captureStackTrace?.(this,r);}};var s={batchSize:50,flushInterval:5e3,maxQueueSize:1e3,maxRetries:3},d=/^lw_[A-Za-z0-9_-]{32}$/;function f(r){return !r||typeof r!="string"?false:d.test(r)}function
|
|
1
|
+
'use strict';var i=class r extends Error{constructor(t,n,o,h=false){super(t);this.code=n;this.statusCode=o;this.retryable=h;this.name="LogwellError",Error.captureStackTrace?.(this,r);}};var s={batchSize:50,flushInterval:5e3,maxQueueSize:1e3,maxRetries:3},d=/^lw_[A-Za-z0-9_-]{32}$/;function f(r){return !r||typeof r!="string"?false:d.test(r)}function g(r){try{return new URL(r),!0}catch{return false}}function p(r){if(!r.apiKey)throw new i("apiKey is required","INVALID_CONFIG");if(!r.endpoint)throw new i("endpoint is required","INVALID_CONFIG");if(!f(r.apiKey))throw new i("Invalid API key format. Expected: lw_[32 characters]","INVALID_CONFIG");if(!g(r.endpoint))throw new i("Invalid endpoint URL","INVALID_CONFIG");if(r.batchSize!==void 0&&r.batchSize<=0)throw new i("batchSize must be positive","INVALID_CONFIG");if(r.flushInterval!==void 0&&r.flushInterval<=0)throw new i("flushInterval must be positive","INVALID_CONFIG");if(r.maxQueueSize!==void 0&&r.maxQueueSize<=0)throw new i("maxQueueSize must be positive","INVALID_CONFIG");if(r.maxRetries!==void 0&&r.maxRetries<0)throw new i("maxRetries must be non-negative","INVALID_CONFIG");return {apiKey:r.apiKey,endpoint:r.endpoint,service:r.service,batchSize:r.batchSize??s.batchSize,flushInterval:r.flushInterval??s.flushInterval,maxQueueSize:r.maxQueueSize??s.maxQueueSize,maxRetries:r.maxRetries??s.maxRetries,onError:r.onError,onFlush:r.onFlush}}var a=class{constructor(e,t){this.sendBatch=e;this.config=t;}queue=[];flushTimer=null;flushing=false;stopped=false;get size(){return this.queue.length}add(e){if(!this.stopped){if(this.queue.length>=this.config.maxQueueSize){let t=this.queue.shift();this.config.onError?.(new i(`Queue overflow. Dropped log: ${t?.message.substring(0,50)}...`,"QUEUE_OVERFLOW"));}this.queue.push(e),!this.flushTimer&&!this.stopped&&this.startTimer(),this.queue.length>=this.config.batchSize&&this.flush();}}async flush(){if(this.flushing||this.queue.length===0)return null;this.flushing=true,this.stopTimer();let e=this.queue.splice(0),t=e.length;try{let n=await this.sendBatch(e);return this.config.onFlush?.(t),this.queue.length>0&&!this.stopped&&this.startTimer(),n}catch(n){return this.queue.unshift(...e),this.config.onError?.(n),this.stopped||this.startTimer(),null}finally{this.flushing=false;}}async shutdown(){this.stopped||(this.stopped=true,this.stopTimer(),this.queue.length>0&&(this.flushing=false,await this.flush()));}startTimer(){this.flushTimer=setTimeout(()=>{this.flush();},this.config.flushInterval);}stopTimer(){this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null);}};function c(r,e=100){let t=Math.min(e*2**r,1e4),n=Math.random()*t*.3;return new Promise(o=>setTimeout(o,t+n))}var u=class{constructor(e){this.config=e;this.ingestUrl=`${e.endpoint}/v1/ingest`;}ingestUrl;async send(e){let t=new i("Max retries exceeded","NETWORK_ERROR",void 0,true);for(let n=0;n<=this.config.maxRetries;n++)try{return await this.doRequest(e)}catch(o){if(t=o,!t.retryable)throw t;n<this.config.maxRetries&&await c(n);}throw t}async doRequest(e){let t;try{t=await fetch(this.ingestUrl,{method:"POST",headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e)});}catch(n){throw new i(`Network error: ${n.message}`,"NETWORK_ERROR",void 0,true)}if(!t.ok){let n=await this.tryParseError(t);throw this.createError(t.status,n)}return await t.json()}async tryParseError(e){try{let t=await e.json();return t.message||t.error||"Unknown error"}catch{return `HTTP ${e.status}`}}createError(e,t){switch(e){case 401:return new i(`Unauthorized: ${t}`,"UNAUTHORIZED",e,false);case 400:return new i(`Validation error: ${t}`,"VALIDATION_ERROR",e,false);case 429:return new i(`Rate limited: ${t}`,"RATE_LIMITED",e,true);default:return e>=500?new i(`Server error: ${t}`,"SERVER_ERROR",e,true):new i(`HTTP error ${e}: ${t}`,"SERVER_ERROR",e,false)}}};var l=class r{config;queue;transport;parentMetadata;stopped=false;constructor(e,t,n){if(this.config=p(e),this.parentMetadata=n,this.transport=new u({endpoint:this.config.endpoint,apiKey:this.config.apiKey,maxRetries:this.config.maxRetries}),t)this.queue=t;else {let o={batchSize:this.config.batchSize,flushInterval:this.config.flushInterval,maxQueueSize:this.config.maxQueueSize,onError:this.config.onError,onFlush:this.config.onFlush};this.queue=new a(h=>this.transport.send(h),o);}}get queueSize(){return this.queue.size}log(e){if(this.stopped)return;let t={...e,timestamp:e.timestamp??new Date().toISOString(),service:e.service??this.config.service,metadata:this.mergeMetadata(e.metadata)};this.queue.add(t);}debug(e,t){this.log({level:"debug",message:e,metadata:t});}info(e,t){this.log({level:"info",message:e,metadata:t});}warn(e,t){this.log({level:"warn",message:e,metadata:t});}error(e,t){this.log({level:"error",message:e,metadata:t});}fatal(e,t){this.log({level:"fatal",message:e,metadata:t});}async flush(){return this.queue.flush()}async shutdown(){this.stopped=true,await this.queue.shutdown();}child(e){let t={...this.config,service:e.service??this.config.service},n={...this.parentMetadata,...e.metadata};return new r(t,this.queue,n)}mergeMetadata(e){if(!(!this.parentMetadata&&!e))return {...this.parentMetadata,...e}}};exports.Logwell=l;exports.LogwellError=i;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/queue.ts","../src/transport.ts","../src/client.ts"],"names":["LogwellError","_LogwellError","message","code","statusCode","retryable","DEFAULT_CONFIG","API_KEY_REGEX","validateApiKeyFormat","apiKey","isValidUrl","url","validateConfig","config","BatchQueue","sendBatch","entry","dropped","batch","count","response","error","delay","attempt","baseDelay","ms","jitter","resolve","HttpTransport","logs","lastError","errorBody","body","status","Logwell","_Logwell","existingQueue","parentMetadata","queueConfig","fullEntry","metadata","options","childConfig","childMetadata","entryMetadata"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Error codes for Logwell SDK errors\n */\nexport type LogwellErrorCode =\n | 'NETWORK_ERROR'\n | 'UNAUTHORIZED'\n | 'VALIDATION_ERROR'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR'\n | 'QUEUE_OVERFLOW'\n | 'INVALID_CONFIG';\n\n/**\n * Custom error class for Logwell SDK errors\n *\n * @example\n * ```ts\n * throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);\n * ```\n */\n// V8 specific type for captureStackTrace\ndeclare global {\n interface ErrorConstructor {\n captureStackTrace?(targetObject: object, constructorOpt?: NewableFunction): void;\n }\n}\n\nexport class LogwellError extends Error {\n /**\n * Creates a new LogwellError\n *\n * @param message - Human-readable error message\n * @param code - Error code for programmatic handling\n * @param statusCode - HTTP status code if applicable\n * @param retryable - Whether the operation can be retried\n */\n constructor(\n message: string,\n public readonly code: LogwellErrorCode,\n public readonly statusCode?: number,\n public readonly retryable: boolean = false,\n ) {\n super(message);\n this.name = 'LogwellError';\n\n // Maintains proper stack trace for where our error was thrown (V8 only)\n Error.captureStackTrace?.(this, LogwellError);\n }\n}\n","import { LogwellError } from './errors';\nimport type { LogwellConfig } from './types';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n batchSize: 50,\n flushInterval: 5000,\n maxQueueSize: 1000,\n maxRetries: 3,\n} as const;\n\n/**\n * API key format regex: lw_[32 alphanumeric chars including - and _]\n */\nexport const API_KEY_REGEX = /^lw_[A-Za-z0-9_-]{32}$/;\n\n/**\n * Validates API key format\n *\n * @param apiKey - API key to validate\n * @returns true if valid format, false otherwise\n */\nexport function validateApiKeyFormat(apiKey: string): boolean {\n if (!apiKey || typeof apiKey !== 'string') {\n return false;\n }\n return API_KEY_REGEX.test(apiKey);\n}\n\n/**\n * Validates a URL string\n *\n * @param url - URL string to validate\n * @returns true if valid URL, false otherwise\n */\nfunction isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates configuration and returns merged config with defaults\n *\n * @param config - Partial configuration to validate\n * @returns Complete configuration with defaults applied\n * @throws LogwellError if configuration is invalid\n */\nexport function validateConfig(config: Partial<LogwellConfig>): LogwellConfig {\n // Validate required fields\n if (!config.apiKey) {\n throw new LogwellError('apiKey is required', 'INVALID_CONFIG');\n }\n\n if (!config.endpoint) {\n throw new LogwellError('endpoint is required', 'INVALID_CONFIG');\n }\n\n // Validate API key format\n if (!validateApiKeyFormat(config.apiKey)) {\n throw new LogwellError(\n 'Invalid API key format. Expected: lw_[32 characters]',\n 'INVALID_CONFIG',\n );\n }\n\n // Validate endpoint URL\n if (!isValidUrl(config.endpoint)) {\n throw new LogwellError('Invalid endpoint URL', 'INVALID_CONFIG');\n }\n\n // Validate numeric options\n if (config.batchSize !== undefined && config.batchSize <= 0) {\n throw new LogwellError('batchSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.flushInterval !== undefined && config.flushInterval <= 0) {\n throw new LogwellError('flushInterval must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxQueueSize !== undefined && config.maxQueueSize <= 0) {\n throw new LogwellError('maxQueueSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxRetries !== undefined && config.maxRetries < 0) {\n throw new LogwellError('maxRetries must be non-negative', 'INVALID_CONFIG');\n }\n\n // Return merged config with defaults\n return {\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n service: config.service,\n batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,\n flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,\n maxRetries: config.maxRetries ?? DEFAULT_CONFIG.maxRetries,\n onError: config.onError,\n onFlush: config.onFlush,\n };\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Callback type for sending batched logs\n */\nexport type SendBatchFn = (logs: LogEntry[]) => Promise<IngestResponse>;\n\n/**\n * Queue configuration options\n */\nexport interface QueueConfig {\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Batch queue for buffering and sending logs\n *\n * Features:\n * - Automatic flush on batch size threshold\n * - Automatic flush on time interval\n * - Queue overflow protection (drops oldest)\n * - Re-queue on send failure\n * - Graceful shutdown\n */\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private flushing = false;\n private stopped = false;\n\n constructor(\n private sendBatch: SendBatchFn,\n private config: QueueConfig,\n ) {}\n\n /**\n * Current number of logs in the queue\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Add a log entry to the queue\n *\n * Triggers flush if batch size is reached.\n * Drops oldest log if queue overflows.\n */\n add(entry: LogEntry): void {\n if (this.stopped) {\n return;\n }\n\n // Handle queue overflow\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.onError?.(\n new LogwellError(\n `Queue overflow. Dropped log: ${dropped?.message.substring(0, 50)}...`,\n 'QUEUE_OVERFLOW',\n ),\n );\n }\n\n this.queue.push(entry);\n\n // Start timer on first entry\n if (!this.flushTimer && !this.stopped) {\n this.startTimer();\n }\n\n // Flush immediately if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n // Prevent concurrent flushes\n if (this.flushing || this.queue.length === 0) {\n return null;\n }\n\n this.flushing = true;\n this.stopTimer();\n\n // Take current batch\n const batch = this.queue.splice(0);\n const count = batch.length;\n\n try {\n const response = await this.sendBatch(batch);\n this.config.onFlush?.(count);\n\n // Restart timer if more logs remain (added during flush)\n if (this.queue.length > 0 && !this.stopped) {\n this.startTimer();\n }\n\n return response;\n } catch (error) {\n // Re-queue failed logs at the front\n this.queue.unshift(...batch);\n this.config.onError?.(error as Error);\n\n // Restart timer to retry\n if (!this.stopped) {\n this.startTimer();\n }\n\n return null;\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining logs and stop the queue\n */\n async shutdown(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n this.stopTimer();\n\n // Flush all remaining logs\n if (this.queue.length > 0) {\n this.flushing = false; // Reset flushing flag\n await this.flush();\n }\n }\n\n private startTimer(): void {\n this.flushTimer = setTimeout(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n private stopTimer(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Transport configuration\n */\nexport interface TransportConfig {\n endpoint: string;\n apiKey: string;\n maxRetries: number;\n timeout?: number;\n}\n\n/**\n * Delay helper with exponential backoff\n */\nfunction delay(attempt: number, baseDelay = 100): Promise<void> {\n const ms = Math.min(baseDelay * 2 ** attempt, 10000);\n const jitter = Math.random() * ms * 0.3;\n return new Promise((resolve) => setTimeout(resolve, ms + jitter));\n}\n\n/**\n * Check if error is retryable based on status code\n */\nfunction isRetryableStatus(status: number): boolean {\n return status >= 500 || status === 429;\n}\n\n/**\n * HTTP transport for sending logs to Logwell server\n *\n * Features:\n * - Automatic retry with exponential backoff\n * - Error classification with retryable flag\n * - Proper error handling for all HTTP status codes\n */\nexport class HttpTransport {\n private readonly ingestUrl: string;\n\n constructor(private config: TransportConfig) {\n this.ingestUrl = `${config.endpoint}/v1/ingest`;\n }\n\n /**\n * Send logs to the Logwell server\n *\n * @param logs - Array of log entries to send\n * @returns Response with accepted/rejected counts\n * @throws LogwellError on failure after all retries\n */\n async send(logs: LogEntry[]): Promise<IngestResponse> {\n let lastError: LogwellError | null = null;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const response = await this.doRequest(logs);\n return response;\n } catch (error) {\n lastError = error as LogwellError;\n\n // Don't retry non-retryable errors\n if (!lastError.retryable) {\n throw lastError;\n }\n\n // Don't delay after the last attempt\n if (attempt < this.config.maxRetries) {\n await delay(attempt);\n }\n }\n }\n\n throw lastError!;\n }\n\n private async doRequest(logs: LogEntry[]): Promise<IngestResponse> {\n let response: Response;\n\n try {\n response = await fetch(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(logs),\n });\n } catch (error) {\n // Network error (fetch failed)\n throw new LogwellError(\n `Network error: ${(error as Error).message}`,\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n }\n\n // Handle error responses\n if (!response.ok) {\n const errorBody = await this.tryParseError(response);\n throw this.createError(response.status, errorBody);\n }\n\n // Parse successful response\n return (await response.json()) as IngestResponse;\n }\n\n private async tryParseError(response: Response): Promise<string> {\n try {\n const body = await response.json();\n return body.message || body.error || 'Unknown error';\n } catch {\n return `HTTP ${response.status}`;\n }\n }\n\n private createError(status: number, message: string): LogwellError {\n switch (status) {\n case 401:\n return new LogwellError(\n `Unauthorized: ${message}`,\n 'UNAUTHORIZED',\n status,\n false,\n );\n case 400:\n return new LogwellError(\n `Validation error: ${message}`,\n 'VALIDATION_ERROR',\n status,\n false,\n );\n case 429:\n return new LogwellError(\n `Rate limited: ${message}`,\n 'RATE_LIMITED',\n status,\n true,\n );\n default:\n if (status >= 500) {\n return new LogwellError(\n `Server error: ${message}`,\n 'SERVER_ERROR',\n status,\n true,\n );\n }\n return new LogwellError(\n `HTTP error ${status}: ${message}`,\n 'SERVER_ERROR',\n status,\n false,\n );\n }\n }\n}\n","import { validateConfig } from './config';\nimport { BatchQueue, type QueueConfig } from './queue';\nimport { HttpTransport } from './transport';\nimport type { IngestResponse, LogEntry, LogwellConfig } from './types';\n\n/**\n * Child logger options\n */\nexport interface ChildLoggerOptions {\n service?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Internal resolved config with all defaults applied\n */\ninterface ResolvedConfig {\n apiKey: string;\n endpoint: string;\n service?: string;\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n maxRetries: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Asserts that config has all required fields after validation\n */\nfunction asResolvedConfig(config: LogwellConfig): ResolvedConfig {\n return config as ResolvedConfig;\n}\n\n/**\n * Main Logwell client class\n *\n * Provides methods for logging at different levels with automatic\n * batching, retry, and queue management.\n *\n * @example\n * ```ts\n * const logger = new Logwell({\n * apiKey: 'lw_xxx',\n * endpoint: 'https://logs.example.com',\n * service: 'my-app',\n * });\n *\n * logger.info('User logged in', { userId: '123' });\n * await logger.shutdown();\n * ```\n */\nexport class Logwell {\n private readonly config: ResolvedConfig;\n private readonly queue: BatchQueue;\n private readonly transport: HttpTransport;\n private readonly parentMetadata?: Record<string, unknown>;\n private stopped = false;\n\n constructor(config: LogwellConfig);\n constructor(config: LogwellConfig, queue: BatchQueue, parentMetadata?: Record<string, unknown>);\n constructor(\n config: LogwellConfig,\n existingQueue?: BatchQueue,\n parentMetadata?: Record<string, unknown>,\n ) {\n // Validate and apply defaults\n this.config = asResolvedConfig(validateConfig(config));\n this.parentMetadata = parentMetadata;\n\n // Create transport\n this.transport = new HttpTransport({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxRetries: this.config.maxRetries,\n });\n\n // Use existing queue (for child loggers) or create new one\n if (existingQueue) {\n this.queue = existingQueue;\n } else {\n const queueConfig: QueueConfig = {\n batchSize: this.config.batchSize,\n flushInterval: this.config.flushInterval,\n maxQueueSize: this.config.maxQueueSize,\n onError: this.config.onError,\n onFlush: this.config.onFlush,\n };\n this.queue = new BatchQueue((logs) => this.transport.send(logs), queueConfig);\n }\n }\n\n /**\n * Current number of logs waiting in the queue\n */\n get queueSize(): number {\n return this.queue.size;\n }\n\n /**\n * Log a message at the specified level\n */\n log(entry: LogEntry): void {\n if (this.stopped) return;\n\n const fullEntry: LogEntry = {\n ...entry,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n service: entry.service ?? this.config.service,\n metadata: this.mergeMetadata(entry.metadata),\n };\n\n this.queue.add(fullEntry);\n }\n\n /**\n * Log a debug message\n */\n debug(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'debug', message, metadata });\n }\n\n /**\n * Log an info message\n */\n info(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'info', message, metadata });\n }\n\n /**\n * Log a warning message\n */\n warn(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'warn', message, metadata });\n }\n\n /**\n * Log an error message\n */\n error(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'error', message, metadata });\n }\n\n /**\n * Log a fatal error message\n */\n fatal(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'fatal', message, metadata });\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n return this.queue.flush();\n }\n\n /**\n * Flush remaining logs and stop the client\n *\n * Call this before process exit to ensure all logs are sent.\n */\n async shutdown(): Promise<void> {\n this.stopped = true;\n await this.queue.shutdown();\n }\n\n /**\n * Create a child logger with additional context\n *\n * Child loggers share the same queue as the parent,\n * but can have their own service name and default metadata.\n *\n * @example\n * ```ts\n * const requestLogger = logger.child({\n * metadata: { requestId: req.id },\n * });\n * requestLogger.info('Request received');\n * ```\n */\n child(options: ChildLoggerOptions): Logwell {\n const childConfig: LogwellConfig = {\n ...this.config,\n service: options.service ?? this.config.service,\n };\n\n const childMetadata = {\n ...this.parentMetadata,\n ...options.metadata,\n };\n\n return new Logwell(childConfig, this.queue, childMetadata);\n }\n\n private mergeMetadata(\n entryMetadata?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!this.parentMetadata && !entryMetadata) {\n return undefined;\n }\n return {\n ...this.parentMetadata,\n ...entryMetadata,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/queue.ts","../src/transport.ts","../src/client.ts"],"names":["LogwellError","_LogwellError","message","code","statusCode","retryable","DEFAULT_CONFIG","API_KEY_REGEX","validateApiKeyFormat","apiKey","isValidUrl","url","validateConfig","config","BatchQueue","sendBatch","entry","dropped","batch","count","response","error","delay","attempt","baseDelay","ms","jitter","resolve","HttpTransport","logs","lastError","errorBody","body","status","Logwell","_Logwell","existingQueue","parentMetadata","queueConfig","fullEntry","metadata","options","childConfig","childMetadata","entryMetadata"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Error codes for Logwell SDK errors\n */\nexport type LogwellErrorCode =\n | 'NETWORK_ERROR'\n | 'UNAUTHORIZED'\n | 'VALIDATION_ERROR'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR'\n | 'QUEUE_OVERFLOW'\n | 'INVALID_CONFIG';\n\n/**\n * Custom error class for Logwell SDK errors\n *\n * @example\n * ```ts\n * throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);\n * ```\n */\nexport class LogwellError extends Error {\n /**\n * Creates a new LogwellError\n *\n * @param message - Human-readable error message\n * @param code - Error code for programmatic handling\n * @param statusCode - HTTP status code if applicable\n * @param retryable - Whether the operation can be retried\n */\n constructor(\n message: string,\n public readonly code: LogwellErrorCode,\n public readonly statusCode?: number,\n public readonly retryable: boolean = false,\n ) {\n super(message);\n this.name = 'LogwellError';\n\n // Maintains proper stack trace for where our error was thrown (V8 only)\n // Use type assertion to avoid global augmentation (required for JSR compatibility)\n const ErrorWithCapture = Error as unknown as {\n captureStackTrace?: (target: object, ctor?: NewableFunction) => void;\n };\n ErrorWithCapture.captureStackTrace?.(this, LogwellError);\n }\n}\n","import { LogwellError } from './errors';\nimport type { LogwellConfig } from './types';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n batchSize: 50,\n flushInterval: 5000,\n maxQueueSize: 1000,\n maxRetries: 3,\n} as const;\n\n/**\n * API key format regex: lw_[32 alphanumeric chars including - and _]\n */\nexport const API_KEY_REGEX = /^lw_[A-Za-z0-9_-]{32}$/;\n\n/**\n * Validates API key format\n *\n * @param apiKey - API key to validate\n * @returns true if valid format, false otherwise\n */\nexport function validateApiKeyFormat(apiKey: string): boolean {\n if (!apiKey || typeof apiKey !== 'string') {\n return false;\n }\n return API_KEY_REGEX.test(apiKey);\n}\n\n/**\n * Validates a URL string\n *\n * @param url - URL string to validate\n * @returns true if valid URL, false otherwise\n */\nfunction isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates configuration and returns merged config with defaults\n *\n * @param config - Partial configuration to validate\n * @returns Complete configuration with defaults applied\n * @throws LogwellError if configuration is invalid\n */\nexport function validateConfig(config: Partial<LogwellConfig>): LogwellConfig {\n // Validate required fields\n if (!config.apiKey) {\n throw new LogwellError('apiKey is required', 'INVALID_CONFIG');\n }\n\n if (!config.endpoint) {\n throw new LogwellError('endpoint is required', 'INVALID_CONFIG');\n }\n\n // Validate API key format\n if (!validateApiKeyFormat(config.apiKey)) {\n throw new LogwellError(\n 'Invalid API key format. Expected: lw_[32 characters]',\n 'INVALID_CONFIG',\n );\n }\n\n // Validate endpoint URL\n if (!isValidUrl(config.endpoint)) {\n throw new LogwellError('Invalid endpoint URL', 'INVALID_CONFIG');\n }\n\n // Validate numeric options\n if (config.batchSize !== undefined && config.batchSize <= 0) {\n throw new LogwellError('batchSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.flushInterval !== undefined && config.flushInterval <= 0) {\n throw new LogwellError('flushInterval must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxQueueSize !== undefined && config.maxQueueSize <= 0) {\n throw new LogwellError('maxQueueSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxRetries !== undefined && config.maxRetries < 0) {\n throw new LogwellError('maxRetries must be non-negative', 'INVALID_CONFIG');\n }\n\n // Return merged config with defaults\n return {\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n service: config.service,\n batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,\n flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,\n maxRetries: config.maxRetries ?? DEFAULT_CONFIG.maxRetries,\n onError: config.onError,\n onFlush: config.onFlush,\n };\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Callback type for sending batched logs\n */\nexport type SendBatchFn = (logs: LogEntry[]) => Promise<IngestResponse>;\n\n/**\n * Queue configuration options\n */\nexport interface QueueConfig {\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Batch queue for buffering and sending logs\n *\n * Features:\n * - Automatic flush on batch size threshold\n * - Automatic flush on time interval\n * - Queue overflow protection (drops oldest)\n * - Re-queue on send failure\n * - Graceful shutdown\n */\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private flushing = false;\n private stopped = false;\n\n constructor(\n private sendBatch: SendBatchFn,\n private config: QueueConfig,\n ) {}\n\n /**\n * Current number of logs in the queue\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Add a log entry to the queue\n *\n * Triggers flush if batch size is reached.\n * Drops oldest log if queue overflows.\n */\n add(entry: LogEntry): void {\n if (this.stopped) {\n return;\n }\n\n // Handle queue overflow\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.onError?.(\n new LogwellError(\n `Queue overflow. Dropped log: ${dropped?.message.substring(0, 50)}...`,\n 'QUEUE_OVERFLOW',\n ),\n );\n }\n\n this.queue.push(entry);\n\n // Start timer on first entry\n if (!this.flushTimer && !this.stopped) {\n this.startTimer();\n }\n\n // Flush immediately if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n // Prevent concurrent flushes\n if (this.flushing || this.queue.length === 0) {\n return null;\n }\n\n this.flushing = true;\n this.stopTimer();\n\n // Take current batch\n const batch = this.queue.splice(0);\n const count = batch.length;\n\n try {\n const response = await this.sendBatch(batch);\n this.config.onFlush?.(count);\n\n // Restart timer if more logs remain (added during flush)\n if (this.queue.length > 0 && !this.stopped) {\n this.startTimer();\n }\n\n return response;\n } catch (error) {\n // Re-queue failed logs at the front\n this.queue.unshift(...batch);\n this.config.onError?.(error as Error);\n\n // Restart timer to retry\n if (!this.stopped) {\n this.startTimer();\n }\n\n return null;\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining logs and stop the queue\n */\n async shutdown(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n this.stopTimer();\n\n // Flush all remaining logs\n if (this.queue.length > 0) {\n this.flushing = false; // Reset flushing flag\n await this.flush();\n }\n }\n\n private startTimer(): void {\n this.flushTimer = setTimeout(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n private stopTimer(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Transport configuration\n */\nexport interface TransportConfig {\n endpoint: string;\n apiKey: string;\n maxRetries: number;\n timeout?: number;\n}\n\n/**\n * Delay helper with exponential backoff\n */\nfunction delay(attempt: number, baseDelay = 100): Promise<void> {\n const ms = Math.min(baseDelay * 2 ** attempt, 10000);\n const jitter = Math.random() * ms * 0.3;\n return new Promise((resolve) => setTimeout(resolve, ms + jitter));\n}\n\n/**\n * HTTP transport for sending logs to Logwell server\n *\n * Features:\n * - Automatic retry with exponential backoff\n * - Error classification with retryable flag\n * - Proper error handling for all HTTP status codes\n */\nexport class HttpTransport {\n private readonly ingestUrl: string;\n\n constructor(private config: TransportConfig) {\n this.ingestUrl = `${config.endpoint}/v1/ingest`;\n }\n\n /**\n * Send logs to the Logwell server\n *\n * @param logs - Array of log entries to send\n * @returns Response with accepted/rejected counts\n * @throws LogwellError on failure after all retries\n */\n async send(logs: LogEntry[]): Promise<IngestResponse> {\n let lastError: LogwellError = new LogwellError(\n 'Max retries exceeded',\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n return await this.doRequest(logs);\n } catch (error) {\n lastError = error as LogwellError;\n\n // Don't retry non-retryable errors\n if (!lastError.retryable) {\n throw lastError;\n }\n\n // Don't delay after the last attempt\n if (attempt < this.config.maxRetries) {\n await delay(attempt);\n }\n }\n }\n\n throw lastError;\n }\n\n private async doRequest(logs: LogEntry[]): Promise<IngestResponse> {\n let response: Response;\n\n try {\n response = await fetch(this.ingestUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(logs),\n });\n } catch (error) {\n // Network error (fetch failed)\n throw new LogwellError(\n `Network error: ${(error as Error).message}`,\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n }\n\n // Handle error responses\n if (!response.ok) {\n const errorBody = await this.tryParseError(response);\n throw this.createError(response.status, errorBody);\n }\n\n // Parse successful response\n return (await response.json()) as IngestResponse;\n }\n\n private async tryParseError(response: Response): Promise<string> {\n try {\n const body = await response.json();\n return body.message || body.error || 'Unknown error';\n } catch {\n return `HTTP ${response.status}`;\n }\n }\n\n private createError(status: number, message: string): LogwellError {\n switch (status) {\n case 401:\n return new LogwellError(`Unauthorized: ${message}`, 'UNAUTHORIZED', status, false);\n case 400:\n return new LogwellError(`Validation error: ${message}`, 'VALIDATION_ERROR', status, false);\n case 429:\n return new LogwellError(`Rate limited: ${message}`, 'RATE_LIMITED', status, true);\n default:\n if (status >= 500) {\n return new LogwellError(`Server error: ${message}`, 'SERVER_ERROR', status, true);\n }\n return new LogwellError(`HTTP error ${status}: ${message}`, 'SERVER_ERROR', status, false);\n }\n }\n}\n","import { validateConfig } from './config';\nimport { BatchQueue, type QueueConfig } from './queue';\nimport { HttpTransport } from './transport';\nimport type { IngestResponse, LogEntry, LogwellConfig } from './types';\n\n/**\n * Child logger options\n */\nexport interface ChildLoggerOptions {\n service?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Internal resolved config with all defaults applied\n */\ninterface ResolvedConfig {\n apiKey: string;\n endpoint: string;\n service?: string;\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n maxRetries: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Asserts that config has all required fields after validation\n */\nfunction asResolvedConfig(config: LogwellConfig): ResolvedConfig {\n return config as ResolvedConfig;\n}\n\n/**\n * Main Logwell client class\n *\n * Provides methods for logging at different levels with automatic\n * batching, retry, and queue management.\n *\n * @example\n * ```ts\n * const logger = new Logwell({\n * apiKey: 'lw_xxx',\n * endpoint: 'https://logs.example.com',\n * service: 'my-app',\n * });\n *\n * logger.info('User logged in', { userId: '123' });\n * await logger.shutdown();\n * ```\n */\nexport class Logwell {\n private readonly config: ResolvedConfig;\n private readonly queue: BatchQueue;\n private readonly transport: HttpTransport;\n private readonly parentMetadata?: Record<string, unknown>;\n private stopped = false;\n\n constructor(config: LogwellConfig);\n constructor(config: LogwellConfig, queue: BatchQueue, parentMetadata?: Record<string, unknown>);\n constructor(\n config: LogwellConfig,\n existingQueue?: BatchQueue,\n parentMetadata?: Record<string, unknown>,\n ) {\n // Validate and apply defaults\n this.config = asResolvedConfig(validateConfig(config));\n this.parentMetadata = parentMetadata;\n\n // Create transport\n this.transport = new HttpTransport({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxRetries: this.config.maxRetries,\n });\n\n // Use existing queue (for child loggers) or create new one\n if (existingQueue) {\n this.queue = existingQueue;\n } else {\n const queueConfig: QueueConfig = {\n batchSize: this.config.batchSize,\n flushInterval: this.config.flushInterval,\n maxQueueSize: this.config.maxQueueSize,\n onError: this.config.onError,\n onFlush: this.config.onFlush,\n };\n this.queue = new BatchQueue((logs) => this.transport.send(logs), queueConfig);\n }\n }\n\n /**\n * Current number of logs waiting in the queue\n */\n get queueSize(): number {\n return this.queue.size;\n }\n\n /**\n * Log a message at the specified level\n */\n log(entry: LogEntry): void {\n if (this.stopped) return;\n\n const fullEntry: LogEntry = {\n ...entry,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n service: entry.service ?? this.config.service,\n metadata: this.mergeMetadata(entry.metadata),\n };\n\n this.queue.add(fullEntry);\n }\n\n /**\n * Log a debug message\n */\n debug(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'debug', message, metadata });\n }\n\n /**\n * Log an info message\n */\n info(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'info', message, metadata });\n }\n\n /**\n * Log a warning message\n */\n warn(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'warn', message, metadata });\n }\n\n /**\n * Log an error message\n */\n error(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'error', message, metadata });\n }\n\n /**\n * Log a fatal error message\n */\n fatal(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'fatal', message, metadata });\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n return this.queue.flush();\n }\n\n /**\n * Flush remaining logs and stop the client\n *\n * Call this before process exit to ensure all logs are sent.\n */\n async shutdown(): Promise<void> {\n this.stopped = true;\n await this.queue.shutdown();\n }\n\n /**\n * Create a child logger with additional context\n *\n * Child loggers share the same queue as the parent,\n * but can have their own service name and default metadata.\n *\n * @example\n * ```ts\n * const requestLogger = logger.child({\n * metadata: { requestId: req.id },\n * });\n * requestLogger.info('Request received');\n * ```\n */\n child(options: ChildLoggerOptions): Logwell {\n const childConfig: LogwellConfig = {\n ...this.config,\n service: options.service ?? this.config.service,\n };\n\n const childMetadata = {\n ...this.parentMetadata,\n ...options.metadata,\n };\n\n return new Logwell(childConfig, this.queue, childMetadata);\n }\n\n private mergeMetadata(\n entryMetadata?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!this.parentMetadata && !entryMetadata) {\n return undefined;\n }\n return {\n ...this.parentMetadata,\n ...entryMetadata,\n };\n }\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -204,11 +204,6 @@ type LogwellErrorCode = 'NETWORK_ERROR' | 'UNAUTHORIZED' | 'VALIDATION_ERROR' |
|
|
|
204
204
|
* throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);
|
|
205
205
|
* ```
|
|
206
206
|
*/
|
|
207
|
-
declare global {
|
|
208
|
-
interface ErrorConstructor {
|
|
209
|
-
captureStackTrace?(targetObject: object, constructorOpt?: NewableFunction): void;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
207
|
declare class LogwellError extends Error {
|
|
213
208
|
readonly code: LogwellErrorCode;
|
|
214
209
|
readonly statusCode?: number | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -204,11 +204,6 @@ type LogwellErrorCode = 'NETWORK_ERROR' | 'UNAUTHORIZED' | 'VALIDATION_ERROR' |
|
|
|
204
204
|
* throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);
|
|
205
205
|
* ```
|
|
206
206
|
*/
|
|
207
|
-
declare global {
|
|
208
|
-
interface ErrorConstructor {
|
|
209
|
-
captureStackTrace?(targetObject: object, constructorOpt?: NewableFunction): void;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
207
|
declare class LogwellError extends Error {
|
|
213
208
|
readonly code: LogwellErrorCode;
|
|
214
209
|
readonly statusCode?: number | undefined;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var i=class r extends Error{constructor(t,n,o,h=false){super(t);this.code=n;this.statusCode=o;this.retryable=h;this.name="LogwellError",Error.captureStackTrace?.(this,r);}};var s={batchSize:50,flushInterval:5e3,maxQueueSize:1e3,maxRetries:3},d=/^lw_[A-Za-z0-9_-]{32}$/;function f(r){return !r||typeof r!="string"?false:d.test(r)}function
|
|
1
|
+
var i=class r extends Error{constructor(t,n,o,h=false){super(t);this.code=n;this.statusCode=o;this.retryable=h;this.name="LogwellError",Error.captureStackTrace?.(this,r);}};var s={batchSize:50,flushInterval:5e3,maxQueueSize:1e3,maxRetries:3},d=/^lw_[A-Za-z0-9_-]{32}$/;function f(r){return !r||typeof r!="string"?false:d.test(r)}function g(r){try{return new URL(r),!0}catch{return false}}function p(r){if(!r.apiKey)throw new i("apiKey is required","INVALID_CONFIG");if(!r.endpoint)throw new i("endpoint is required","INVALID_CONFIG");if(!f(r.apiKey))throw new i("Invalid API key format. Expected: lw_[32 characters]","INVALID_CONFIG");if(!g(r.endpoint))throw new i("Invalid endpoint URL","INVALID_CONFIG");if(r.batchSize!==void 0&&r.batchSize<=0)throw new i("batchSize must be positive","INVALID_CONFIG");if(r.flushInterval!==void 0&&r.flushInterval<=0)throw new i("flushInterval must be positive","INVALID_CONFIG");if(r.maxQueueSize!==void 0&&r.maxQueueSize<=0)throw new i("maxQueueSize must be positive","INVALID_CONFIG");if(r.maxRetries!==void 0&&r.maxRetries<0)throw new i("maxRetries must be non-negative","INVALID_CONFIG");return {apiKey:r.apiKey,endpoint:r.endpoint,service:r.service,batchSize:r.batchSize??s.batchSize,flushInterval:r.flushInterval??s.flushInterval,maxQueueSize:r.maxQueueSize??s.maxQueueSize,maxRetries:r.maxRetries??s.maxRetries,onError:r.onError,onFlush:r.onFlush}}var a=class{constructor(e,t){this.sendBatch=e;this.config=t;}queue=[];flushTimer=null;flushing=false;stopped=false;get size(){return this.queue.length}add(e){if(!this.stopped){if(this.queue.length>=this.config.maxQueueSize){let t=this.queue.shift();this.config.onError?.(new i(`Queue overflow. Dropped log: ${t?.message.substring(0,50)}...`,"QUEUE_OVERFLOW"));}this.queue.push(e),!this.flushTimer&&!this.stopped&&this.startTimer(),this.queue.length>=this.config.batchSize&&this.flush();}}async flush(){if(this.flushing||this.queue.length===0)return null;this.flushing=true,this.stopTimer();let e=this.queue.splice(0),t=e.length;try{let n=await this.sendBatch(e);return this.config.onFlush?.(t),this.queue.length>0&&!this.stopped&&this.startTimer(),n}catch(n){return this.queue.unshift(...e),this.config.onError?.(n),this.stopped||this.startTimer(),null}finally{this.flushing=false;}}async shutdown(){this.stopped||(this.stopped=true,this.stopTimer(),this.queue.length>0&&(this.flushing=false,await this.flush()));}startTimer(){this.flushTimer=setTimeout(()=>{this.flush();},this.config.flushInterval);}stopTimer(){this.flushTimer&&(clearTimeout(this.flushTimer),this.flushTimer=null);}};function c(r,e=100){let t=Math.min(e*2**r,1e4),n=Math.random()*t*.3;return new Promise(o=>setTimeout(o,t+n))}var u=class{constructor(e){this.config=e;this.ingestUrl=`${e.endpoint}/v1/ingest`;}ingestUrl;async send(e){let t=new i("Max retries exceeded","NETWORK_ERROR",void 0,true);for(let n=0;n<=this.config.maxRetries;n++)try{return await this.doRequest(e)}catch(o){if(t=o,!t.retryable)throw t;n<this.config.maxRetries&&await c(n);}throw t}async doRequest(e){let t;try{t=await fetch(this.ingestUrl,{method:"POST",headers:{Authorization:`Bearer ${this.config.apiKey}`,"Content-Type":"application/json"},body:JSON.stringify(e)});}catch(n){throw new i(`Network error: ${n.message}`,"NETWORK_ERROR",void 0,true)}if(!t.ok){let n=await this.tryParseError(t);throw this.createError(t.status,n)}return await t.json()}async tryParseError(e){try{let t=await e.json();return t.message||t.error||"Unknown error"}catch{return `HTTP ${e.status}`}}createError(e,t){switch(e){case 401:return new i(`Unauthorized: ${t}`,"UNAUTHORIZED",e,false);case 400:return new i(`Validation error: ${t}`,"VALIDATION_ERROR",e,false);case 429:return new i(`Rate limited: ${t}`,"RATE_LIMITED",e,true);default:return e>=500?new i(`Server error: ${t}`,"SERVER_ERROR",e,true):new i(`HTTP error ${e}: ${t}`,"SERVER_ERROR",e,false)}}};var l=class r{config;queue;transport;parentMetadata;stopped=false;constructor(e,t,n){if(this.config=p(e),this.parentMetadata=n,this.transport=new u({endpoint:this.config.endpoint,apiKey:this.config.apiKey,maxRetries:this.config.maxRetries}),t)this.queue=t;else {let o={batchSize:this.config.batchSize,flushInterval:this.config.flushInterval,maxQueueSize:this.config.maxQueueSize,onError:this.config.onError,onFlush:this.config.onFlush};this.queue=new a(h=>this.transport.send(h),o);}}get queueSize(){return this.queue.size}log(e){if(this.stopped)return;let t={...e,timestamp:e.timestamp??new Date().toISOString(),service:e.service??this.config.service,metadata:this.mergeMetadata(e.metadata)};this.queue.add(t);}debug(e,t){this.log({level:"debug",message:e,metadata:t});}info(e,t){this.log({level:"info",message:e,metadata:t});}warn(e,t){this.log({level:"warn",message:e,metadata:t});}error(e,t){this.log({level:"error",message:e,metadata:t});}fatal(e,t){this.log({level:"fatal",message:e,metadata:t});}async flush(){return this.queue.flush()}async shutdown(){this.stopped=true,await this.queue.shutdown();}child(e){let t={...this.config,service:e.service??this.config.service},n={...this.parentMetadata,...e.metadata};return new r(t,this.queue,n)}mergeMetadata(e){if(!(!this.parentMetadata&&!e))return {...this.parentMetadata,...e}}};export{l as Logwell,i as LogwellError};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/queue.ts","../src/transport.ts","../src/client.ts"],"names":["LogwellError","_LogwellError","message","code","statusCode","retryable","DEFAULT_CONFIG","API_KEY_REGEX","validateApiKeyFormat","apiKey","isValidUrl","url","validateConfig","config","BatchQueue","sendBatch","entry","dropped","batch","count","response","error","delay","attempt","baseDelay","ms","jitter","resolve","HttpTransport","logs","lastError","errorBody","body","status","Logwell","_Logwell","existingQueue","parentMetadata","queueConfig","fullEntry","metadata","options","childConfig","childMetadata","entryMetadata"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Error codes for Logwell SDK errors\n */\nexport type LogwellErrorCode =\n | 'NETWORK_ERROR'\n | 'UNAUTHORIZED'\n | 'VALIDATION_ERROR'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR'\n | 'QUEUE_OVERFLOW'\n | 'INVALID_CONFIG';\n\n/**\n * Custom error class for Logwell SDK errors\n *\n * @example\n * ```ts\n * throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);\n * ```\n */\n// V8 specific type for captureStackTrace\ndeclare global {\n interface ErrorConstructor {\n captureStackTrace?(targetObject: object, constructorOpt?: NewableFunction): void;\n }\n}\n\nexport class LogwellError extends Error {\n /**\n * Creates a new LogwellError\n *\n * @param message - Human-readable error message\n * @param code - Error code for programmatic handling\n * @param statusCode - HTTP status code if applicable\n * @param retryable - Whether the operation can be retried\n */\n constructor(\n message: string,\n public readonly code: LogwellErrorCode,\n public readonly statusCode?: number,\n public readonly retryable: boolean = false,\n ) {\n super(message);\n this.name = 'LogwellError';\n\n // Maintains proper stack trace for where our error was thrown (V8 only)\n Error.captureStackTrace?.(this, LogwellError);\n }\n}\n","import { LogwellError } from './errors';\nimport type { LogwellConfig } from './types';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n batchSize: 50,\n flushInterval: 5000,\n maxQueueSize: 1000,\n maxRetries: 3,\n} as const;\n\n/**\n * API key format regex: lw_[32 alphanumeric chars including - and _]\n */\nexport const API_KEY_REGEX = /^lw_[A-Za-z0-9_-]{32}$/;\n\n/**\n * Validates API key format\n *\n * @param apiKey - API key to validate\n * @returns true if valid format, false otherwise\n */\nexport function validateApiKeyFormat(apiKey: string): boolean {\n if (!apiKey || typeof apiKey !== 'string') {\n return false;\n }\n return API_KEY_REGEX.test(apiKey);\n}\n\n/**\n * Validates a URL string\n *\n * @param url - URL string to validate\n * @returns true if valid URL, false otherwise\n */\nfunction isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates configuration and returns merged config with defaults\n *\n * @param config - Partial configuration to validate\n * @returns Complete configuration with defaults applied\n * @throws LogwellError if configuration is invalid\n */\nexport function validateConfig(config: Partial<LogwellConfig>): LogwellConfig {\n // Validate required fields\n if (!config.apiKey) {\n throw new LogwellError('apiKey is required', 'INVALID_CONFIG');\n }\n\n if (!config.endpoint) {\n throw new LogwellError('endpoint is required', 'INVALID_CONFIG');\n }\n\n // Validate API key format\n if (!validateApiKeyFormat(config.apiKey)) {\n throw new LogwellError(\n 'Invalid API key format. Expected: lw_[32 characters]',\n 'INVALID_CONFIG',\n );\n }\n\n // Validate endpoint URL\n if (!isValidUrl(config.endpoint)) {\n throw new LogwellError('Invalid endpoint URL', 'INVALID_CONFIG');\n }\n\n // Validate numeric options\n if (config.batchSize !== undefined && config.batchSize <= 0) {\n throw new LogwellError('batchSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.flushInterval !== undefined && config.flushInterval <= 0) {\n throw new LogwellError('flushInterval must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxQueueSize !== undefined && config.maxQueueSize <= 0) {\n throw new LogwellError('maxQueueSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxRetries !== undefined && config.maxRetries < 0) {\n throw new LogwellError('maxRetries must be non-negative', 'INVALID_CONFIG');\n }\n\n // Return merged config with defaults\n return {\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n service: config.service,\n batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,\n flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,\n maxRetries: config.maxRetries ?? DEFAULT_CONFIG.maxRetries,\n onError: config.onError,\n onFlush: config.onFlush,\n };\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Callback type for sending batched logs\n */\nexport type SendBatchFn = (logs: LogEntry[]) => Promise<IngestResponse>;\n\n/**\n * Queue configuration options\n */\nexport interface QueueConfig {\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Batch queue for buffering and sending logs\n *\n * Features:\n * - Automatic flush on batch size threshold\n * - Automatic flush on time interval\n * - Queue overflow protection (drops oldest)\n * - Re-queue on send failure\n * - Graceful shutdown\n */\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private flushing = false;\n private stopped = false;\n\n constructor(\n private sendBatch: SendBatchFn,\n private config: QueueConfig,\n ) {}\n\n /**\n * Current number of logs in the queue\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Add a log entry to the queue\n *\n * Triggers flush if batch size is reached.\n * Drops oldest log if queue overflows.\n */\n add(entry: LogEntry): void {\n if (this.stopped) {\n return;\n }\n\n // Handle queue overflow\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.onError?.(\n new LogwellError(\n `Queue overflow. Dropped log: ${dropped?.message.substring(0, 50)}...`,\n 'QUEUE_OVERFLOW',\n ),\n );\n }\n\n this.queue.push(entry);\n\n // Start timer on first entry\n if (!this.flushTimer && !this.stopped) {\n this.startTimer();\n }\n\n // Flush immediately if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n // Prevent concurrent flushes\n if (this.flushing || this.queue.length === 0) {\n return null;\n }\n\n this.flushing = true;\n this.stopTimer();\n\n // Take current batch\n const batch = this.queue.splice(0);\n const count = batch.length;\n\n try {\n const response = await this.sendBatch(batch);\n this.config.onFlush?.(count);\n\n // Restart timer if more logs remain (added during flush)\n if (this.queue.length > 0 && !this.stopped) {\n this.startTimer();\n }\n\n return response;\n } catch (error) {\n // Re-queue failed logs at the front\n this.queue.unshift(...batch);\n this.config.onError?.(error as Error);\n\n // Restart timer to retry\n if (!this.stopped) {\n this.startTimer();\n }\n\n return null;\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining logs and stop the queue\n */\n async shutdown(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n this.stopTimer();\n\n // Flush all remaining logs\n if (this.queue.length > 0) {\n this.flushing = false; // Reset flushing flag\n await this.flush();\n }\n }\n\n private startTimer(): void {\n this.flushTimer = setTimeout(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n private stopTimer(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Transport configuration\n */\nexport interface TransportConfig {\n endpoint: string;\n apiKey: string;\n maxRetries: number;\n timeout?: number;\n}\n\n/**\n * Delay helper with exponential backoff\n */\nfunction delay(attempt: number, baseDelay = 100): Promise<void> {\n const ms = Math.min(baseDelay * 2 ** attempt, 10000);\n const jitter = Math.random() * ms * 0.3;\n return new Promise((resolve) => setTimeout(resolve, ms + jitter));\n}\n\n/**\n * Check if error is retryable based on status code\n */\nfunction isRetryableStatus(status: number): boolean {\n return status >= 500 || status === 429;\n}\n\n/**\n * HTTP transport for sending logs to Logwell server\n *\n * Features:\n * - Automatic retry with exponential backoff\n * - Error classification with retryable flag\n * - Proper error handling for all HTTP status codes\n */\nexport class HttpTransport {\n private readonly ingestUrl: string;\n\n constructor(private config: TransportConfig) {\n this.ingestUrl = `${config.endpoint}/v1/ingest`;\n }\n\n /**\n * Send logs to the Logwell server\n *\n * @param logs - Array of log entries to send\n * @returns Response with accepted/rejected counts\n * @throws LogwellError on failure after all retries\n */\n async send(logs: LogEntry[]): Promise<IngestResponse> {\n let lastError: LogwellError | null = null;\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n const response = await this.doRequest(logs);\n return response;\n } catch (error) {\n lastError = error as LogwellError;\n\n // Don't retry non-retryable errors\n if (!lastError.retryable) {\n throw lastError;\n }\n\n // Don't delay after the last attempt\n if (attempt < this.config.maxRetries) {\n await delay(attempt);\n }\n }\n }\n\n throw lastError!;\n }\n\n private async doRequest(logs: LogEntry[]): Promise<IngestResponse> {\n let response: Response;\n\n try {\n response = await fetch(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(logs),\n });\n } catch (error) {\n // Network error (fetch failed)\n throw new LogwellError(\n `Network error: ${(error as Error).message}`,\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n }\n\n // Handle error responses\n if (!response.ok) {\n const errorBody = await this.tryParseError(response);\n throw this.createError(response.status, errorBody);\n }\n\n // Parse successful response\n return (await response.json()) as IngestResponse;\n }\n\n private async tryParseError(response: Response): Promise<string> {\n try {\n const body = await response.json();\n return body.message || body.error || 'Unknown error';\n } catch {\n return `HTTP ${response.status}`;\n }\n }\n\n private createError(status: number, message: string): LogwellError {\n switch (status) {\n case 401:\n return new LogwellError(\n `Unauthorized: ${message}`,\n 'UNAUTHORIZED',\n status,\n false,\n );\n case 400:\n return new LogwellError(\n `Validation error: ${message}`,\n 'VALIDATION_ERROR',\n status,\n false,\n );\n case 429:\n return new LogwellError(\n `Rate limited: ${message}`,\n 'RATE_LIMITED',\n status,\n true,\n );\n default:\n if (status >= 500) {\n return new LogwellError(\n `Server error: ${message}`,\n 'SERVER_ERROR',\n status,\n true,\n );\n }\n return new LogwellError(\n `HTTP error ${status}: ${message}`,\n 'SERVER_ERROR',\n status,\n false,\n );\n }\n }\n}\n","import { validateConfig } from './config';\nimport { BatchQueue, type QueueConfig } from './queue';\nimport { HttpTransport } from './transport';\nimport type { IngestResponse, LogEntry, LogwellConfig } from './types';\n\n/**\n * Child logger options\n */\nexport interface ChildLoggerOptions {\n service?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Internal resolved config with all defaults applied\n */\ninterface ResolvedConfig {\n apiKey: string;\n endpoint: string;\n service?: string;\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n maxRetries: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Asserts that config has all required fields after validation\n */\nfunction asResolvedConfig(config: LogwellConfig): ResolvedConfig {\n return config as ResolvedConfig;\n}\n\n/**\n * Main Logwell client class\n *\n * Provides methods for logging at different levels with automatic\n * batching, retry, and queue management.\n *\n * @example\n * ```ts\n * const logger = new Logwell({\n * apiKey: 'lw_xxx',\n * endpoint: 'https://logs.example.com',\n * service: 'my-app',\n * });\n *\n * logger.info('User logged in', { userId: '123' });\n * await logger.shutdown();\n * ```\n */\nexport class Logwell {\n private readonly config: ResolvedConfig;\n private readonly queue: BatchQueue;\n private readonly transport: HttpTransport;\n private readonly parentMetadata?: Record<string, unknown>;\n private stopped = false;\n\n constructor(config: LogwellConfig);\n constructor(config: LogwellConfig, queue: BatchQueue, parentMetadata?: Record<string, unknown>);\n constructor(\n config: LogwellConfig,\n existingQueue?: BatchQueue,\n parentMetadata?: Record<string, unknown>,\n ) {\n // Validate and apply defaults\n this.config = asResolvedConfig(validateConfig(config));\n this.parentMetadata = parentMetadata;\n\n // Create transport\n this.transport = new HttpTransport({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxRetries: this.config.maxRetries,\n });\n\n // Use existing queue (for child loggers) or create new one\n if (existingQueue) {\n this.queue = existingQueue;\n } else {\n const queueConfig: QueueConfig = {\n batchSize: this.config.batchSize,\n flushInterval: this.config.flushInterval,\n maxQueueSize: this.config.maxQueueSize,\n onError: this.config.onError,\n onFlush: this.config.onFlush,\n };\n this.queue = new BatchQueue((logs) => this.transport.send(logs), queueConfig);\n }\n }\n\n /**\n * Current number of logs waiting in the queue\n */\n get queueSize(): number {\n return this.queue.size;\n }\n\n /**\n * Log a message at the specified level\n */\n log(entry: LogEntry): void {\n if (this.stopped) return;\n\n const fullEntry: LogEntry = {\n ...entry,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n service: entry.service ?? this.config.service,\n metadata: this.mergeMetadata(entry.metadata),\n };\n\n this.queue.add(fullEntry);\n }\n\n /**\n * Log a debug message\n */\n debug(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'debug', message, metadata });\n }\n\n /**\n * Log an info message\n */\n info(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'info', message, metadata });\n }\n\n /**\n * Log a warning message\n */\n warn(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'warn', message, metadata });\n }\n\n /**\n * Log an error message\n */\n error(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'error', message, metadata });\n }\n\n /**\n * Log a fatal error message\n */\n fatal(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'fatal', message, metadata });\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n return this.queue.flush();\n }\n\n /**\n * Flush remaining logs and stop the client\n *\n * Call this before process exit to ensure all logs are sent.\n */\n async shutdown(): Promise<void> {\n this.stopped = true;\n await this.queue.shutdown();\n }\n\n /**\n * Create a child logger with additional context\n *\n * Child loggers share the same queue as the parent,\n * but can have their own service name and default metadata.\n *\n * @example\n * ```ts\n * const requestLogger = logger.child({\n * metadata: { requestId: req.id },\n * });\n * requestLogger.info('Request received');\n * ```\n */\n child(options: ChildLoggerOptions): Logwell {\n const childConfig: LogwellConfig = {\n ...this.config,\n service: options.service ?? this.config.service,\n };\n\n const childMetadata = {\n ...this.parentMetadata,\n ...options.metadata,\n };\n\n return new Logwell(childConfig, this.queue, childMetadata);\n }\n\n private mergeMetadata(\n entryMetadata?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!this.parentMetadata && !entryMetadata) {\n return undefined;\n }\n return {\n ...this.parentMetadata,\n ...entryMetadata,\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/config.ts","../src/queue.ts","../src/transport.ts","../src/client.ts"],"names":["LogwellError","_LogwellError","message","code","statusCode","retryable","DEFAULT_CONFIG","API_KEY_REGEX","validateApiKeyFormat","apiKey","isValidUrl","url","validateConfig","config","BatchQueue","sendBatch","entry","dropped","batch","count","response","error","delay","attempt","baseDelay","ms","jitter","resolve","HttpTransport","logs","lastError","errorBody","body","status","Logwell","_Logwell","existingQueue","parentMetadata","queueConfig","fullEntry","metadata","options","childConfig","childMetadata","entryMetadata"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Error codes for Logwell SDK errors\n */\nexport type LogwellErrorCode =\n | 'NETWORK_ERROR'\n | 'UNAUTHORIZED'\n | 'VALIDATION_ERROR'\n | 'RATE_LIMITED'\n | 'SERVER_ERROR'\n | 'QUEUE_OVERFLOW'\n | 'INVALID_CONFIG';\n\n/**\n * Custom error class for Logwell SDK errors\n *\n * @example\n * ```ts\n * throw new LogwellError('Invalid API key', 'UNAUTHORIZED', 401, false);\n * ```\n */\nexport class LogwellError extends Error {\n /**\n * Creates a new LogwellError\n *\n * @param message - Human-readable error message\n * @param code - Error code for programmatic handling\n * @param statusCode - HTTP status code if applicable\n * @param retryable - Whether the operation can be retried\n */\n constructor(\n message: string,\n public readonly code: LogwellErrorCode,\n public readonly statusCode?: number,\n public readonly retryable: boolean = false,\n ) {\n super(message);\n this.name = 'LogwellError';\n\n // Maintains proper stack trace for where our error was thrown (V8 only)\n // Use type assertion to avoid global augmentation (required for JSR compatibility)\n const ErrorWithCapture = Error as unknown as {\n captureStackTrace?: (target: object, ctor?: NewableFunction) => void;\n };\n ErrorWithCapture.captureStackTrace?.(this, LogwellError);\n }\n}\n","import { LogwellError } from './errors';\nimport type { LogwellConfig } from './types';\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n batchSize: 50,\n flushInterval: 5000,\n maxQueueSize: 1000,\n maxRetries: 3,\n} as const;\n\n/**\n * API key format regex: lw_[32 alphanumeric chars including - and _]\n */\nexport const API_KEY_REGEX = /^lw_[A-Za-z0-9_-]{32}$/;\n\n/**\n * Validates API key format\n *\n * @param apiKey - API key to validate\n * @returns true if valid format, false otherwise\n */\nexport function validateApiKeyFormat(apiKey: string): boolean {\n if (!apiKey || typeof apiKey !== 'string') {\n return false;\n }\n return API_KEY_REGEX.test(apiKey);\n}\n\n/**\n * Validates a URL string\n *\n * @param url - URL string to validate\n * @returns true if valid URL, false otherwise\n */\nfunction isValidUrl(url: string): boolean {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates configuration and returns merged config with defaults\n *\n * @param config - Partial configuration to validate\n * @returns Complete configuration with defaults applied\n * @throws LogwellError if configuration is invalid\n */\nexport function validateConfig(config: Partial<LogwellConfig>): LogwellConfig {\n // Validate required fields\n if (!config.apiKey) {\n throw new LogwellError('apiKey is required', 'INVALID_CONFIG');\n }\n\n if (!config.endpoint) {\n throw new LogwellError('endpoint is required', 'INVALID_CONFIG');\n }\n\n // Validate API key format\n if (!validateApiKeyFormat(config.apiKey)) {\n throw new LogwellError(\n 'Invalid API key format. Expected: lw_[32 characters]',\n 'INVALID_CONFIG',\n );\n }\n\n // Validate endpoint URL\n if (!isValidUrl(config.endpoint)) {\n throw new LogwellError('Invalid endpoint URL', 'INVALID_CONFIG');\n }\n\n // Validate numeric options\n if (config.batchSize !== undefined && config.batchSize <= 0) {\n throw new LogwellError('batchSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.flushInterval !== undefined && config.flushInterval <= 0) {\n throw new LogwellError('flushInterval must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxQueueSize !== undefined && config.maxQueueSize <= 0) {\n throw new LogwellError('maxQueueSize must be positive', 'INVALID_CONFIG');\n }\n\n if (config.maxRetries !== undefined && config.maxRetries < 0) {\n throw new LogwellError('maxRetries must be non-negative', 'INVALID_CONFIG');\n }\n\n // Return merged config with defaults\n return {\n apiKey: config.apiKey,\n endpoint: config.endpoint,\n service: config.service,\n batchSize: config.batchSize ?? DEFAULT_CONFIG.batchSize,\n flushInterval: config.flushInterval ?? DEFAULT_CONFIG.flushInterval,\n maxQueueSize: config.maxQueueSize ?? DEFAULT_CONFIG.maxQueueSize,\n maxRetries: config.maxRetries ?? DEFAULT_CONFIG.maxRetries,\n onError: config.onError,\n onFlush: config.onFlush,\n };\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Callback type for sending batched logs\n */\nexport type SendBatchFn = (logs: LogEntry[]) => Promise<IngestResponse>;\n\n/**\n * Queue configuration options\n */\nexport interface QueueConfig {\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Batch queue for buffering and sending logs\n *\n * Features:\n * - Automatic flush on batch size threshold\n * - Automatic flush on time interval\n * - Queue overflow protection (drops oldest)\n * - Re-queue on send failure\n * - Graceful shutdown\n */\nexport class BatchQueue {\n private queue: LogEntry[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private flushing = false;\n private stopped = false;\n\n constructor(\n private sendBatch: SendBatchFn,\n private config: QueueConfig,\n ) {}\n\n /**\n * Current number of logs in the queue\n */\n get size(): number {\n return this.queue.length;\n }\n\n /**\n * Add a log entry to the queue\n *\n * Triggers flush if batch size is reached.\n * Drops oldest log if queue overflows.\n */\n add(entry: LogEntry): void {\n if (this.stopped) {\n return;\n }\n\n // Handle queue overflow\n if (this.queue.length >= this.config.maxQueueSize) {\n const dropped = this.queue.shift();\n this.config.onError?.(\n new LogwellError(\n `Queue overflow. Dropped log: ${dropped?.message.substring(0, 50)}...`,\n 'QUEUE_OVERFLOW',\n ),\n );\n }\n\n this.queue.push(entry);\n\n // Start timer on first entry\n if (!this.flushTimer && !this.stopped) {\n this.startTimer();\n }\n\n // Flush immediately if batch size reached\n if (this.queue.length >= this.config.batchSize) {\n void this.flush();\n }\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n // Prevent concurrent flushes\n if (this.flushing || this.queue.length === 0) {\n return null;\n }\n\n this.flushing = true;\n this.stopTimer();\n\n // Take current batch\n const batch = this.queue.splice(0);\n const count = batch.length;\n\n try {\n const response = await this.sendBatch(batch);\n this.config.onFlush?.(count);\n\n // Restart timer if more logs remain (added during flush)\n if (this.queue.length > 0 && !this.stopped) {\n this.startTimer();\n }\n\n return response;\n } catch (error) {\n // Re-queue failed logs at the front\n this.queue.unshift(...batch);\n this.config.onError?.(error as Error);\n\n // Restart timer to retry\n if (!this.stopped) {\n this.startTimer();\n }\n\n return null;\n } finally {\n this.flushing = false;\n }\n }\n\n /**\n * Flush remaining logs and stop the queue\n */\n async shutdown(): Promise<void> {\n if (this.stopped) {\n return;\n }\n\n this.stopped = true;\n this.stopTimer();\n\n // Flush all remaining logs\n if (this.queue.length > 0) {\n this.flushing = false; // Reset flushing flag\n await this.flush();\n }\n }\n\n private startTimer(): void {\n this.flushTimer = setTimeout(() => {\n void this.flush();\n }, this.config.flushInterval);\n }\n\n private stopTimer(): void {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n }\n}\n","import { LogwellError } from './errors';\nimport type { IngestResponse, LogEntry } from './types';\n\n/**\n * Transport configuration\n */\nexport interface TransportConfig {\n endpoint: string;\n apiKey: string;\n maxRetries: number;\n timeout?: number;\n}\n\n/**\n * Delay helper with exponential backoff\n */\nfunction delay(attempt: number, baseDelay = 100): Promise<void> {\n const ms = Math.min(baseDelay * 2 ** attempt, 10000);\n const jitter = Math.random() * ms * 0.3;\n return new Promise((resolve) => setTimeout(resolve, ms + jitter));\n}\n\n/**\n * HTTP transport for sending logs to Logwell server\n *\n * Features:\n * - Automatic retry with exponential backoff\n * - Error classification with retryable flag\n * - Proper error handling for all HTTP status codes\n */\nexport class HttpTransport {\n private readonly ingestUrl: string;\n\n constructor(private config: TransportConfig) {\n this.ingestUrl = `${config.endpoint}/v1/ingest`;\n }\n\n /**\n * Send logs to the Logwell server\n *\n * @param logs - Array of log entries to send\n * @returns Response with accepted/rejected counts\n * @throws LogwellError on failure after all retries\n */\n async send(logs: LogEntry[]): Promise<IngestResponse> {\n let lastError: LogwellError = new LogwellError(\n 'Max retries exceeded',\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n\n for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) {\n try {\n return await this.doRequest(logs);\n } catch (error) {\n lastError = error as LogwellError;\n\n // Don't retry non-retryable errors\n if (!lastError.retryable) {\n throw lastError;\n }\n\n // Don't delay after the last attempt\n if (attempt < this.config.maxRetries) {\n await delay(attempt);\n }\n }\n }\n\n throw lastError;\n }\n\n private async doRequest(logs: LogEntry[]): Promise<IngestResponse> {\n let response: Response;\n\n try {\n response = await fetch(this.ingestUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.config.apiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(logs),\n });\n } catch (error) {\n // Network error (fetch failed)\n throw new LogwellError(\n `Network error: ${(error as Error).message}`,\n 'NETWORK_ERROR',\n undefined,\n true,\n );\n }\n\n // Handle error responses\n if (!response.ok) {\n const errorBody = await this.tryParseError(response);\n throw this.createError(response.status, errorBody);\n }\n\n // Parse successful response\n return (await response.json()) as IngestResponse;\n }\n\n private async tryParseError(response: Response): Promise<string> {\n try {\n const body = await response.json();\n return body.message || body.error || 'Unknown error';\n } catch {\n return `HTTP ${response.status}`;\n }\n }\n\n private createError(status: number, message: string): LogwellError {\n switch (status) {\n case 401:\n return new LogwellError(`Unauthorized: ${message}`, 'UNAUTHORIZED', status, false);\n case 400:\n return new LogwellError(`Validation error: ${message}`, 'VALIDATION_ERROR', status, false);\n case 429:\n return new LogwellError(`Rate limited: ${message}`, 'RATE_LIMITED', status, true);\n default:\n if (status >= 500) {\n return new LogwellError(`Server error: ${message}`, 'SERVER_ERROR', status, true);\n }\n return new LogwellError(`HTTP error ${status}: ${message}`, 'SERVER_ERROR', status, false);\n }\n }\n}\n","import { validateConfig } from './config';\nimport { BatchQueue, type QueueConfig } from './queue';\nimport { HttpTransport } from './transport';\nimport type { IngestResponse, LogEntry, LogwellConfig } from './types';\n\n/**\n * Child logger options\n */\nexport interface ChildLoggerOptions {\n service?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Internal resolved config with all defaults applied\n */\ninterface ResolvedConfig {\n apiKey: string;\n endpoint: string;\n service?: string;\n batchSize: number;\n flushInterval: number;\n maxQueueSize: number;\n maxRetries: number;\n onError?: (error: Error) => void;\n onFlush?: (count: number) => void;\n}\n\n/**\n * Asserts that config has all required fields after validation\n */\nfunction asResolvedConfig(config: LogwellConfig): ResolvedConfig {\n return config as ResolvedConfig;\n}\n\n/**\n * Main Logwell client class\n *\n * Provides methods for logging at different levels with automatic\n * batching, retry, and queue management.\n *\n * @example\n * ```ts\n * const logger = new Logwell({\n * apiKey: 'lw_xxx',\n * endpoint: 'https://logs.example.com',\n * service: 'my-app',\n * });\n *\n * logger.info('User logged in', { userId: '123' });\n * await logger.shutdown();\n * ```\n */\nexport class Logwell {\n private readonly config: ResolvedConfig;\n private readonly queue: BatchQueue;\n private readonly transport: HttpTransport;\n private readonly parentMetadata?: Record<string, unknown>;\n private stopped = false;\n\n constructor(config: LogwellConfig);\n constructor(config: LogwellConfig, queue: BatchQueue, parentMetadata?: Record<string, unknown>);\n constructor(\n config: LogwellConfig,\n existingQueue?: BatchQueue,\n parentMetadata?: Record<string, unknown>,\n ) {\n // Validate and apply defaults\n this.config = asResolvedConfig(validateConfig(config));\n this.parentMetadata = parentMetadata;\n\n // Create transport\n this.transport = new HttpTransport({\n endpoint: this.config.endpoint,\n apiKey: this.config.apiKey,\n maxRetries: this.config.maxRetries,\n });\n\n // Use existing queue (for child loggers) or create new one\n if (existingQueue) {\n this.queue = existingQueue;\n } else {\n const queueConfig: QueueConfig = {\n batchSize: this.config.batchSize,\n flushInterval: this.config.flushInterval,\n maxQueueSize: this.config.maxQueueSize,\n onError: this.config.onError,\n onFlush: this.config.onFlush,\n };\n this.queue = new BatchQueue((logs) => this.transport.send(logs), queueConfig);\n }\n }\n\n /**\n * Current number of logs waiting in the queue\n */\n get queueSize(): number {\n return this.queue.size;\n }\n\n /**\n * Log a message at the specified level\n */\n log(entry: LogEntry): void {\n if (this.stopped) return;\n\n const fullEntry: LogEntry = {\n ...entry,\n timestamp: entry.timestamp ?? new Date().toISOString(),\n service: entry.service ?? this.config.service,\n metadata: this.mergeMetadata(entry.metadata),\n };\n\n this.queue.add(fullEntry);\n }\n\n /**\n * Log a debug message\n */\n debug(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'debug', message, metadata });\n }\n\n /**\n * Log an info message\n */\n info(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'info', message, metadata });\n }\n\n /**\n * Log a warning message\n */\n warn(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'warn', message, metadata });\n }\n\n /**\n * Log an error message\n */\n error(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'error', message, metadata });\n }\n\n /**\n * Log a fatal error message\n */\n fatal(message: string, metadata?: Record<string, unknown>): void {\n this.log({ level: 'fatal', message, metadata });\n }\n\n /**\n * Flush all queued logs immediately\n *\n * @returns Response from the server, or null if queue was empty\n */\n async flush(): Promise<IngestResponse | null> {\n return this.queue.flush();\n }\n\n /**\n * Flush remaining logs and stop the client\n *\n * Call this before process exit to ensure all logs are sent.\n */\n async shutdown(): Promise<void> {\n this.stopped = true;\n await this.queue.shutdown();\n }\n\n /**\n * Create a child logger with additional context\n *\n * Child loggers share the same queue as the parent,\n * but can have their own service name and default metadata.\n *\n * @example\n * ```ts\n * const requestLogger = logger.child({\n * metadata: { requestId: req.id },\n * });\n * requestLogger.info('Request received');\n * ```\n */\n child(options: ChildLoggerOptions): Logwell {\n const childConfig: LogwellConfig = {\n ...this.config,\n service: options.service ?? this.config.service,\n };\n\n const childMetadata = {\n ...this.parentMetadata,\n ...options.metadata,\n };\n\n return new Logwell(childConfig, this.queue, childMetadata);\n }\n\n private mergeMetadata(\n entryMetadata?: Record<string, unknown>,\n ): Record<string, unknown> | undefined {\n if (!this.parentMetadata && !entryMetadata) {\n return undefined;\n }\n return {\n ...this.parentMetadata,\n ...entryMetadata,\n };\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "logwell",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Official TypeScript SDK for Logwell logging platform",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"logging",
|
|
7
|
+
"logs",
|
|
8
|
+
"observability",
|
|
9
|
+
"logwell",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
6
12
|
"license": "MIT",
|
|
7
13
|
"author": "Logwell",
|
|
8
14
|
"repository": {
|
|
9
15
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/Divkix/Logwell",
|
|
11
17
|
"directory": "sdks/typescript"
|
|
12
18
|
},
|
|
13
|
-
"homepage": "https://github.com/
|
|
19
|
+
"homepage": "https://github.com/Divkix/Logwell/tree/main/sdks/typescript#readme",
|
|
14
20
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/
|
|
21
|
+
"url": "https://github.com/Divkix/Logwell/issues"
|
|
16
22
|
},
|
|
17
23
|
"type": "module",
|
|
18
24
|
"main": "./dist/index.cjs",
|