@lelemondev/sdk 0.4.0 → 0.5.1

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 CHANGED
@@ -6,6 +6,13 @@
6
6
 
7
7
  Automatic LLM observability for Node.js. Wrap your client, everything is traced.
8
8
 
9
+ ## LLM Agent Docs
10
+
11
+ ```
12
+ https://lelemondev.github.io/lelemondev-sdk/llms.txt
13
+ https://lelemondev.github.io/lelemondev-sdk/llms-full.txt
14
+ ```
15
+
9
16
  ## Features
10
17
 
11
18
  - **Automatic Tracing** - Wrap your client, all calls are traced
@@ -39,8 +46,150 @@ const response = await openai.chat.completions.create({
39
46
  });
40
47
  ```
41
48
 
49
+ ## Supported Providers
50
+
51
+ | Provider | Status | Methods |
52
+ |----------|--------|---------|
53
+ | OpenAI | ✅ | `chat.completions.create()`, `responses.create()`, `completions.create()`, `embeddings.create()` |
54
+ | OpenRouter | ✅ | `chat.completions.create()` (access to 400+ models) |
55
+ | Anthropic | ✅ | `messages.create()`, `messages.stream()` |
56
+ | AWS Bedrock | ✅ | `ConverseCommand`, `ConverseStreamCommand`, `InvokeModelCommand` |
57
+ | Google Gemini | ✅ | `generateContent()`, `generateContentStream()`, `chat.sendMessage()` |
58
+
59
+ ### OpenRouter
60
+
61
+ [OpenRouter](https://openrouter.ai) provides unified access to 400+ models from OpenAI, Anthropic, Google, Meta, Mistral, and more through a single API.
62
+
63
+ ```typescript
64
+ import { init, observe } from '@lelemondev/sdk';
65
+ import OpenAI from 'openai';
66
+
67
+ init({ apiKey: process.env.LELEMON_API_KEY });
68
+
69
+ // Configure OpenAI SDK to use OpenRouter
70
+ const openrouter = observe(new OpenAI({
71
+ baseURL: 'https://openrouter.ai/api/v1',
72
+ apiKey: process.env.OPENROUTER_API_KEY,
73
+ defaultHeaders: {
74
+ 'HTTP-Referer': 'https://your-app.com', // Optional: for OpenRouter rankings
75
+ 'X-Title': 'Your App Name', // Optional: for OpenRouter rankings
76
+ },
77
+ }));
78
+
79
+ // Access any model through OpenRouter
80
+ const response = await openrouter.chat.completions.create({
81
+ model: 'anthropic/claude-3-opus', // or 'openai/gpt-4', 'google/gemini-pro', etc.
82
+ messages: [{ role: 'user', content: 'Hello!' }],
83
+ });
84
+ ```
85
+
86
+ Models are specified in `provider/model` format (e.g., `anthropic/claude-3-opus`, `openai/gpt-4`, `meta-llama/llama-3-70b`). See [OpenRouter Models](https://openrouter.ai/models) for the full list.
87
+
88
+ ## Usage Without Framework Integrations
89
+
90
+ The SDK works with **any Node.js application**. Framework integrations are optional - they just automate the `flush()` call.
91
+
92
+ ### Long-running Processes (Servers, Workers)
93
+
94
+ For long-running processes, the SDK auto-flushes every second (configurable via `flushIntervalMs`):
95
+
96
+ ```typescript
97
+ import { init, observe } from '@lelemondev/sdk';
98
+ import OpenAI from 'openai';
99
+
100
+ init({ apiKey: process.env.LELEMON_API_KEY });
101
+ const openai = observe(new OpenAI());
102
+
103
+ // In your HTTP server, WebSocket handler, queue worker, etc.
104
+ async function handleRequest(userId: string, message: string) {
105
+ const client = observe(new OpenAI(), { userId });
106
+
107
+ const result = await client.chat.completions.create({
108
+ model: 'gpt-4',
109
+ messages: [{ role: 'user', content: message }],
110
+ });
111
+
112
+ return result.choices[0].message;
113
+ // Traces are auto-flushed in the background (every 1s by default)
114
+ }
115
+ ```
116
+
117
+ ### Short-lived Processes (Scripts, Serverless, CLI)
118
+
119
+ For scripts or serverless functions, call `flush()` before the process exits:
120
+
121
+ ```typescript
122
+ import { init, observe, flush } from '@lelemondev/sdk';
123
+ import OpenAI from 'openai';
124
+
125
+ init({ apiKey: process.env.LELEMON_API_KEY });
126
+ const openai = observe(new OpenAI());
127
+
128
+ async function main() {
129
+ const result = await openai.chat.completions.create({
130
+ model: 'gpt-4',
131
+ messages: [{ role: 'user', content: 'Hello!' }],
132
+ });
133
+
134
+ console.log(result.choices[0].message.content);
135
+
136
+ // IMPORTANT: Flush before exit to ensure traces are sent
137
+ await flush();
138
+ }
139
+
140
+ main();
141
+ ```
142
+
143
+ ### Custom HTTP Server (No Framework)
144
+
145
+ ```typescript
146
+ import http from 'http';
147
+ import { init, observe, flush } from '@lelemondev/sdk';
148
+ import OpenAI from 'openai';
149
+
150
+ init({ apiKey: process.env.LELEMON_API_KEY });
151
+
152
+ const server = http.createServer(async (req, res) => {
153
+ if (req.method === 'POST' && req.url === '/chat') {
154
+ const openai = observe(new OpenAI(), {
155
+ userId: req.headers['x-user-id'] as string,
156
+ });
157
+
158
+ const result = await openai.chat.completions.create({
159
+ model: 'gpt-4',
160
+ messages: [{ role: 'user', content: 'Hello!' }],
161
+ });
162
+
163
+ res.writeHead(200, { 'Content-Type': 'application/json' });
164
+ res.end(JSON.stringify(result.choices[0].message));
165
+ // Auto-flush handles this in background
166
+ }
167
+ });
168
+
169
+ // Graceful shutdown - flush remaining traces
170
+ process.on('SIGTERM', async () => {
171
+ await flush();
172
+ server.close();
173
+ });
174
+
175
+ server.listen(3000);
176
+ ```
177
+
178
+ ### When to Use Framework Integrations vs Manual
179
+
180
+ | Scenario | Recommendation |
181
+ |----------|----------------|
182
+ | Next.js, Express, Hono, Lambda | Use framework integration (auto-flush) |
183
+ | Custom HTTP server | Auto-flush works, add `flush()` on shutdown |
184
+ | CLI scripts | Always call `flush()` before exit |
185
+ | Background workers (Bull, BullMQ) | Auto-flush works, add `flush()` on shutdown |
186
+ | One-off scripts | Always call `flush()` before exit |
187
+ | Long-running daemons | Auto-flush works |
188
+
42
189
  ## Framework Integrations
43
190
 
191
+ Framework integrations automate the `flush()` call so you don't have to think about it.
192
+
44
193
  ### Next.js App Router
45
194
 
46
195
  ```typescript
