@lelemondev/sdk 0.6.0 → 0.6.2
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/express.js.map +1 -1
- package/dist/express.mjs.map +1 -1
- package/dist/hono.js.map +1 -1
- package/dist/hono.mjs.map +1 -1
- package/dist/index.js +117 -39
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +117 -39
- package/dist/index.mjs.map +1 -1
- package/dist/integrations.js.map +1 -1
- package/dist/integrations.mjs.map +1 -1
- package/dist/lambda.js.map +1 -1
- package/dist/lambda.mjs.map +1 -1
- package/dist/next.js.map +1 -1
- package/dist/next.mjs.map +1 -1
- package/package.json +1 -1
package/dist/express.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;;;AA2FA,eAAsB,KAAA,GAAuB;AAI7C;;;ACrBO,SAAS,gBAAA,GAAsC;AACpD,EAAA,OAAO,CAAC,IAAA,EAAM,GAAA,EAAK,IAAA,KAAS;AAE1B,IAAA,GAAA,CAAI,EAAA,CAAG,UAAU,MAAM;AACrB,MAAA,KAAA,EAAM,CAAE,MAAM,MAAM;AAAA,MAEpB,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AACF","file":"express.js","sourcesContent":["/**\n * Global Configuration\n *\n * Manages SDK configuration and transport instance.\n */\n\nimport type { LelemonConfig } from './types';\nimport { Transport } from './transport';\nimport { setDebug, info, warn, debug } from './logger';\n\n// ─────────────────────────────────────────────────────────────\n// Global State\n// ─────────────────────────────────────────────────────────────\n\nlet globalConfig: LelemonConfig = {};\nlet globalTransport: Transport | null = null;\nlet initialized = false;\n\n// ─────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────\n\nconst DEFAULT_ENDPOINT = 'https://lelemon.dev';\n\n/**\n * Initialize the SDK\n * Call once at app startup\n */\nexport function init(config: LelemonConfig = {}): void {\n globalConfig = config;\n\n // Configure debug mode\n if (config.debug) {\n setDebug(true);\n }\n\n info('Initializing SDK', {\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? false,\n });\n\n globalTransport = createTransport(config);\n initialized = true;\n\n // Log status after transport is created\n if (globalTransport.isEnabled()) {\n info('SDK initialized - tracing enabled');\n } else {\n debug('SDK initialized - tracing disabled (no API key or explicitly disabled)');\n }\n}\n\n/**\n * Get current config\n */\nexport function getConfig(): LelemonConfig {\n return globalConfig;\n}\n\n/**\n * Check if SDK is initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Check if SDK is enabled\n */\nexport function isEnabled(): boolean {\n return getTransport().isEnabled();\n}\n\n// ─────────────────────────────────────────────────────────────\n// Transport\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Get or create transport instance\n */\nexport function getTransport(): Transport {\n if (!globalTransport) {\n globalTransport = createTransport(globalConfig);\n }\n return globalTransport;\n}\n\n/**\n * Flush all pending traces\n */\nexport async function flush(): Promise<void> {\n if (globalTransport) {\n await globalTransport.flush();\n }\n}\n\n/**\n * Create transport instance\n */\nfunction createTransport(config: LelemonConfig): Transport {\n const apiKey = config.apiKey ?? getEnvVar('LELEMON_API_KEY');\n\n if (!apiKey && !config.disabled) {\n warn('No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.');\n }\n\n return new Transport({\n apiKey: apiKey ?? '',\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? !apiKey,\n batchSize: config.batchSize,\n flushIntervalMs: config.flushIntervalMs,\n requestTimeoutMs: config.requestTimeoutMs,\n });\n}\n\n/**\n * Get environment variable (works in Node and edge)\n */\nfunction getEnvVar(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n","/**\n * Express Integration\n *\n * Middleware that automatically flushes traces when response finishes.\n *\n * @example\n * import express from 'express';\n * import { createMiddleware } from '@lelemondev/sdk/express';\n *\n * const app = express();\n * app.use(createMiddleware());\n */\n\nimport { flush } from '../core/config';\n\n// ─────────────────────────────────────────────────────────────\n// Types (minimal to avoid requiring express as dependency)\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Minimal Express request type (avoids requiring express as dependency)\n */\nexport interface ExpressRequest {\n [key: string]: unknown;\n}\n\n/**\n * Minimal Express response type (avoids requiring express as dependency)\n */\nexport interface ExpressResponse {\n on(event: 'finish' | 'close' | 'error', listener: () => void): this;\n [key: string]: unknown;\n}\n\n/**\n * Express next function type\n */\nexport type ExpressNextFunction = (error?: unknown) => void;\n\n/**\n * Express middleware function type\n *\n * @param req - Express request object\n * @param res - Express response object\n * @param next - Next function to pass control\n */\nexport type ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: ExpressNextFunction\n) => void;\n\n// ─────────────────────────────────────────────────────────────\n// Middleware\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Create Express middleware for automatic trace flushing\n *\n * Flushes traces when the response finishes (after res.send/res.json).\n * This is fire-and-forget and doesn't block the response.\n *\n * @returns Express middleware function\n *\n * @example\n * // Global middleware\n * app.use(createMiddleware());\n *\n * @example\n * // Per-route middleware\n * app.post('/chat', createMiddleware(), async (req, res) => {\n * res.json({ ok: true });\n * });\n */\nexport function createMiddleware(): ExpressMiddleware {\n return (_req, res, next) => {\n // Flush when response is finished (after headers + body sent)\n res.on('finish', () => {\n flush().catch(() => {\n // Silently ignore flush errors - fire and forget\n });\n });\n\n next();\n };\n}\n"]}
|
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;AA2FA,eAAsB,KAAA,GAAuB;AAI7C;;;ACrBO,SAAS,gBAAA,GAAsC;AACpD,EAAA,OAAO,CAAC,IAAA,EAAM,GAAA,EAAK,IAAA,KAAS;AAE1B,IAAA,GAAA,CAAI,EAAA,CAAG,UAAU,MAAM;AACrB,MAAA,KAAA,EAAM,CAAE,MAAM,MAAM;AAAA,MAEpB,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,IAAA,EAAK;AAAA,EACP,CAAA;AACF","file":"express.mjs","sourcesContent":["/**\n * Global Configuration\n *\n * Manages SDK configuration and transport instance.\n */\n\nimport type { LelemonConfig } from './types';\nimport { Transport } from './transport';\nimport { setDebug, info, warn, debug } from './logger';\n\n// ─────────────────────────────────────────────────────────────\n// Global State\n// ─────────────────────────────────────────────────────────────\n\nlet globalConfig: LelemonConfig = {};\nlet globalTransport: Transport | null = null;\nlet initialized = false;\n\n// ─────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────\n\nconst DEFAULT_ENDPOINT = 'https://lelemon.dev';\n\n/**\n * Initialize the SDK\n * Call once at app startup\n */\nexport function init(config: LelemonConfig = {}): void {\n globalConfig = config;\n\n // Configure debug mode\n if (config.debug) {\n setDebug(true);\n }\n\n info('Initializing SDK', {\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? false,\n });\n\n globalTransport = createTransport(config);\n initialized = true;\n\n // Log status after transport is created\n if (globalTransport.isEnabled()) {\n info('SDK initialized - tracing enabled');\n } else {\n debug('SDK initialized - tracing disabled (no API key or explicitly disabled)');\n }\n}\n\n/**\n * Get current config\n */\nexport function getConfig(): LelemonConfig {\n return globalConfig;\n}\n\n/**\n * Check if SDK is initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Check if SDK is enabled\n */\nexport function isEnabled(): boolean {\n return getTransport().isEnabled();\n}\n\n// ─────────────────────────────────────────────────────────────\n// Transport\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Get or create transport instance\n */\nexport function getTransport(): Transport {\n if (!globalTransport) {\n globalTransport = createTransport(globalConfig);\n }\n return globalTransport;\n}\n\n/**\n * Flush all pending traces\n */\nexport async function flush(): Promise<void> {\n if (globalTransport) {\n await globalTransport.flush();\n }\n}\n\n/**\n * Create transport instance\n */\nfunction createTransport(config: LelemonConfig): Transport {\n const apiKey = config.apiKey ?? getEnvVar('LELEMON_API_KEY');\n\n if (!apiKey && !config.disabled) {\n warn('No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.');\n }\n\n return new Transport({\n apiKey: apiKey ?? '',\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? !apiKey,\n batchSize: config.batchSize,\n flushIntervalMs: config.flushIntervalMs,\n requestTimeoutMs: config.requestTimeoutMs,\n });\n}\n\n/**\n * Get environment variable (works in Node and edge)\n */\nfunction getEnvVar(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n","/**\n * Express Integration\n *\n * Middleware that automatically flushes traces when response finishes.\n *\n * @example\n * import express from 'express';\n * import { createMiddleware } from '@lelemondev/sdk/express';\n *\n * const app = express();\n * app.use(createMiddleware());\n */\n\nimport { flush } from '../core/config';\n\n// ─────────────────────────────────────────────────────────────\n// Types (minimal to avoid requiring express as dependency)\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Minimal Express request type (avoids requiring express as dependency)\n */\nexport interface ExpressRequest {\n [key: string]: unknown;\n}\n\n/**\n * Minimal Express response type (avoids requiring express as dependency)\n */\nexport interface ExpressResponse {\n on(event: 'finish' | 'close' | 'error', listener: () => void): this;\n [key: string]: unknown;\n}\n\n/**\n * Express next function type\n */\nexport type ExpressNextFunction = (error?: unknown) => void;\n\n/**\n * Express middleware function type\n *\n * @param req - Express request object\n * @param res - Express response object\n * @param next - Next function to pass control\n */\nexport type ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: ExpressNextFunction\n) => void;\n\n// ─────────────────────────────────────────────────────────────\n// Middleware\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Create Express middleware for automatic trace flushing\n *\n * Flushes traces when the response finishes (after res.send/res.json).\n * This is fire-and-forget and doesn't block the response.\n *\n * @returns Express middleware function\n *\n * @example\n * // Global middleware\n * app.use(createMiddleware());\n *\n * @example\n * // Per-route middleware\n * app.post('/chat', createMiddleware(), async (req, res) => {\n * res.json({ ok: true });\n * });\n */\nexport function createMiddleware(): ExpressMiddleware {\n return (_req, res, next) => {\n // Flush when response is finished (after headers + body sent)\n res.on('finish', () => {\n flush().catch(() => {\n // Silently ignore flush errors - fire and forget\n });\n });\n\n next();\n };\n}\n"]}
|
package/dist/hono.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/integrations/hono.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/integrations/hono.ts"],"names":[],"mappings":";;;;AA2FA,eAAsB,KAAA,GAAuB;AAI7C;;;ACZO,SAAS,gBAAA,GAAmC;AACjD,EAAA,OAAO,OAAO,GAAG,IAAA,KAAS;AACxB,IAAA,MAAM,IAAA,EAAK;AAGX,IAAA,IAAI,CAAA,CAAE,cAAc,SAAA,EAAW;AAC7B,MAAA,CAAA,CAAE,YAAA,CAAa,SAAA,CAAU,KAAA,EAAO,CAAA;AAAA,IAClC,CAAA,MAAO;AAEL,MAAA,KAAA,EAAM,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AACF","file":"hono.js","sourcesContent":["/**\n * Global Configuration\n *\n * Manages SDK configuration and transport instance.\n */\n\nimport type { LelemonConfig } from './types';\nimport { Transport } from './transport';\nimport { setDebug, info, warn, debug } from './logger';\n\n// ─────────────────────────────────────────────────────────────\n// Global State\n// ─────────────────────────────────────────────────────────────\n\nlet globalConfig: LelemonConfig = {};\nlet globalTransport: Transport | null = null;\nlet initialized = false;\n\n// ─────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────\n\nconst DEFAULT_ENDPOINT = 'https://lelemon.dev';\n\n/**\n * Initialize the SDK\n * Call once at app startup\n */\nexport function init(config: LelemonConfig = {}): void {\n globalConfig = config;\n\n // Configure debug mode\n if (config.debug) {\n setDebug(true);\n }\n\n info('Initializing SDK', {\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? false,\n });\n\n globalTransport = createTransport(config);\n initialized = true;\n\n // Log status after transport is created\n if (globalTransport.isEnabled()) {\n info('SDK initialized - tracing enabled');\n } else {\n debug('SDK initialized - tracing disabled (no API key or explicitly disabled)');\n }\n}\n\n/**\n * Get current config\n */\nexport function getConfig(): LelemonConfig {\n return globalConfig;\n}\n\n/**\n * Check if SDK is initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Check if SDK is enabled\n */\nexport function isEnabled(): boolean {\n return getTransport().isEnabled();\n}\n\n// ─────────────────────────────────────────────────────────────\n// Transport\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Get or create transport instance\n */\nexport function getTransport(): Transport {\n if (!globalTransport) {\n globalTransport = createTransport(globalConfig);\n }\n return globalTransport;\n}\n\n/**\n * Flush all pending traces\n */\nexport async function flush(): Promise<void> {\n if (globalTransport) {\n await globalTransport.flush();\n }\n}\n\n/**\n * Create transport instance\n */\nfunction createTransport(config: LelemonConfig): Transport {\n const apiKey = config.apiKey ?? getEnvVar('LELEMON_API_KEY');\n\n if (!apiKey && !config.disabled) {\n warn('No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.');\n }\n\n return new Transport({\n apiKey: apiKey ?? '',\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? !apiKey,\n batchSize: config.batchSize,\n flushIntervalMs: config.flushIntervalMs,\n requestTimeoutMs: config.requestTimeoutMs,\n });\n}\n\n/**\n * Get environment variable (works in Node and edge)\n */\nfunction getEnvVar(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n","/**\n * Hono Integration\n *\n * Middleware for Hono framework (Cloudflare Workers, Deno, Bun, Node.js).\n * Uses executionCtx.waitUntil() when available for non-blocking flush.\n *\n * @example\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@lelemondev/sdk/hono';\n *\n * const app = new Hono();\n * app.use(createMiddleware());\n */\n\nimport { flush } from '../core/config';\n\n// ─────────────────────────────────────────────────────────────\n// Types (minimal to avoid requiring hono as dependency)\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Execution context for edge runtimes (Cloudflare Workers, Deno Deploy)\n */\nexport interface ExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void;\n}\n\n/**\n * Minimal Hono context type (avoids requiring hono as dependency)\n */\nexport interface HonoContext {\n req: {\n raw: Request;\n [key: string]: unknown;\n };\n res: Response | undefined;\n executionCtx?: ExecutionContext;\n [key: string]: unknown;\n}\n\n/**\n * Hono next function type\n */\nexport type HonoNextFunction = () => Promise<void>;\n\n/**\n * Hono middleware function type\n *\n * @param c - Hono context object\n * @param next - Next function to continue middleware chain\n */\nexport type HonoMiddleware = (c: HonoContext, next: HonoNextFunction) => Promise<void>;\n\n// ─────────────────────────────────────────────────────────────\n// Middleware\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Create Hono middleware for automatic trace flushing\n *\n * On Cloudflare Workers/Deno Deploy: uses executionCtx.waitUntil() for non-blocking flush\n * On Node.js/Bun: flushes after response (fire-and-forget)\n *\n * @returns Hono middleware function\n *\n * @example\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@lelemondev/sdk/hono';\n *\n * const app = new Hono();\n *\n * // Global middleware\n * app.use(createMiddleware());\n *\n * app.post('/chat', async (c) => {\n * const openai = observe(new OpenAI());\n * const result = await openai.chat.completions.create({...});\n * return c.json(result);\n * });\n *\n * export default app;\n */\nexport function createMiddleware(): HonoMiddleware {\n return async (c, next) => {\n await next();\n\n // Use waitUntil if available (Cloudflare Workers, Deno Deploy)\n if (c.executionCtx?.waitUntil) {\n c.executionCtx.waitUntil(flush());\n } else {\n // Fire-and-forget for Node.js/Bun\n flush().catch(() => {});\n }\n };\n}\n"]}
|
package/dist/hono.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/integrations/hono.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/integrations/hono.ts"],"names":[],"mappings":";;AA2FA,eAAsB,KAAA,GAAuB;AAI7C;;;ACZO,SAAS,gBAAA,GAAmC;AACjD,EAAA,OAAO,OAAO,GAAG,IAAA,KAAS;AACxB,IAAA,MAAM,IAAA,EAAK;AAGX,IAAA,IAAI,CAAA,CAAE,cAAc,SAAA,EAAW;AAC7B,MAAA,CAAA,CAAE,YAAA,CAAa,SAAA,CAAU,KAAA,EAAO,CAAA;AAAA,IAClC,CAAA,MAAO;AAEL,MAAA,KAAA,EAAM,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IACxB;AAAA,EACF,CAAA;AACF","file":"hono.mjs","sourcesContent":["/**\n * Global Configuration\n *\n * Manages SDK configuration and transport instance.\n */\n\nimport type { LelemonConfig } from './types';\nimport { Transport } from './transport';\nimport { setDebug, info, warn, debug } from './logger';\n\n// ─────────────────────────────────────────────────────────────\n// Global State\n// ─────────────────────────────────────────────────────────────\n\nlet globalConfig: LelemonConfig = {};\nlet globalTransport: Transport | null = null;\nlet initialized = false;\n\n// ─────────────────────────────────────────────────────────────\n// Configuration\n// ─────────────────────────────────────────────────────────────\n\nconst DEFAULT_ENDPOINT = 'https://lelemon.dev';\n\n/**\n * Initialize the SDK\n * Call once at app startup\n */\nexport function init(config: LelemonConfig = {}): void {\n globalConfig = config;\n\n // Configure debug mode\n if (config.debug) {\n setDebug(true);\n }\n\n info('Initializing SDK', {\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? false,\n });\n\n globalTransport = createTransport(config);\n initialized = true;\n\n // Log status after transport is created\n if (globalTransport.isEnabled()) {\n info('SDK initialized - tracing enabled');\n } else {\n debug('SDK initialized - tracing disabled (no API key or explicitly disabled)');\n }\n}\n\n/**\n * Get current config\n */\nexport function getConfig(): LelemonConfig {\n return globalConfig;\n}\n\n/**\n * Check if SDK is initialized\n */\nexport function isInitialized(): boolean {\n return initialized;\n}\n\n/**\n * Check if SDK is enabled\n */\nexport function isEnabled(): boolean {\n return getTransport().isEnabled();\n}\n\n// ─────────────────────────────────────────────────────────────\n// Transport\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Get or create transport instance\n */\nexport function getTransport(): Transport {\n if (!globalTransport) {\n globalTransport = createTransport(globalConfig);\n }\n return globalTransport;\n}\n\n/**\n * Flush all pending traces\n */\nexport async function flush(): Promise<void> {\n if (globalTransport) {\n await globalTransport.flush();\n }\n}\n\n/**\n * Create transport instance\n */\nfunction createTransport(config: LelemonConfig): Transport {\n const apiKey = config.apiKey ?? getEnvVar('LELEMON_API_KEY');\n\n if (!apiKey && !config.disabled) {\n warn('No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.');\n }\n\n return new Transport({\n apiKey: apiKey ?? '',\n endpoint: config.endpoint ?? DEFAULT_ENDPOINT,\n debug: config.debug ?? false,\n disabled: config.disabled ?? !apiKey,\n batchSize: config.batchSize,\n flushIntervalMs: config.flushIntervalMs,\n requestTimeoutMs: config.requestTimeoutMs,\n });\n}\n\n/**\n * Get environment variable (works in Node and edge)\n */\nfunction getEnvVar(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n","/**\n * Hono Integration\n *\n * Middleware for Hono framework (Cloudflare Workers, Deno, Bun, Node.js).\n * Uses executionCtx.waitUntil() when available for non-blocking flush.\n *\n * @example\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@lelemondev/sdk/hono';\n *\n * const app = new Hono();\n * app.use(createMiddleware());\n */\n\nimport { flush } from '../core/config';\n\n// ─────────────────────────────────────────────────────────────\n// Types (minimal to avoid requiring hono as dependency)\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Execution context for edge runtimes (Cloudflare Workers, Deno Deploy)\n */\nexport interface ExecutionContext {\n waitUntil(promise: Promise<unknown>): void;\n passThroughOnException(): void;\n}\n\n/**\n * Minimal Hono context type (avoids requiring hono as dependency)\n */\nexport interface HonoContext {\n req: {\n raw: Request;\n [key: string]: unknown;\n };\n res: Response | undefined;\n executionCtx?: ExecutionContext;\n [key: string]: unknown;\n}\n\n/**\n * Hono next function type\n */\nexport type HonoNextFunction = () => Promise<void>;\n\n/**\n * Hono middleware function type\n *\n * @param c - Hono context object\n * @param next - Next function to continue middleware chain\n */\nexport type HonoMiddleware = (c: HonoContext, next: HonoNextFunction) => Promise<void>;\n\n// ─────────────────────────────────────────────────────────────\n// Middleware\n// ─────────────────────────────────────────────────────────────\n\n/**\n * Create Hono middleware for automatic trace flushing\n *\n * On Cloudflare Workers/Deno Deploy: uses executionCtx.waitUntil() for non-blocking flush\n * On Node.js/Bun: flushes after response (fire-and-forget)\n *\n * @returns Hono middleware function\n *\n * @example\n * import { Hono } from 'hono';\n * import { createMiddleware } from '@lelemondev/sdk/hono';\n *\n * const app = new Hono();\n *\n * // Global middleware\n * app.use(createMiddleware());\n *\n * app.post('/chat', async (c) => {\n * const openai = observe(new OpenAI());\n * const result = await openai.chat.completions.create({...});\n * return c.json(result);\n * });\n *\n * export default app;\n */\nexport function createMiddleware(): HonoMiddleware {\n return async (c, next) => {\n await next();\n\n // Use waitUntil if available (Cloudflare Workers, Deno Deploy)\n if (c.executionCtx?.waitUntil) {\n c.executionCtx.waitUntil(flush());\n } else {\n // Fire-and-forget for Node.js/Bun\n flush().catch(() => {});\n }\n };\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,75 @@ var __defProp = Object.defineProperty;
|
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
6
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
7
|
|
|
8
|
+
// src/core/logger.ts
|
|
9
|
+
var debugEnabled = false;
|
|
10
|
+
function setDebug(enabled) {
|
|
11
|
+
debugEnabled = enabled;
|
|
12
|
+
}
|
|
13
|
+
function isDebugEnabled() {
|
|
14
|
+
if (debugEnabled) return true;
|
|
15
|
+
return getEnvVar("LELEMON_DEBUG") === "true";
|
|
16
|
+
}
|
|
17
|
+
var PREFIX = "[Lelemon]";
|
|
18
|
+
function debug(message, data) {
|
|
19
|
+
if (!isDebugEnabled()) return;
|
|
20
|
+
logWithPrefix("debug", message, data);
|
|
21
|
+
}
|
|
22
|
+
function info(message, data) {
|
|
23
|
+
if (!isDebugEnabled()) return;
|
|
24
|
+
logWithPrefix("info", message, data);
|
|
25
|
+
}
|
|
26
|
+
function warn(message, data) {
|
|
27
|
+
logWithPrefix("warn", message, data);
|
|
28
|
+
}
|
|
29
|
+
function traceCapture(provider, model, durationMs, status) {
|
|
30
|
+
if (!isDebugEnabled()) return;
|
|
31
|
+
console.log(
|
|
32
|
+
`${PREFIX} Captured trace: provider=${provider} model=${model} duration=${durationMs}ms status=${status}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
function traceCaptureError(provider, err) {
|
|
36
|
+
console.error(`${PREFIX} Failed to capture trace: provider=${provider} error=${err.message}`);
|
|
37
|
+
}
|
|
38
|
+
function clientWrapped(provider) {
|
|
39
|
+
if (!isDebugEnabled()) return;
|
|
40
|
+
console.log(`${PREFIX} Wrapped client: provider=${provider}`);
|
|
41
|
+
}
|
|
42
|
+
function batchSend(count, endpoint) {
|
|
43
|
+
if (!isDebugEnabled()) return;
|
|
44
|
+
console.log(`${PREFIX} Sending batch: count=${count} endpoint=${endpoint}`);
|
|
45
|
+
}
|
|
46
|
+
function batchSuccess(count, durationMs) {
|
|
47
|
+
if (!isDebugEnabled()) return;
|
|
48
|
+
console.log(`${PREFIX} Batch sent successfully: count=${count} duration=${durationMs}ms`);
|
|
49
|
+
}
|
|
50
|
+
function batchError(count, err) {
|
|
51
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
52
|
+
console.error(`${PREFIX} Batch send failed: count=${count} error=${message}`);
|
|
53
|
+
}
|
|
54
|
+
function requestDetails(method, url, bodySize) {
|
|
55
|
+
if (!isDebugEnabled()) return;
|
|
56
|
+
console.log(`${PREFIX} Request: ${method} ${url} (${bodySize} bytes)`);
|
|
57
|
+
}
|
|
58
|
+
function responseDetails(status, durationMs) {
|
|
59
|
+
if (!isDebugEnabled()) return;
|
|
60
|
+
console.log(`${PREFIX} Response: status=${status} duration=${durationMs}ms`);
|
|
61
|
+
}
|
|
62
|
+
function logWithPrefix(level, message, data) {
|
|
63
|
+
const logFn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
|
|
64
|
+
if (data !== void 0) {
|
|
65
|
+
logFn(`${PREFIX} ${message}`, data);
|
|
66
|
+
} else {
|
|
67
|
+
logFn(`${PREFIX} ${message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function getEnvVar(name) {
|
|
71
|
+
if (typeof process !== "undefined" && process.env) {
|
|
72
|
+
return process.env[name];
|
|
73
|
+
}
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
8
77
|
// src/core/transport.ts
|
|
9
78
|
var DEFAULT_BATCH_SIZE = 10;
|
|
10
79
|
var DEFAULT_FLUSH_INTERVAL_MS = 1e3;
|
|
@@ -87,19 +156,24 @@ var Transport = class {
|
|
|
87
156
|
}
|
|
88
157
|
async sendBatch(items) {
|
|
89
158
|
if (items.length === 0) return;
|
|
90
|
-
|
|
159
|
+
const startTime = Date.now();
|
|
160
|
+
batchSend(items.length, `${this.config.endpoint}/api/v1/ingest`);
|
|
91
161
|
try {
|
|
92
162
|
await this.request("POST", "/api/v1/ingest", { events: items });
|
|
163
|
+
batchSuccess(items.length, Date.now() - startTime);
|
|
93
164
|
} catch (error) {
|
|
94
|
-
|
|
165
|
+
batchError(items.length, error);
|
|
95
166
|
}
|
|
96
167
|
}
|
|
97
168
|
async request(method, path, body) {
|
|
98
169
|
const url = `${this.config.endpoint}${path}`;
|
|
99
170
|
const controller = new AbortController();
|
|
171
|
+
const bodyStr = body ? JSON.stringify(body) : void 0;
|
|
172
|
+
requestDetails(method, url, bodyStr?.length ?? 0);
|
|
100
173
|
const timeoutId = setTimeout(() => {
|
|
101
174
|
controller.abort();
|
|
102
175
|
}, this.config.requestTimeoutMs);
|
|
176
|
+
const startTime = Date.now();
|
|
103
177
|
try {
|
|
104
178
|
const response = await fetch(url, {
|
|
105
179
|
method,
|
|
@@ -107,10 +181,11 @@ var Transport = class {
|
|
|
107
181
|
"Content-Type": "application/json",
|
|
108
182
|
"Authorization": `Bearer ${this.config.apiKey}`
|
|
109
183
|
},
|
|
110
|
-
body:
|
|
184
|
+
body: bodyStr,
|
|
111
185
|
signal: controller.signal
|
|
112
186
|
});
|
|
113
187
|
clearTimeout(timeoutId);
|
|
188
|
+
responseDetails(response.status, Date.now() - startTime);
|
|
114
189
|
if (!response.ok) {
|
|
115
190
|
const errorText = await response.text().catch(() => "Unknown error");
|
|
116
191
|
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
@@ -125,15 +200,6 @@ var Transport = class {
|
|
|
125
200
|
throw error;
|
|
126
201
|
}
|
|
127
202
|
}
|
|
128
|
-
log(message, data) {
|
|
129
|
-
if (this.config.debug) {
|
|
130
|
-
if (data !== void 0) {
|
|
131
|
-
console.log(`[Lelemon] ${message}`, data);
|
|
132
|
-
} else {
|
|
133
|
-
console.log(`[Lelemon] ${message}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
203
|
};
|
|
138
204
|
|
|
139
205
|
// src/core/config.ts
|
|
@@ -142,7 +208,20 @@ var globalTransport = null;
|
|
|
142
208
|
var DEFAULT_ENDPOINT = "https://lelemon.dev";
|
|
143
209
|
function init(config = {}) {
|
|
144
210
|
globalConfig = config;
|
|
211
|
+
if (config.debug) {
|
|
212
|
+
setDebug(true);
|
|
213
|
+
}
|
|
214
|
+
info("Initializing SDK", {
|
|
215
|
+
endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
|
|
216
|
+
debug: config.debug ?? false,
|
|
217
|
+
disabled: config.disabled ?? false
|
|
218
|
+
});
|
|
145
219
|
globalTransport = createTransport(config);
|
|
220
|
+
if (globalTransport.isEnabled()) {
|
|
221
|
+
info("SDK initialized - tracing enabled");
|
|
222
|
+
} else {
|
|
223
|
+
debug("SDK initialized - tracing disabled (no API key or explicitly disabled)");
|
|
224
|
+
}
|
|
146
225
|
}
|
|
147
226
|
function getConfig() {
|
|
148
227
|
return globalConfig;
|
|
@@ -162,11 +241,9 @@ async function flush() {
|
|
|
162
241
|
}
|
|
163
242
|
}
|
|
164
243
|
function createTransport(config) {
|
|
165
|
-
const apiKey = config.apiKey ??
|
|
244
|
+
const apiKey = config.apiKey ?? getEnvVar2("LELEMON_API_KEY");
|
|
166
245
|
if (!apiKey && !config.disabled) {
|
|
167
|
-
|
|
168
|
-
"[Lelemon] No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled."
|
|
169
|
-
);
|
|
246
|
+
warn("No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.");
|
|
170
247
|
}
|
|
171
248
|
return new Transport({
|
|
172
249
|
apiKey: apiKey ?? "",
|
|
@@ -178,7 +255,7 @@ function createTransport(config) {
|
|
|
178
255
|
requestTimeoutMs: config.requestTimeoutMs
|
|
179
256
|
});
|
|
180
257
|
}
|
|
181
|
-
function
|
|
258
|
+
function getEnvVar2(name) {
|
|
182
259
|
if (typeof process !== "undefined" && process.env) {
|
|
183
260
|
return process.env[name];
|
|
184
261
|
}
|
|
@@ -216,6 +293,7 @@ function isValidNumber(value) {
|
|
|
216
293
|
var globalContext = {};
|
|
217
294
|
function setGlobalContext(options) {
|
|
218
295
|
globalContext = options;
|
|
296
|
+
debug("Global context updated", options);
|
|
219
297
|
}
|
|
220
298
|
function getGlobalContext() {
|
|
221
299
|
return globalContext;
|
|
@@ -223,7 +301,10 @@ function getGlobalContext() {
|
|
|
223
301
|
function captureTrace(params) {
|
|
224
302
|
try {
|
|
225
303
|
const transport = getTransport();
|
|
226
|
-
if (!transport.isEnabled())
|
|
304
|
+
if (!transport.isEnabled()) {
|
|
305
|
+
debug("Transport disabled, skipping trace capture");
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
227
308
|
const context = getGlobalContext();
|
|
228
309
|
const request = {
|
|
229
310
|
provider: params.provider,
|
|
@@ -240,14 +321,19 @@ function captureTrace(params) {
|
|
|
240
321
|
metadata: { ...context.metadata, ...params.metadata },
|
|
241
322
|
tags: context.tags
|
|
242
323
|
};
|
|
324
|
+
traceCapture(params.provider, params.model, params.durationMs, params.status);
|
|
243
325
|
transport.enqueue(request);
|
|
244
|
-
} catch {
|
|
326
|
+
} catch (err) {
|
|
327
|
+
traceCaptureError(params.provider, err instanceof Error ? err : new Error(String(err)));
|
|
245
328
|
}
|
|
246
329
|
}
|
|
247
330
|
function captureError(params) {
|
|
248
331
|
try {
|
|
249
332
|
const transport = getTransport();
|
|
250
|
-
if (!transport.isEnabled())
|
|
333
|
+
if (!transport.isEnabled()) {
|
|
334
|
+
debug("Transport disabled, skipping error capture");
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
251
337
|
const context = getGlobalContext();
|
|
252
338
|
const request = {
|
|
253
339
|
provider: params.provider,
|
|
@@ -266,8 +352,11 @@ function captureError(params) {
|
|
|
266
352
|
metadata: { ...context.metadata, ...params.metadata },
|
|
267
353
|
tags: context.tags
|
|
268
354
|
};
|
|
355
|
+
traceCapture(params.provider, params.model, params.durationMs, "error");
|
|
356
|
+
debug("Error details", { message: params.error.message, stack: params.error.stack });
|
|
269
357
|
transport.enqueue(request);
|
|
270
|
-
} catch {
|
|
358
|
+
} catch (err) {
|
|
359
|
+
traceCaptureError(params.provider, err instanceof Error ? err : new Error(String(err)));
|
|
271
360
|
}
|
|
272
361
|
}
|
|
273
362
|
var MAX_STRING_LENGTH = 1e5;
|
|
@@ -1686,41 +1775,30 @@ function observe(client, options) {
|
|
|
1686
1775
|
}
|
|
1687
1776
|
const config = getConfig();
|
|
1688
1777
|
if (config.disabled) {
|
|
1778
|
+
debug("Tracing disabled, returning unwrapped client");
|
|
1689
1779
|
return client;
|
|
1690
1780
|
}
|
|
1691
1781
|
if (canHandle5(client)) {
|
|
1692
|
-
|
|
1693
|
-
console.log("[Lelemon] Wrapping OpenRouter client");
|
|
1694
|
-
}
|
|
1782
|
+
clientWrapped("openrouter");
|
|
1695
1783
|
return wrap3(client);
|
|
1696
1784
|
}
|
|
1697
1785
|
if (canHandle(client)) {
|
|
1698
|
-
|
|
1699
|
-
console.log("[Lelemon] Wrapping OpenAI client");
|
|
1700
|
-
}
|
|
1786
|
+
clientWrapped("openai");
|
|
1701
1787
|
return wrapOpenAI(client);
|
|
1702
1788
|
}
|
|
1703
1789
|
if (canHandle2(client)) {
|
|
1704
|
-
|
|
1705
|
-
console.log("[Lelemon] Wrapping Anthropic client");
|
|
1706
|
-
}
|
|
1790
|
+
clientWrapped("anthropic");
|
|
1707
1791
|
return wrapAnthropic(client);
|
|
1708
1792
|
}
|
|
1709
1793
|
if (canHandle3(client)) {
|
|
1710
|
-
|
|
1711
|
-
console.log("[Lelemon] Wrapping Bedrock client");
|
|
1712
|
-
}
|
|
1794
|
+
clientWrapped("bedrock");
|
|
1713
1795
|
return wrap(client);
|
|
1714
1796
|
}
|
|
1715
1797
|
if (canHandle4(client)) {
|
|
1716
|
-
|
|
1717
|
-
console.log("[Lelemon] Wrapping Gemini client");
|
|
1718
|
-
}
|
|
1798
|
+
clientWrapped("gemini");
|
|
1719
1799
|
return wrap2(client);
|
|
1720
1800
|
}
|
|
1721
|
-
|
|
1722
|
-
"[Lelemon] Unknown client type. Tracing not enabled. Supported: OpenAI, OpenRouter, Anthropic, Bedrock, Gemini"
|
|
1723
|
-
);
|
|
1801
|
+
warn("Unknown client type. Tracing not enabled. Supported: OpenAI, OpenRouter, Anthropic, Bedrock, Gemini");
|
|
1724
1802
|
return client;
|
|
1725
1803
|
}
|
|
1726
1804
|
function wrapOpenAI(client) {
|