@fullevent/node 0.0.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.
@@ -0,0 +1,1096 @@
1
+ import { Context, Next } from 'hono';
2
+ import { Request, Response, NextFunction } from 'express';
3
+
4
+ /**
5
+ * Configuration for tail-based sampling.
6
+ *
7
+ * @remarks
8
+ * Tail sampling makes the decision AFTER the request completes,
9
+ * allowing you to keep all errors and slow requests while sampling
10
+ * normal traffic. This gives you 100% visibility into problems.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const sampling: SamplingConfig = {
15
+ * defaultRate: 0.1, // Keep 10% of normal requests
16
+ * alwaysKeepErrors: true, // But 100% of errors
17
+ * slowRequestThresholdMs: 500, // And anything slow
18
+ * alwaysKeepPaths: ['/api/checkout', '/api/payment'],
19
+ * alwaysKeepUsers: ['usr_vip_123'],
20
+ * };
21
+ * ```
22
+ *
23
+ * @category Middleware
24
+ */
25
+ interface SamplingConfig {
26
+ /**
27
+ * Base sample rate for normal successful requests.
28
+ *
29
+ * @remarks
30
+ * Value between 0.0 and 1.0. For example, 0.1 means 10% of
31
+ * successful requests are kept.
32
+ *
33
+ * @defaultValue `1.0` (keep all)
34
+ */
35
+ defaultRate?: number;
36
+ /**
37
+ * Always keep error responses (4xx/5xx status codes).
38
+ *
39
+ * @remarks
40
+ * Highly recommended to leave enabled. Errors are rare and
41
+ * you want 100% visibility into failures.
42
+ *
43
+ * @defaultValue `true`
44
+ */
45
+ alwaysKeepErrors?: boolean;
46
+ /**
47
+ * Always keep requests slower than this threshold.
48
+ *
49
+ * @remarks
50
+ * Slow requests often indicate problems. This ensures you
51
+ * capture them even at low sample rates.
52
+ *
53
+ * @defaultValue `2000` (2 seconds)
54
+ */
55
+ slowRequestThresholdMs?: number;
56
+ /**
57
+ * Always keep requests matching these paths.
58
+ *
59
+ * @remarks
60
+ * Uses substring matching. Useful for critical paths like
61
+ * checkout, payment, or authentication.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * alwaysKeepPaths: ['/api/checkout', '/api/payment', '/auth']
66
+ * ```
67
+ */
68
+ alwaysKeepPaths?: string[];
69
+ /**
70
+ * Always keep requests from these user IDs.
71
+ *
72
+ * @remarks
73
+ * Useful for debugging specific users or VIP accounts.
74
+ * Requires `user_id` to be set on the event.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * alwaysKeepUsers: ['usr_vip_123', 'usr_debug_456']
79
+ * ```
80
+ */
81
+ alwaysKeepUsers?: string[];
82
+ }
83
+ /**
84
+ * Configuration for the wide logger middleware.
85
+ *
86
+ * @example Minimal Config
87
+ * ```typescript
88
+ * wideLogger({
89
+ * apiKey: process.env.FULLEVENT_API_KEY!,
90
+ * service: 'checkout-api',
91
+ * })
92
+ * ```
93
+ *
94
+ * @example Full Config
95
+ * ```typescript
96
+ * wideLogger({
97
+ * apiKey: process.env.FULLEVENT_API_KEY!,
98
+ * service: 'checkout-api',
99
+ * environment: 'production',
100
+ * region: 'us-east-1',
101
+ * sampling: {
102
+ * defaultRate: 0.1,
103
+ * alwaysKeepErrors: true,
104
+ * slowRequestThresholdMs: 500,
105
+ * },
106
+ * })
107
+ * ```
108
+ *
109
+ * @category Middleware
110
+ */
111
+ interface WideLoggerConfig {
112
+ /**
113
+ * Your FullEvent API key.
114
+ *
115
+ * @remarks
116
+ * Get this from your FullEvent dashboard under Project Settings → API Keys.
117
+ */
118
+ apiKey: string;
119
+ /**
120
+ * Service name to tag all events with.
121
+ *
122
+ * @remarks
123
+ * Used for filtering and grouping in the dashboard.
124
+ * Examples: 'checkout-api', 'user-service', 'payment-worker'
125
+ */
126
+ service: string;
127
+ /**
128
+ * Base URL for the FullEvent API.
129
+ *
130
+ * @defaultValue `'https://api.fullevent.io'`
131
+ *
132
+ * @remarks
133
+ * Only override for self-hosted deployments or local development.
134
+ */
135
+ baseUrl?: string;
136
+ /**
137
+ * Environment tag for all events.
138
+ *
139
+ * @defaultValue `process.env.NODE_ENV` or `'development'`
140
+ */
141
+ environment?: string;
142
+ /**
143
+ * Region tag for all events.
144
+ *
145
+ * @remarks
146
+ * Useful for multi-region deployments. Examples: 'us-east-1', 'eu-west-1'
147
+ */
148
+ region?: string;
149
+ /**
150
+ * Sampling configuration for tail-based sampling.
151
+ *
152
+ * @see {@link SamplingConfig}
153
+ */
154
+ sampling?: SamplingConfig;
155
+ }
156
+ /**
157
+ * Type for Hono context variables.
158
+ *
159
+ * @remarks
160
+ * Use this to type your Hono app for proper TypeScript support.
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * import { Hono } from 'hono';
165
+ * import { wideLogger, WideEventVariables } from '@fullevent/node-sdk';
166
+ *
167
+ * const app = new Hono<{ Variables: WideEventVariables }>();
168
+ *
169
+ * app.use(wideLogger({ apiKey: '...', service: 'my-api' }));
170
+ *
171
+ * app.get('/', (c) => {
172
+ * const event = c.get('wideEvent'); // Properly typed!
173
+ * event.user_id = 'usr_123';
174
+ * return c.text('Hello!');
175
+ * });
176
+ * ```
177
+ *
178
+ * @category Middleware
179
+ */
180
+ type WideEventVariables = {
181
+ /** The wide event for the current request */
182
+ wideEvent: WideEvent;
183
+ };
184
+ /**
185
+ * Hono middleware for Wide Event logging.
186
+ *
187
+ * @remarks
188
+ * Creates a single, rich event per request that captures the complete context.
189
+ * The event is initialized with request data and automatically finalized with
190
+ * status, duration, and outcome. Your handlers enrich it with business context.
191
+ *
192
+ * ## How It Works
193
+ *
194
+ * 1. **Request Start**: Middleware creates the event with request context
195
+ * 2. **Handler Runs**: Your code enriches the event via `c.get('wideEvent')`
196
+ * 3. **Request End**: Middleware adds status/duration and sends to FullEvent
197
+ *
198
+ * ## Features
199
+ *
200
+ * - **Automatic context capture**: method, path, status, duration, errors
201
+ * - **Distributed tracing**: Propagates `x-fullevent-trace-id` headers
202
+ * - **Tail-based sampling**: 100% error visibility at any sample rate
203
+ * - **Fire and forget**: Non-blocking, won't slow down your responses
204
+ *
205
+ * @param config - Middleware configuration
206
+ * @returns Hono middleware function
207
+ *
208
+ * @example Quick Start
209
+ * ```typescript
210
+ * import { Hono } from 'hono';
211
+ * import { wideLogger, WideEventVariables } from '@fullevent/node-sdk';
212
+ *
213
+ * const app = new Hono<{ Variables: WideEventVariables }>();
214
+ *
215
+ * app.use(wideLogger({
216
+ * apiKey: process.env.FULLEVENT_API_KEY!,
217
+ * service: 'checkout-api',
218
+ * }));
219
+ * ```
220
+ *
221
+ * @example Enriching Events
222
+ * ```typescript
223
+ * app.post('/checkout', async (c) => {
224
+ * const event = c.get('wideEvent');
225
+ *
226
+ * // Add user context
227
+ * event.user = {
228
+ * id: user.id,
229
+ * subscription: user.plan,
230
+ * account_age_days: daysSince(user.createdAt),
231
+ * };
232
+ *
233
+ * // Add cart context
234
+ * event.cart = {
235
+ * id: cart.id,
236
+ * item_count: cart.items.length,
237
+ * total_cents: cart.total,
238
+ * };
239
+ *
240
+ * // Add payment timing
241
+ * const paymentStart = Date.now();
242
+ * const payment = await processPayment(cart);
243
+ * event.payment = {
244
+ * provider: 'stripe',
245
+ * latency_ms: Date.now() - paymentStart,
246
+ * };
247
+ *
248
+ * return c.json({ orderId: payment.orderId });
249
+ * });
250
+ * ```
251
+ *
252
+ * @example With Sampling
253
+ * ```typescript
254
+ * app.use(wideLogger({
255
+ * apiKey: process.env.FULLEVENT_API_KEY!,
256
+ * service: 'checkout-api',
257
+ * sampling: {
258
+ * defaultRate: 0.1, // 10% of normal traffic
259
+ * alwaysKeepErrors: true, // 100% of errors
260
+ * slowRequestThresholdMs: 500, // Slow requests
261
+ * alwaysKeepPaths: ['/api/checkout'], // Critical paths
262
+ * },
263
+ * }));
264
+ * ```
265
+ *
266
+ * @category Middleware
267
+ */
268
+ declare const wideLogger: (config: WideLoggerConfig) => (c: Context<{
269
+ Variables: WideEventVariables;
270
+ }>, next: Next) => Promise<void>;
271
+
272
+ declare global {
273
+ namespace Express {
274
+ interface Request {
275
+ /** The wide event for the current request */
276
+ wideEvent: WideEvent;
277
+ }
278
+ }
279
+ }
280
+ /**
281
+ * Express middleware for Wide Event logging.
282
+ *
283
+ * @remarks
284
+ * Creates a single, rich event per request that captures the complete context.
285
+ * The event is initialized with request data and automatically finalized with
286
+ * status, duration, and outcome. Your handlers enrich it with business context.
287
+ *
288
+ * ## How It Works
289
+ *
290
+ * 1. **Request Start**: Middleware creates the event with request context
291
+ * 2. **Handler Runs**: Your code enriches the event via `req.wideEvent`
292
+ * 3. **Response Finish**: Middleware adds status/duration and sends to FullEvent
293
+ *
294
+ * ## Features
295
+ *
296
+ * - **Automatic context capture**: method, path, status, duration
297
+ * - **Distributed tracing**: Propagates `x-fullevent-trace-id` headers
298
+ * - **Tail-based sampling**: 100% error visibility at any sample rate
299
+ * - **Fire and forget**: Non-blocking, won't slow down your responses
300
+ *
301
+ * @param config - Middleware configuration
302
+ * @returns Express middleware function
303
+ *
304
+ * @example Quick Start
305
+ * ```typescript
306
+ * import express from 'express';
307
+ * import { expressWideLogger } from '@fullevent/node-sdk';
308
+ *
309
+ * const app = express();
310
+ *
311
+ * app.use(expressWideLogger({
312
+ * apiKey: process.env.FULLEVENT_API_KEY!,
313
+ * service: 'checkout-api',
314
+ * }));
315
+ * ```
316
+ *
317
+ * @example Enriching Events
318
+ * ```typescript
319
+ * app.post('/checkout', async (req, res) => {
320
+ * const event = req.wideEvent;
321
+ *
322
+ * // Add user context
323
+ * event.user = {
324
+ * id: req.user.id,
325
+ * subscription: req.user.plan,
326
+ * };
327
+ *
328
+ * // Add cart context
329
+ * event.cart = {
330
+ * id: cart.id,
331
+ * item_count: cart.items.length,
332
+ * total_cents: cart.total,
333
+ * };
334
+ *
335
+ * // Add payment timing
336
+ * const paymentStart = Date.now();
337
+ * const payment = await processPayment(cart);
338
+ * event.payment = {
339
+ * provider: 'stripe',
340
+ * latency_ms: Date.now() - paymentStart,
341
+ * };
342
+ *
343
+ * res.json({ orderId: payment.orderId });
344
+ * });
345
+ * ```
346
+ *
347
+ * @example With Sampling
348
+ * ```typescript
349
+ * app.use(expressWideLogger({
350
+ * apiKey: process.env.FULLEVENT_API_KEY!,
351
+ * service: 'checkout-api',
352
+ * sampling: {
353
+ * defaultRate: 0.1, // 10% of normal traffic
354
+ * alwaysKeepErrors: true, // 100% of errors
355
+ * slowRequestThresholdMs: 500, // Slow requests
356
+ * },
357
+ * }));
358
+ * ```
359
+ *
360
+ * @category Middleware
361
+ */
362
+ declare function expressWideLogger(config: WideLoggerConfig): (req: Request, res: Response, next: NextFunction) => void;
363
+
364
+ /**
365
+ * Configuration options for the FullEvent client.
366
+ *
367
+ * @example
368
+ * ```typescript
369
+ * const client = new FullEvent({
370
+ * apiKey: process.env.FULLEVENT_API_KEY!,
371
+ * baseUrl: 'https://api.fullevent.io', // optional
372
+ * ping: true, // send a test ping on initialization
373
+ * });
374
+ * ```
375
+ *
376
+ * @category Client
377
+ */
378
+ interface FullEventConfig {
379
+ /**
380
+ * Your FullEvent project API key.
381
+ *
382
+ * @remarks
383
+ * Get this from your FullEvent dashboard under Project Settings → API Keys.
384
+ */
385
+ apiKey: string;
386
+ /**
387
+ * Base URL for the FullEvent API.
388
+ *
389
+ * @defaultValue `'https://api.fullevent.io'`
390
+ *
391
+ * @remarks
392
+ * Only override this for self-hosted deployments or local development.
393
+ */
394
+ baseUrl?: string;
395
+ /**
396
+ * Send a ping event on initialization to verify connection.
397
+ *
398
+ * @defaultValue `false`
399
+ *
400
+ * @remarks
401
+ * Set to `true` during initial setup to verify your SDK configuration
402
+ * is working correctly. The ping is sent asynchronously and won't block
403
+ * your application startup.
404
+ */
405
+ ping?: boolean;
406
+ }
407
+ /**
408
+ * Standard properties for HTTP request events.
409
+ *
410
+ * These properties are automatically extracted by FullEvent for first-class
411
+ * filtering, dashboards, and error rate calculations.
412
+ *
413
+ * @example
414
+ * ```typescript
415
+ * const props: HttpRequestProperties = {
416
+ * status_code: 200,
417
+ * method: 'POST',
418
+ * path: '/api/checkout',
419
+ * duration_ms: 234,
420
+ * outcome: 'success',
421
+ * // Plus any custom properties
422
+ * user_id: 'usr_123',
423
+ * order_total: 9999,
424
+ * };
425
+ * ```
426
+ *
427
+ * @category Client
428
+ */
429
+ interface HttpRequestProperties {
430
+ /**
431
+ * HTTP status code (e.g., 200, 404, 500).
432
+ *
433
+ * @remarks
434
+ * Used for automatic error rate calculations:
435
+ * - `< 400` = success
436
+ * - `>= 400` = error
437
+ */
438
+ status_code: number;
439
+ /**
440
+ * HTTP method (GET, POST, PUT, DELETE, etc.)
441
+ */
442
+ method?: string;
443
+ /**
444
+ * Request path (e.g., `/api/users`, `/checkout`)
445
+ */
446
+ path?: string;
447
+ /**
448
+ * Request duration in milliseconds.
449
+ */
450
+ duration_ms?: number;
451
+ /**
452
+ * Explicit success/error indicator.
453
+ *
454
+ * @remarks
455
+ * If set, this takes precedence over `status_code` for error rate calculations.
456
+ */
457
+ outcome?: 'success' | 'error';
458
+ /**
459
+ * Additional custom properties.
460
+ *
461
+ * @remarks
462
+ * Add any context relevant to your application.
463
+ */
464
+ [key: string]: unknown;
465
+ }
466
+ /**
467
+ * The main FullEvent client for ingesting events.
468
+ *
469
+ * @remarks
470
+ * Use this client to send events directly from your application.
471
+ * For automatic request logging, see the middleware integrations:
472
+ * - {@link wideLogger} for Hono
473
+ * - {@link expressWideLogger} for Express
474
+ *
475
+ * @example Basic Usage
476
+ * ```typescript
477
+ * import { FullEvent } from '@fullevent/node-sdk';
478
+ *
479
+ * const client = new FullEvent({
480
+ * apiKey: process.env.FULLEVENT_API_KEY!,
481
+ * });
482
+ *
483
+ * // Ingest a simple event
484
+ * await client.ingest('user.signup', {
485
+ * plan: 'pro',
486
+ * referral: 'newsletter',
487
+ * });
488
+ * ```
489
+ *
490
+ * @example Fire and Forget
491
+ * ```typescript
492
+ * // Don't await if you don't want to block
493
+ * client.ingest('page.view', { path: '/home' })
494
+ * .catch(err => console.error('FullEvent error:', err));
495
+ * ```
496
+ *
497
+ * @category Client
498
+ */
499
+ declare class FullEvent {
500
+ private apiKey;
501
+ private baseUrl;
502
+ /**
503
+ * Creates a new FullEvent client instance.
504
+ *
505
+ * @param config - Configuration options
506
+ */
507
+ constructor(config: FullEventConfig);
508
+ /**
509
+ * Ingest a generic event with any properties.
510
+ *
511
+ * @param event - Event name/type (e.g., 'user.signup', 'order.completed')
512
+ * @param properties - Key-value pairs of event data
513
+ * @param timestamp - Optional ISO timestamp (defaults to now)
514
+ * @returns Promise resolving to success/error status
515
+ *
516
+ * @remarks
517
+ * Events are processed asynchronously. The promise resolves when
518
+ * the event is accepted by the API, not when it's fully processed.
519
+ *
520
+ * @example
521
+ * ```typescript
522
+ * await client.ingest('checkout.completed', {
523
+ * order_id: 'ord_123',
524
+ * total_cents: 9999,
525
+ * items: 3,
526
+ * user: {
527
+ * id: 'usr_456',
528
+ * plan: 'premium',
529
+ * },
530
+ * });
531
+ * ```
532
+ */
533
+ ingest(event: string, properties?: Record<string, unknown>, timestamp?: string): Promise<{
534
+ success: boolean;
535
+ error?: unknown;
536
+ }>;
537
+ /**
538
+ * Ingest an HTTP request event with typed properties.
539
+ *
540
+ * @param properties - HTTP request properties with optional custom data
541
+ * @param timestamp - Optional ISO timestamp (defaults to now)
542
+ * @returns Promise resolving to success/error status
543
+ *
544
+ * @remarks
545
+ * This is a convenience method that provides TypeScript autocomplete
546
+ * for standard HTTP properties. Under the hood, it calls `ingest()`
547
+ * with the event type `'http_request'`.
548
+ *
549
+ * @example
550
+ * ```typescript
551
+ * await client.ingestHttpRequest({
552
+ * status_code: 200,
553
+ * method: 'POST',
554
+ * path: '/api/checkout',
555
+ * duration_ms: 234,
556
+ * outcome: 'success',
557
+ * // Custom properties
558
+ * user_id: 'usr_123',
559
+ * });
560
+ * ```
561
+ */
562
+ ingestHttpRequest(properties: HttpRequestProperties, timestamp?: string): Promise<{
563
+ success: boolean;
564
+ error?: unknown;
565
+ }>;
566
+ /**
567
+ * Ping the FullEvent API to verify connection.
568
+ *
569
+ * @returns Promise resolving to connection status with latency
570
+ *
571
+ * @remarks
572
+ * Use this method to verify your SDK is correctly configured.
573
+ * It sends a lightweight ping event and measures round-trip latency.
574
+ * Commonly used during setup or in health check endpoints.
575
+ *
576
+ * @example Basic ping
577
+ * ```typescript
578
+ * const result = await client.ping();
579
+ * if (result.success) {
580
+ * console.log(`Connected! Latency: ${result.latency_ms}ms`);
581
+ * } else {
582
+ * console.error('Connection failed:', result.error);
583
+ * }
584
+ * ```
585
+ *
586
+ * @example Health check endpoint
587
+ * ```typescript
588
+ * app.get('/health', async (c) => {
589
+ * const ping = await fullevent.ping();
590
+ * return c.json({
591
+ * status: ping.success ? 'healthy' : 'degraded',
592
+ * fullevent: ping,
593
+ * });
594
+ * });
595
+ * ```
596
+ */
597
+ ping(): Promise<{
598
+ success: boolean;
599
+ latency_ms?: number;
600
+ error?: unknown;
601
+ }>;
602
+ }
603
+
604
+ /**
605
+ * A fluent builder for enriching wide events throughout the request lifecycle.
606
+ *
607
+ * @remarks
608
+ * While you can modify the event object directly (and that's perfectly fine),
609
+ * the builder provides chainable methods for cleaner code and useful helpers.
610
+ *
611
+ * ## Two Approaches
612
+ *
613
+ * **Direct access (simple):**
614
+ * ```typescript
615
+ * const event = c.get('wideEvent');
616
+ * event.user = { id: user.id, plan: user.plan };
617
+ * event.cart = { total: cart.total, items: cart.items.length };
618
+ * ```
619
+ *
620
+ * **Builder pattern (chainable):**
621
+ * ```typescript
622
+ * new WideEventBuilder(c.get('wideEvent'))
623
+ * .setContext('user', { id: user.id, plan: user.plan })
624
+ * .setContext('cart', { total: cart.total, items: cart.items.length })
625
+ * .setTiming('db_latency_ms', dbStart);
626
+ * ```
627
+ *
628
+ * @example Full Example
629
+ * ```typescript
630
+ * const builder = new WideEventBuilder(c.get('wideEvent'));
631
+ *
632
+ * builder
633
+ * .setUser(user.id)
634
+ * .setContext('user', {
635
+ * subscription: user.plan,
636
+ * account_age_days: daysSince(user.createdAt),
637
+ * lifetime_value_cents: user.ltv,
638
+ * })
639
+ * .setContext('cart', {
640
+ * id: cart.id,
641
+ * item_count: cart.items.length,
642
+ * total_cents: cart.total,
643
+ * });
644
+ *
645
+ * // Later, after payment processing
646
+ * builder
647
+ * .setContext('payment', { method: 'card', provider: 'stripe' })
648
+ * .setTiming('payment_latency_ms', paymentStart);
649
+ *
650
+ * if (paymentError) {
651
+ * builder.setError({
652
+ * type: 'PaymentError',
653
+ * code: paymentError.code,
654
+ * message: paymentError.message,
655
+ * stripe_decline_code: paymentError.declineCode,
656
+ * });
657
+ * }
658
+ * ```
659
+ *
660
+ * @category Builder
661
+ */
662
+ declare class WideEventBuilder {
663
+ private event;
664
+ /**
665
+ * Creates a new builder wrapping the given event.
666
+ *
667
+ * @param event - The wide event to enrich
668
+ */
669
+ constructor(event: WideEvent);
670
+ /**
671
+ * Returns the underlying event object for direct access.
672
+ *
673
+ * @returns The wrapped WideEvent
674
+ *
675
+ * @example
676
+ * ```typescript
677
+ * const event = builder.getEvent();
678
+ * console.log(event.user_id);
679
+ * ```
680
+ */
681
+ getEvent(): WideEvent;
682
+ /**
683
+ * Sets any key-value pair on the event.
684
+ *
685
+ * @param key - Property name
686
+ * @param value - Property value (any type)
687
+ * @returns `this` for chaining
688
+ *
689
+ * @example
690
+ * ```typescript
691
+ * builder
692
+ * .set('order_id', 'ord_123')
693
+ * .set('llm_model', 'gpt-4')
694
+ * .set('tokens_used', 1500);
695
+ * ```
696
+ */
697
+ set(key: string, value: unknown): this;
698
+ /**
699
+ * Sets a named context object on the event.
700
+ *
701
+ * @remarks
702
+ * This is the primary method for adding structured business context.
703
+ * Each context is a nested object that groups related properties.
704
+ *
705
+ * @param name - Context name (e.g., 'user', 'cart', 'payment')
706
+ * @param data - Context data as key-value pairs
707
+ * @returns `this` for chaining
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * builder
712
+ * .setContext('user', {
713
+ * id: 'user_456',
714
+ * subscription: 'premium',
715
+ * account_age_days: 847,
716
+ * })
717
+ * .setContext('cart', {
718
+ * id: 'cart_xyz',
719
+ * item_count: 3,
720
+ * total_cents: 15999,
721
+ * })
722
+ * .setContext('payment', {
723
+ * method: 'card',
724
+ * provider: 'stripe',
725
+ * });
726
+ * ```
727
+ */
728
+ setContext(name: string, data: Record<string, unknown>): this;
729
+ /**
730
+ * Merges additional fields into an existing context object.
731
+ *
732
+ * @remarks
733
+ * Useful for progressively building context throughout the request.
734
+ * If the context doesn't exist, it will be created.
735
+ *
736
+ * @param name - Context name to merge into
737
+ * @param data - Additional data to merge
738
+ * @returns `this` for chaining
739
+ *
740
+ * @example
741
+ * ```typescript
742
+ * // Initial payment context
743
+ * builder.setContext('payment', { method: 'card', provider: 'stripe' });
744
+ *
745
+ * // After payment completes, add timing
746
+ * builder.mergeContext('payment', {
747
+ * latency_ms: Date.now() - paymentStart,
748
+ * attempt: 1,
749
+ * transaction_id: 'txn_123',
750
+ * });
751
+ *
752
+ * // Result: { method, provider, latency_ms, attempt, transaction_id }
753
+ * ```
754
+ */
755
+ mergeContext(name: string, data: Record<string, unknown>): this;
756
+ /**
757
+ * Sets the user ID on the event.
758
+ *
759
+ * @remarks
760
+ * This sets the top-level `user_id` field, which is commonly used
761
+ * for filtering and user-centric analytics. For richer user context,
762
+ * use `setContext('user', {...})` instead or in addition.
763
+ *
764
+ * @param userId - The user identifier
765
+ * @returns `this` for chaining
766
+ *
767
+ * @example
768
+ * ```typescript
769
+ * builder.setUser('usr_123');
770
+ *
771
+ * // For richer context, also set a user object:
772
+ * builder.setContext('user', {
773
+ * id: 'usr_123',
774
+ * plan: 'premium',
775
+ * ltv_cents: 50000,
776
+ * });
777
+ * ```
778
+ */
779
+ setUser(userId: string): this;
780
+ /**
781
+ * Captures an error with structured details.
782
+ *
783
+ * @remarks
784
+ * Automatically sets `outcome` to 'error'. Accepts either a native
785
+ * Error object or a custom error object with additional fields.
786
+ *
787
+ * @param err - Error object or custom error details
788
+ * @returns `this` for chaining
789
+ *
790
+ * @example Native Error
791
+ * ```typescript
792
+ * try {
793
+ * await riskyOperation();
794
+ * } catch (err) {
795
+ * builder.setError(err);
796
+ * }
797
+ * ```
798
+ *
799
+ * @example Custom Error with Context
800
+ * ```typescript
801
+ * builder.setError({
802
+ * type: 'PaymentError',
803
+ * message: 'Card declined by issuer',
804
+ * code: 'card_declined',
805
+ * stripe_decline_code: 'insufficient_funds',
806
+ * card_brand: 'visa',
807
+ * card_last4: '4242',
808
+ * });
809
+ * ```
810
+ */
811
+ setError(err: Error | {
812
+ type?: string;
813
+ message: string;
814
+ code?: string;
815
+ stack?: string;
816
+ [key: string]: unknown;
817
+ }): this;
818
+ /**
819
+ * Sets the HTTP status code and outcome.
820
+ *
821
+ * @remarks
822
+ * Automatically sets `outcome` based on the status code:
823
+ * - `< 400` → 'success'
824
+ * - `>= 400` → 'error'
825
+ *
826
+ * @param code - HTTP status code
827
+ * @returns `this` for chaining
828
+ *
829
+ * @example
830
+ * ```typescript
831
+ * builder.setStatus(404); // outcome = 'error'
832
+ * builder.setStatus(200); // outcome = 'success'
833
+ * ```
834
+ */
835
+ setStatus(code: number): this;
836
+ /**
837
+ * Records timing for a specific operation.
838
+ *
839
+ * @remarks
840
+ * A convenience method for calculating and setting duration values.
841
+ * The value is calculated as `Date.now() - startTime`.
842
+ *
843
+ * @param key - Property name for the timing (e.g., 'db_latency_ms')
844
+ * @param startTime - Start timestamp from `Date.now()`
845
+ * @returns `this` for chaining
846
+ *
847
+ * @example
848
+ * ```typescript
849
+ * const dbStart = Date.now();
850
+ * const result = await db.query('SELECT ...');
851
+ * builder.setTiming('db_latency_ms', dbStart);
852
+ *
853
+ * const paymentStart = Date.now();
854
+ * await processPayment();
855
+ * builder.setTiming('payment_latency_ms', paymentStart);
856
+ * ```
857
+ */
858
+ setTiming(key: string, startTime: number): this;
859
+ }
860
+
861
+ /**
862
+ * @packageDocumentation
863
+ *
864
+ * # FullEvent Node.js SDK
865
+ *
866
+ * The official Node.js SDK for FullEvent - the wide event analytics platform.
867
+ *
868
+ * ## Installation
869
+ *
870
+ * ```bash
871
+ * npm install @fullevent/node-sdk
872
+ * ```
873
+ *
874
+ * ## Quick Start
875
+ *
876
+ * ### Direct Event Ingestion
877
+ *
878
+ * ```typescript
879
+ * import { FullEvent } from '@fullevent/node-sdk';
880
+ *
881
+ * const client = new FullEvent({
882
+ * apiKey: process.env.FULLEVENT_API_KEY!,
883
+ * });
884
+ *
885
+ * await client.ingest('user.signup', { plan: 'pro' });
886
+ * ```
887
+ *
888
+ * ### Wide Event Middleware (Recommended)
889
+ *
890
+ * ```typescript
891
+ * import { Hono } from 'hono';
892
+ * import { wideLogger, WideEventVariables } from '@fullevent/node-sdk';
893
+ *
894
+ * const app = new Hono<{ Variables: WideEventVariables }>();
895
+ *
896
+ * app.use(wideLogger({
897
+ * apiKey: process.env.FULLEVENT_API_KEY!,
898
+ * service: 'my-api',
899
+ * }));
900
+ *
901
+ * app.post('/checkout', async (c) => {
902
+ * const event = c.get('wideEvent');
903
+ * event.user = { id: user.id, plan: user.plan };
904
+ * event.cart = { total_cents: cart.total, items: cart.items.length };
905
+ * return c.json({ success: true });
906
+ * });
907
+ * ```
908
+ *
909
+ * @module @fullevent/node-sdk
910
+ */
911
+
912
+ /**
913
+ * A Wide Event captures the complete context of a request in a single,
914
+ * self-describing record.
915
+ *
916
+ * @remarks
917
+ * Instead of emitting many small logs, you build one rich event throughout
918
+ * the request lifecycle and emit it at the end. This makes debugging and
919
+ * analytics significantly easier.
920
+ *
921
+ * ## The Wide Event Pattern
922
+ *
923
+ * 1. **Initialize**: Middleware creates the event with request context
924
+ * 2. **Enrich**: Handlers add business context throughout the request
925
+ * 3. **Emit**: Middleware sends the complete event when the request ends
926
+ *
927
+ * ## Auto-Captured Fields
928
+ *
929
+ * The middleware automatically captures:
930
+ * - `request_id`, `trace_id` - For distributed tracing
931
+ * - `timestamp` - When the request started
932
+ * - `method`, `path` - HTTP method and path
933
+ * - `status_code` - HTTP response status
934
+ * - `duration_ms` - Total request duration
935
+ * - `outcome` - 'success' or 'error'
936
+ * - `service`, `environment`, `region` - Infrastructure context
937
+ *
938
+ * ## Your Business Context
939
+ *
940
+ * Add any fields your application needs:
941
+ *
942
+ * ```typescript
943
+ * event.user = { id: 'usr_123', plan: 'premium', ltv_cents: 50000 };
944
+ * event.cart = { id: 'cart_xyz', items: 3, total_cents: 15999 };
945
+ * event.payment = { provider: 'stripe', method: 'card', latency_ms: 234 };
946
+ * event.feature_flags = { new_checkout: true };
947
+ * event.experiment = { name: 'pricing_v2', variant: 'control' };
948
+ * ```
949
+ *
950
+ * @example Basic Wide Event
951
+ * ```typescript
952
+ * const event: WideEvent = {
953
+ * // Auto-captured by middleware
954
+ * request_id: 'req_abc123',
955
+ * timestamp: '2024-01-15T10:23:45.612Z',
956
+ * method: 'POST',
957
+ * path: '/api/checkout',
958
+ * service: 'checkout-api',
959
+ * status_code: 200,
960
+ * duration_ms: 847,
961
+ * outcome: 'success',
962
+ *
963
+ * // Your business context
964
+ * user: { id: 'usr_456', plan: 'premium' },
965
+ * cart: { total_cents: 15999, items: 3 },
966
+ * payment: { provider: 'stripe', latency_ms: 234 },
967
+ * };
968
+ * ```
969
+ *
970
+ * @category Types
971
+ */
972
+ interface WideEvent {
973
+ /**
974
+ * Unique identifier for this request.
975
+ *
976
+ * @remarks
977
+ * Auto-generated or extracted from `x-request-id` / `x-fullevent-trace-id` headers.
978
+ */
979
+ request_id?: string;
980
+ /**
981
+ * Trace ID for distributed tracing.
982
+ *
983
+ * @remarks
984
+ * Propagated across service boundaries via the `x-fullevent-trace-id` header.
985
+ * Same as `request_id` by default.
986
+ *
987
+ * @see {@link https://fullevent.io/docs/distributed-tracing | Distributed Tracing Guide}
988
+ */
989
+ trace_id?: string;
990
+ /**
991
+ * ISO timestamp when the request started.
992
+ */
993
+ timestamp: string;
994
+ /**
995
+ * HTTP method (GET, POST, PUT, DELETE, etc.)
996
+ */
997
+ method: string;
998
+ /**
999
+ * Request path (e.g., `/api/checkout`)
1000
+ */
1001
+ path: string;
1002
+ /**
1003
+ * HTTP status code.
1004
+ *
1005
+ * @remarks
1006
+ * Auto-captured from the response. Used for error rate calculations.
1007
+ */
1008
+ status_code?: number;
1009
+ /**
1010
+ * Request duration in milliseconds.
1011
+ *
1012
+ * @remarks
1013
+ * Auto-captured by middleware in the `finally` block.
1014
+ */
1015
+ duration_ms?: number;
1016
+ /**
1017
+ * Request outcome: 'success' or 'error'.
1018
+ *
1019
+ * @remarks
1020
+ * Auto-set based on `status_code` (>= 400 = error).
1021
+ * Can be overridden manually.
1022
+ */
1023
+ outcome?: 'success' | 'error';
1024
+ /**
1025
+ * Service name (e.g., 'checkout-api', 'user-service').
1026
+ *
1027
+ * @remarks
1028
+ * Set via middleware config. Used for filtering and grouping.
1029
+ */
1030
+ service: string;
1031
+ /**
1032
+ * Deployment region (e.g., 'us-east-1', 'eu-west-1').
1033
+ */
1034
+ region?: string;
1035
+ /**
1036
+ * Environment (e.g., 'production', 'staging', 'development').
1037
+ *
1038
+ * @remarks
1039
+ * Defaults to `NODE_ENV` if not specified.
1040
+ */
1041
+ environment?: string;
1042
+ /**
1043
+ * User identifier.
1044
+ *
1045
+ * @remarks
1046
+ * The most commonly enriched field. For richer user context,
1047
+ * add a `user` object with additional properties.
1048
+ */
1049
+ user_id?: string;
1050
+ /**
1051
+ * Structured error information.
1052
+ *
1053
+ * @remarks
1054
+ * Auto-populated when an exception is thrown. Can also be set manually
1055
+ * for handled errors (e.g., payment failures).
1056
+ *
1057
+ * @example
1058
+ * ```typescript
1059
+ * event.error = {
1060
+ * type: 'PaymentError',
1061
+ * code: 'card_declined',
1062
+ * message: 'Your card was declined',
1063
+ * stripe_decline_code: 'insufficient_funds',
1064
+ * };
1065
+ * ```
1066
+ */
1067
+ error?: {
1068
+ /** Error class/type name (e.g., 'TypeError', 'PaymentError') */
1069
+ type: string;
1070
+ /** Human-readable error message */
1071
+ message: string;
1072
+ /** Stack trace (auto-captured for thrown errors) */
1073
+ stack?: string;
1074
+ /** Error code (e.g., 'ECONNREFUSED', 'card_declined') */
1075
+ code?: string;
1076
+ /** Additional error context */
1077
+ [key: string]: unknown;
1078
+ };
1079
+ /**
1080
+ * Dynamic business context.
1081
+ *
1082
+ * @remarks
1083
+ * Add any fields your application needs. Common patterns:
1084
+ *
1085
+ * - `user` - User context (id, plan, ltv, etc.)
1086
+ * - `cart` - Shopping cart (id, items, total, coupon)
1087
+ * - `payment` - Payment details (provider, method, latency)
1088
+ * - `order` - Order details (id, status, items)
1089
+ * - `feature_flags` - Feature flag states
1090
+ * - `experiment` - A/B test assignments
1091
+ * - `llm` - LLM usage (model, tokens, latency)
1092
+ */
1093
+ [key: string]: unknown;
1094
+ }
1095
+
1096
+ export { FullEvent, type FullEventConfig, type HttpRequestProperties, type SamplingConfig, type WideEvent, WideEventBuilder, type WideEventVariables, type WideLoggerConfig, expressWideLogger, wideLogger };