@@ -138,13 +287,6 @@ app.post('/chat', async (c) => {
138
287
  export default app;
139
288
  ```
140
289
 
141
- ## Supported Providers
142
-
143
- | Provider | Status | Methods |
144
- |----------|--------|---------|
145
- | OpenAI | Supported | `chat.completions.create()`, `completions.create()`, `embeddings.create()` |
146
- | Anthropic | Supported | `messages.create()`, `messages.stream()` |
147
-
148
290
  ## API Reference
149
291
 
150
292
  ### `init(config)`
@@ -183,9 +325,295 @@ Manually flush pending traces. Use in serverless without framework integration.
183
325
  await flush();
184
326
  ```
185
327
 
328
+ ## User & Session Tracking
329
+
330
+ Track which user generated each trace and group related conversations.
331
+
332
+ ### Available Fields
333
+
334
+ | Field | Purpose | In Dashboard |
335
+ |-------|---------|--------------|
336
+ | `userId` | Identify the end user | Shown in "User" column, searchable |
337
+ | `sessionId` | Group related traces (e.g., a conversation) | Shown in "Session" column, searchable |
338
+ | `metadata` | Custom data attached to the trace | Visible in trace detail view |
339
+ | `tags` | Labels for categorization | Shown as badges, filterable dropdown |
340
+
341
+ #### Field Details
342
+
343
+ **`userId`** - Identifies who made the request
344
+ ```typescript
345
+ userId: req.user.id // From your auth system
346
+ userId: 'user-123' // Any string identifier
347
+ userId: req.user.email // Email works too
348
+ ```
349
+
350
+ **`sessionId`** - Groups multiple calls into one conversation/session
351
+ ```typescript
352
+ sessionId: req.body.conversationId // Chat conversation ID
353
+ sessionId: req.headers['x-session-id'] // From client header
354
+ sessionId: crypto.randomUUID() // Generate per session
355
+ ```
356
+
357
+ **`metadata`** - Any extra data you want to attach (stored as JSON)
358
+ ```typescript
359
+ metadata: {
360
+ plan: 'premium', // User's subscription plan
361
+ feature: 'chat', // Which feature triggered this
362
+ version: '2.1.0', // Your app version
363
+ environment: 'production', // Environment
364
+ ip: req.ip, // Client IP (for debugging)
365
+ customField: 'any value', // Anything you need
366
+ }
367
+ ```
368
+
369
+ **`tags`** - Quick labels for filtering (array of strings)
370
+ ```typescript
371
+ tags: ['production'] // Environment
372
+ tags: ['chat', 'premium'] // Feature + plan
373
+ tags: ['api', 'v2'] // Service + version
374
+ tags: [tenantId, 'high-priority'] // Multi-tenant + priority
375
+ ```
376
+
377
+ ### Basic Usage
378
+
379
+ ```typescript
380
+ const openai = observe(new OpenAI(), {
381
+ userId: 'user-123',
382
+ sessionId: 'conversation-abc',
383
+ });
384
+ ```
385
+
386
+ ### In an API Endpoint
387
+
388
+ ```typescript
389
+ // Express
390
+ app.post('/chat', async (req, res) => {
391
+ // Create client with user context from the request
392
+ const openai = observe(new OpenAI(), {
393
+ userId: req.user.id,
394
+ sessionId: req.headers['x-session-id'],
395
+ metadata: {
396
+ plan: req.user.plan,
397
+ endpoint: '/chat'
398
+ },
399
+ });
400
+
401
+ const result = await openai.chat.completions.create({
402
+ model: 'gpt-4',
403
+ messages: [{ role: 'user', content: req.body.message }],
404
+ });
405
+
406
+ res.json(result.choices[0].message);
407
+ });
408
+ ```
409
+
410
+ ### Reusable Context with createObserve()
411
+
412
+ When you have multiple LLM clients or make calls from different places, use `createObserve()` to avoid repeating context:
413
+
414
+ ```typescript
415
+ import { createObserve } from '@lelemondev/sdk';
416
+ import OpenAI from 'openai';
417
+ import { GoogleGenerativeAI } from '@google/generative-ai';
418
+ import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
419
+
420
+ // Create a scoped observe function with user context
421
+ const observeForUser = createObserve({
422
+ userId: 'user-123',
423
+ sessionId: 'session-456',
424
+ tags: ['premium'],
425
+ });
426
+
427
+ // All clients inherit the same context
428
+ const openai = observeForUser(new OpenAI());
429
+ const gemini = observeForUser(new GoogleGenerativeAI(apiKey));
430
+ const bedrock = observeForUser(new BedrockRuntimeClient({}));
431
+
432
+ // All these calls will be associated with user-123
433
+ await openai.chat.completions.create({ ... });
434
+ await gemini.getGenerativeModel({ model: 'gemini-pro' }).generateContent('...');
435
+ ```
436
+
437
+ ### In Middleware (Express)
438
+
439
+ Set up user context once, use it everywhere:
440
+
441
+ ```typescript
442
+ import { createObserve } from '@lelemondev/sdk';
443
+
444
+ // Middleware to attach observe function to request
445
+ app.use((req, res, next) => {
446
+ req.observe = createObserve({
447
+ userId: req.user?.id,
448
+ sessionId: req.headers['x-session-id'] || crypto.randomUUID(),
449
+ metadata: {
450
+ ip: req.ip,
451
+ userAgent: req.headers['user-agent'],
452
+ },
453
+ });
454
+ next();
455
+ });
456
+
457
+ // In any route handler
458
+ app.post('/chat', async (req, res) => {
459
+ const openai = req.observe(new OpenAI());
460
+ // Calls are automatically associated with the user
461
+ });
462
+
463
+ app.post('/summarize', async (req, res) => {
464
+ const gemini = req.observe(new GoogleGenerativeAI(apiKey));
465
+ // Same user context, different endpoint
466
+ });
467
+ ```
468
+
469
+ ### Multi-tenant Application
470
+
471
+ ```typescript
472
+ app.post('/api/:tenantId/chat', async (req, res) => {
473
+ const openai = observe(new OpenAI(), {
474
+ userId: req.user.id,
475
+ sessionId: req.body.conversationId,
476
+ metadata: {
477
+ tenantId: req.params.tenantId,
478
+ environment: process.env.NODE_ENV,
479
+ },
480
+ tags: [req.params.tenantId, req.user.plan],
481
+ });
482
+
483
+ // Traces can be filtered by tenant, user, or conversation
484
+ });
485
+ ```
486
+
487
+ ### Distributed Systems (API + WebSocket)
488
+
489
+ When your application spans multiple services (REST API, WebSocket server, background workers), use consistent identifiers across all of them:
490
+
491
+ ```typescript
492
+ // Shared utility for creating observe context
493
+ // utils/observe-context.ts
494
+ import { createObserve } from '@lelemondev/sdk';
495
+
496
+ export function createUserObserve(userId: string, sessionId: string, service: string) {
497
+ return createObserve({
498
+ userId,
499
+ sessionId,
500
+ metadata: { service },
501
+ tags: [service],
502
+ });
503
+ }
504
+ ```
505
+
506
+ **REST API Server:**
507
+
508
+ ```typescript
509
+ // api-server.ts
510
+ import { createUserObserve } from './utils/observe-context';
511
+
512
+ app.post('/chat/start', async (req, res) => {
513
+ const { userId, sessionId } = req.body;
514
+
515
+ const observe = createUserObserve(userId, sessionId, 'api');
516
+ const openai = observe(new OpenAI());
517
+
518
+ const result = await openai.chat.completions.create({
519
+ model: 'gpt-4',
520
+ messages: [{ role: 'user', content: req.body.message }],
521
+ });
522
+
523
+ res.json({
524
+ response: result.choices[0].message,
525
+ sessionId, // Return sessionId for client to use in WebSocket
526
+ });
527
+ });
528
+ ```
529
+
530
+ **WebSocket Server:**
531
+
532
+ ```typescript
533
+ // ws-server.ts
534
+ import { createUserObserve } from './utils/observe-context';
535
+
536
+ wss.on('connection', (ws, req) => {
537
+ // Get userId and sessionId from connection (query params, auth token, etc.)
538
+ const userId = getUserFromToken(req);
539
+ const sessionId = new URL(req.url, 'http://localhost').searchParams.get('sessionId');
540
+
541
+ // Create observe function for this connection
542
+ const observe = createUserObserve(userId, sessionId, 'websocket');
543
+ const gemini = observe(new GoogleGenerativeAI(apiKey));
544
+
545
+ ws.on('message', async (data) => {
546
+ const { message } = JSON.parse(data);
547
+
548
+ // This trace will be linked to the same session as the API calls
549
+ const model = gemini.getGenerativeModel({ model: 'gemini-pro' });
550
+ const result = await model.generateContent(message);
551
+
552
+ ws.send(JSON.stringify({ response: result.response.text() }));
553
+ });
554
+ });
555
+ ```
556
+
557
+ **Background Worker / Queue Consumer:**
558
+
559
+ ```typescript
560
+ // worker.ts
561
+ import { createUserObserve } from './utils/observe-context';
562
+
563
+ queue.process('ai-task', async (job) => {
564
+ const { userId, sessionId, prompt } = job.data;
565
+
566
+ const observe = createUserObserve(userId, sessionId, 'worker');
567
+ const bedrock = observe(new BedrockRuntimeClient({}));
568
+
569
+ const command = new ConverseCommand({
570
+ modelId: 'anthropic.claude-3-sonnet-20240229-v1:0',
571
+ messages: [{ role: 'user', content: [{ text: prompt }] }],
572
+ });
573
+
574
+ const result = await bedrock.send(command);
575
+ return result.output.message.content[0].text;
576
+ });
577
+ ```
578
+
579
+ **Client-side (passing sessionId between services):**
580
+
581
+ ```typescript
582
+ // Frontend - maintains sessionId across API and WebSocket
583
+ const sessionId = crypto.randomUUID(); // Or from your session management
584
+
585
+ // REST API call
586
+ const response = await fetch('/chat/start', {
587
+ method: 'POST',
588
+ body: JSON.stringify({ userId, sessionId, message: 'Hello' }),
589
+ });
590
+
591
+ // WebSocket connection with same sessionId
592
+ const ws = new WebSocket(`wss://your-app.com/ws?sessionId=${sessionId}`);
593
+
594
+ // Both API and WebSocket traces will be grouped under the same session
595
+ ```
596
+
597
+ **Viewing in Dashboard:**
598
+
599
+ With consistent `userId` and `sessionId` across services, you can:
600
+ - See a user's complete journey across API → WebSocket → Background jobs
601
+ - Filter by service (`metadata.service`) to isolate issues
602
+ - Track a conversation that spans multiple services
603
+
604
+ ### What This Enables
605
+
606
+ With proper user/session tracking you can:
607
+
608
+ - **Filter traces by user** - See all LLM calls from a specific user
609
+ - **View full conversations** - Group all calls in a session together
610
+ - **Debug issues** - Find exactly what happened for a specific user request
611
+ - **Analyze usage patterns** - Understand how different user segments use your AI features
612
+ - **Cost attribution** - Track token usage per user, tenant, or feature
613
+
186
614
  ## Streaming
187
615
 
188
- Both OpenAI and Anthropic streaming are fully supported:
616
+ All providers support streaming:
189
617
 
190
618
  ```typescript
191
619
  const stream = await openai.chat.completions.create({
@@ -204,8 +632,8 @@ for await (const chunk of stream) {
204
632
 
205
633
  Each LLM call automatically captures:
206
634
 
207
- - **Provider** - openai, anthropic
208
- - **Model** - gpt-4, claude-3-opus, etc.
635
+ - **Provider** - openai, anthropic, bedrock, gemini
636
+ - **Model** - gpt-4, claude-3-opus, gemini-pro, etc.
209
637
  - **Input** - Messages/prompt (sanitized)
210
638
  - **Output** - Response content
211
639
  - **Tokens** - Input and output counts
@@ -10,15 +10,31 @@
10
10
  * const app = express();
11
11
  * app.use(createMiddleware());
12
12
  */
13
+ /**
14
+ * Minimal Express request type (avoids requiring express as dependency)
15
+ */
13
16
  interface ExpressRequest {
14
17
  [key: string]: unknown;
15
18
  }
19
+ /**
20
+ * Minimal Express response type (avoids requiring express as dependency)
21
+ */
16
22
  interface ExpressResponse {
17
23
  on(event: 'finish' | 'close' | 'error', listener: () => void): this;
18
24
  [key: string]: unknown;
19
25
  }
20
- type NextFunction = (error?: unknown) => void;
21
- type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
26
+ /**
27
+ * Express next function type
28
+ */
29
+ type ExpressNextFunction = (error?: unknown) => void;
30
+ /**
31
+ * Express middleware function type
32
+ *
33
+ * @param req - Express request object
34
+ * @param res - Express response object
35
+ * @param next - Next function to pass control
36
+ */
37
+ type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void;
22
38
  /**
23
39
  * Create Express middleware for automatic trace flushing
24
40
  *
@@ -39,9 +55,13 @@ type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: NextF
39
55
  */
40
56
  declare function createMiddleware(): ExpressMiddleware;
41
57
 
58
+ type express_ExpressMiddleware = ExpressMiddleware;
59
+ type express_ExpressNextFunction = ExpressNextFunction;
60
+ type express_ExpressRequest = ExpressRequest;
61
+ type express_ExpressResponse = ExpressResponse;
42
62
  declare const express_createMiddleware: typeof createMiddleware;
43
63
  declare namespace express {
44
- export { express_createMiddleware as createMiddleware };
64
+ export { type express_ExpressMiddleware as ExpressMiddleware, type express_ExpressNextFunction as ExpressNextFunction, type express_ExpressRequest as ExpressRequest, type express_ExpressResponse as ExpressResponse, express_createMiddleware as createMiddleware };
45
65
  }
46
66
 
47
- export { createMiddleware as c, express as e };
67
+ export { type ExpressRequest as E, type ExpressResponse as a, type ExpressNextFunction as b, type ExpressMiddleware as c, createMiddleware as d, express as e };
@@ -10,15 +10,31 @@
10
10
  * const app = express();
11
11
  * app.use(createMiddleware());
12
12
  */
13
+ /**
14
+ * Minimal Express request type (avoids requiring express as dependency)
15
+ */
13
16
  interface ExpressRequest {
14
17
  [key: string]: unknown;
15
18
  }
19
+ /**
20
+ * Minimal Express response type (avoids requiring express as dependency)
21
+ */
16
22
  interface ExpressResponse {
17
23
  on(event: 'finish' | 'close' | 'error', listener: () => void): this;
18
24
  [key: string]: unknown;
19
25
  }
20
- type NextFunction = (error?: unknown) => void;
21
- type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
26
+ /**
27
+ * Express next function type
28
+ */
29
+ type ExpressNextFunction = (error?: unknown) => void;
30
+ /**
31
+ * Express middleware function type
32
+ *
33
+ * @param req - Express request object
34
+ * @param res - Express response object
35
+ * @param next - Next function to pass control
36
+ */
37
+ type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void;
22
38
  /**
23
39
  * Create Express middleware for automatic trace flushing
24
40
  *
@@ -39,9 +55,13 @@ type ExpressMiddleware = (req: ExpressRequest, res: ExpressResponse, next: NextF
39
55
  */
40
56
  declare function createMiddleware(): ExpressMiddleware;
41
57
 
58
+ type express_ExpressMiddleware = ExpressMiddleware;
59
+ type express_ExpressNextFunction = ExpressNextFunction;
60
+ type express_ExpressRequest = ExpressRequest;
61
+ type express_ExpressResponse = ExpressResponse;
42
62
  declare const express_createMiddleware: typeof createMiddleware;
43
63
  declare namespace express {
44
- export { express_createMiddleware as createMiddleware };
64
+ export { type express_ExpressMiddleware as ExpressMiddleware, type express_ExpressNextFunction as ExpressNextFunction, type express_ExpressRequest as ExpressRequest, type express_ExpressResponse as ExpressResponse, express_createMiddleware as createMiddleware };
45
65
  }
46
66
 
47
- export { createMiddleware as c, express as e };
67
+ export { type ExpressRequest as E, type ExpressResponse as a, type ExpressNextFunction as b, type ExpressMiddleware as c, createMiddleware as d, express as e };
@@ -1 +1 @@
1
- export { c as createMiddleware } from './express-Cmb_A4sI.mjs';
1
+ export { c as ExpressMiddleware, b as ExpressNextFunction, E as ExpressRequest, a as ExpressResponse, d as createMiddleware } from './express-Dt5wT6_n.mjs';
package/dist/express.d.ts CHANGED
@@ -1 +1 @@
1
- export { c as createMiddleware } from './express-Cmb_A4sI.js';
1
+ export { c as ExpressMiddleware, b as ExpressNextFunction, E as ExpressRequest, a as ExpressResponse, d as createMiddleware } from './express-Dt5wT6_n.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;;;AAuEA,eAAsB,KAAA,GAAuB;AAI7C;;;ACjBO,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';\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://api.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 globalTransport = createTransport(config);\n initialized = true;\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 console.warn(\n '[Lelemon] No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.'\n );\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\ninterface ExpressRequest {\n [key: string]: unknown;\n}\n\ninterface ExpressResponse {\n on(event: 'finish' | 'close' | 'error', listener: () => void): this;\n [key: string]: unknown;\n}\n\ntype NextFunction = (error?: unknown) => void;\n\ntype ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\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"]}
1
+ {"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;;;AAuEA,eAAsB,KAAA,GAAuB;AAI7C;;;ACDO,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';\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 globalTransport = createTransport(config);\n initialized = true;\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 console.warn(\n '[Lelemon] No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.'\n );\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"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;AAuEA,eAAsB,KAAA,GAAuB;AAI7C;;;ACjBO,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';\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://api.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 globalTransport = createTransport(config);\n initialized = true;\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 console.warn(\n '[Lelemon] No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.'\n );\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\ninterface ExpressRequest {\n [key: string]: unknown;\n}\n\ninterface ExpressResponse {\n on(event: 'finish' | 'close' | 'error', listener: () => void): this;\n [key: string]: unknown;\n}\n\ntype NextFunction = (error?: unknown) => void;\n\ntype ExpressMiddleware = (\n req: ExpressRequest,\n res: ExpressResponse,\n next: NextFunction\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"]}
1
+ {"version":3,"sources":["../src/core/config.ts","../src/integrations/express.ts"],"names":[],"mappings":";;AAuEA,eAAsB,KAAA,GAAuB;AAI7C;;;ACDO,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';\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 globalTransport = createTransport(config);\n initialized = true;\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 console.warn(\n '[Lelemon] No API key provided. Set apiKey in init() or LELEMON_API_KEY env var. Tracing disabled.'\n );\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"]}