evlog 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/README.md +96 -0
  2. package/dist/_http-DVDwNag0.mjs +76 -0
  3. package/dist/_http-DVDwNag0.mjs.map +1 -0
  4. package/dist/_severity-CXfyvxQi.mjs +17 -0
  5. package/dist/_severity-CXfyvxQi.mjs.map +1 -0
  6. package/dist/adapters/axiom.d.mts +17 -15
  7. package/dist/adapters/axiom.d.mts.map +1 -0
  8. package/dist/adapters/axiom.mjs +91 -50
  9. package/dist/adapters/axiom.mjs.map +1 -0
  10. package/dist/adapters/better-stack.d.mts +63 -0
  11. package/dist/adapters/better-stack.d.mts.map +1 -0
  12. package/dist/adapters/better-stack.mjs +98 -0
  13. package/dist/adapters/better-stack.mjs.map +1 -0
  14. package/dist/adapters/otlp.d.mts +32 -30
  15. package/dist/adapters/otlp.d.mts.map +1 -0
  16. package/dist/adapters/otlp.mjs +181 -181
  17. package/dist/adapters/otlp.mjs.map +1 -0
  18. package/dist/adapters/posthog.d.mts +54 -19
  19. package/dist/adapters/posthog.d.mts.map +1 -0
  20. package/dist/adapters/posthog.mjs +156 -63
  21. package/dist/adapters/posthog.mjs.map +1 -0
  22. package/dist/adapters/sentry.d.mts +25 -23
  23. package/dist/adapters/sentry.d.mts.map +1 -0
  24. package/dist/adapters/sentry.mjs +198 -153
  25. package/dist/adapters/sentry.mjs.map +1 -0
  26. package/dist/browser.d.mts +63 -0
  27. package/dist/browser.d.mts.map +1 -0
  28. package/dist/browser.mjs +95 -0
  29. package/dist/browser.mjs.map +1 -0
  30. package/dist/enrichers.d.mts +74 -0
  31. package/dist/enrichers.d.mts.map +1 -0
  32. package/dist/enrichers.mjs +172 -0
  33. package/dist/enrichers.mjs.map +1 -0
  34. package/dist/error.d.mts +24 -22
  35. package/dist/error.d.mts.map +1 -0
  36. package/dist/error.mjs +107 -76
  37. package/dist/error.mjs.map +1 -0
  38. package/dist/index.d.mts +6 -5
  39. package/dist/index.mjs +6 -5
  40. package/dist/logger.d.mts +11 -5
  41. package/dist/logger.d.mts.map +1 -0
  42. package/dist/logger.mjs +255 -186
  43. package/dist/logger.mjs.map +1 -0
  44. package/dist/nitro/errorHandler.d.mts +4 -2
  45. package/dist/nitro/errorHandler.d.mts.map +1 -0
  46. package/dist/nitro/errorHandler.mjs +38 -38
  47. package/dist/nitro/errorHandler.mjs.map +1 -0
  48. package/dist/nitro/module.d.mts +11 -0
  49. package/dist/nitro/module.d.mts.map +1 -0
  50. package/dist/nitro/module.mjs +23 -0
  51. package/dist/nitro/module.mjs.map +1 -0
  52. package/dist/nitro/plugin.d.mts +4 -2
  53. package/dist/nitro/plugin.d.mts.map +1 -0
  54. package/dist/nitro/plugin.mjs +135 -140
  55. package/dist/nitro/plugin.mjs.map +1 -0
  56. package/dist/nitro/v3/errorHandler.d.mts +24 -0
  57. package/dist/nitro/v3/errorHandler.d.mts.map +1 -0
  58. package/dist/nitro/v3/errorHandler.mjs +36 -0
  59. package/dist/nitro/v3/errorHandler.mjs.map +1 -0
  60. package/dist/nitro/v3/index.d.mts +4 -0
  61. package/dist/nitro/v3/index.mjs +4 -0
  62. package/dist/nitro/v3/module.d.mts +10 -0
  63. package/dist/nitro/v3/module.d.mts.map +1 -0
  64. package/dist/nitro/v3/module.mjs +22 -0
  65. package/dist/nitro/v3/module.mjs.map +1 -0
  66. package/dist/nitro/v3/plugin.d.mts +14 -0
  67. package/dist/nitro/v3/plugin.d.mts.map +1 -0
  68. package/dist/nitro/v3/plugin.mjs +157 -0
  69. package/dist/nitro/v3/plugin.mjs.map +1 -0
  70. package/dist/nitro/v3/useLogger.d.mts +24 -0
  71. package/dist/nitro/v3/useLogger.d.mts.map +1 -0
  72. package/dist/nitro/v3/useLogger.mjs +27 -0
  73. package/dist/nitro/v3/useLogger.mjs.map +1 -0
  74. package/dist/nitro-D57TWGyN.mjs +73 -0
  75. package/dist/nitro-D57TWGyN.mjs.map +1 -0
  76. package/dist/nitro-D81NBVPi.d.mts +42 -0
  77. package/dist/nitro-D81NBVPi.d.mts.map +1 -0
  78. package/dist/nuxt/module.d.mts +155 -168
  79. package/dist/nuxt/module.d.mts.map +1 -0
  80. package/dist/nuxt/module.mjs +75 -65
  81. package/dist/nuxt/module.mjs.map +1 -0
  82. package/dist/pipeline.d.mts +46 -0
  83. package/dist/pipeline.d.mts.map +1 -0
  84. package/dist/pipeline.mjs +122 -0
  85. package/dist/pipeline.mjs.map +1 -0
  86. package/dist/runtime/client/log.d.mts +12 -7
  87. package/dist/runtime/client/log.d.mts.map +1 -0
  88. package/dist/runtime/client/log.mjs +72 -64
  89. package/dist/runtime/client/log.mjs.map +1 -0
  90. package/dist/runtime/client/plugin.d.mts +3 -1
  91. package/dist/runtime/client/plugin.d.mts.map +1 -0
  92. package/dist/runtime/client/plugin.mjs +14 -12
  93. package/dist/runtime/client/plugin.mjs.map +1 -0
  94. package/dist/runtime/server/routes/_evlog/ingest.post.d.mts +4 -2
  95. package/dist/runtime/server/routes/_evlog/ingest.post.d.mts.map +1 -0
  96. package/dist/runtime/server/routes/_evlog/ingest.post.mjs +113 -76
  97. package/dist/runtime/server/routes/_evlog/ingest.post.mjs.map +1 -0
  98. package/dist/runtime/server/useLogger.d.mts +14 -3
  99. package/dist/runtime/server/useLogger.d.mts.map +1 -0
  100. package/dist/runtime/server/useLogger.mjs +39 -10
  101. package/dist/runtime/server/useLogger.mjs.map +1 -0
  102. package/dist/runtime/utils/parseError.d.mts +5 -3
  103. package/dist/runtime/utils/parseError.d.mts.map +1 -0
  104. package/dist/runtime/utils/parseError.mjs +25 -26
  105. package/dist/runtime/utils/parseError.mjs.map +1 -0
  106. package/dist/types.d.mts +378 -246
  107. package/dist/types.d.mts.map +1 -0
  108. package/dist/types.mjs +1 -1
  109. package/dist/utils.d.mts +19 -14
  110. package/dist/utils.d.mts.map +1 -0
  111. package/dist/utils.mjs +59 -50
  112. package/dist/utils.mjs.map +1 -0
  113. package/dist/workers.d.mts +10 -9
  114. package/dist/workers.d.mts.map +1 -0
  115. package/dist/workers.mjs +68 -39
  116. package/dist/workers.mjs.map +1 -0
  117. package/package.json +55 -10
  118. package/dist/adapters/axiom.d.ts +0 -62
  119. package/dist/adapters/otlp.d.ts +0 -83
  120. package/dist/adapters/posthog.d.ts +0 -72
  121. package/dist/adapters/sentry.d.ts +0 -78
  122. package/dist/error.d.ts +0 -63
  123. package/dist/index.d.ts +0 -5
  124. package/dist/logger.d.ts +0 -40
  125. package/dist/nitro/errorHandler.d.ts +0 -13
  126. package/dist/nitro/plugin.d.ts +0 -5
  127. package/dist/nuxt/module.d.ts +0 -171
  128. package/dist/runtime/client/log.d.ts +0 -10
  129. package/dist/runtime/client/plugin.d.ts +0 -3
  130. package/dist/runtime/server/routes/_evlog/ingest.post.d.ts +0 -5
  131. package/dist/runtime/server/useLogger.d.ts +0 -28
  132. package/dist/runtime/utils/parseError.d.ts +0 -5
  133. package/dist/shared/evlog.Bc35pxiY.mjs +0 -10
  134. package/dist/types.d.ts +0 -364
  135. package/dist/utils.d.ts +0 -29
  136. package/dist/workers.d.ts +0 -45
package/dist/types.d.mts CHANGED
@@ -1,220 +1,328 @@
1
+ //#region src/types.d.ts
1
2
  declare module 'nitropack/types' {
2
- interface NitroRuntimeHooks {
3
- /**
4
- * Tail sampling hook - called before emitting a log.
5
- * Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
6
- *
7
- * @example
8
- * ```ts
9
- * nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
10
- * if (ctx.context.user?.premium) {
11
- * ctx.shouldKeep = true
12
- * }
13
- * })
14
- * ```
15
- */
16
- 'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>;
17
- /**
18
- * Drain hook - called after emitting a log (fire-and-forget).
19
- * Use this to send logs to external services like Axiom, Loki, or custom endpoints.
20
- * Errors are logged but never block the request.
21
- *
22
- * @example
23
- * ```ts
24
- * nitroApp.hooks.hook('evlog:drain', async (ctx) => {
25
- * await fetch('https://api.axiom.co/v1/datasets/logs/ingest', {
26
- * method: 'POST',
27
- * headers: { Authorization: `Bearer ${process.env.AXIOM_TOKEN}` },
28
- * body: JSON.stringify([ctx.event])
29
- * })
30
- * })
31
- * ```
32
- */
33
- 'evlog:drain': (ctx: DrainContext) => void | Promise<void>;
34
- }
3
+ interface NitroRuntimeHooks {
4
+ /**
5
+ * Tail sampling hook - called before emitting a log.
6
+ * Set `ctx.shouldKeep = true` to force-keep the log regardless of head sampling.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
11
+ * if (ctx.context.user?.premium) {
12
+ * ctx.shouldKeep = true
13
+ * }
14
+ * })
15
+ * ```
16
+ */
17
+ 'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>;
18
+ /**
19
+ * Enrichment hook - called after emit, before drain.
20
+ * Use this to enrich the event with derived context (e.g. geo, user agent).
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * nitroApp.hooks.hook('evlog:enrich', (ctx) => {
25
+ * ctx.event.deploymentId = process.env.DEPLOYMENT_ID
26
+ * })
27
+ * ```
28
+ */
29
+ 'evlog:enrich': (ctx: EnrichContext) => void | Promise<void>;
30
+ /**
31
+ * Drain hook - called after emitting a log (fire-and-forget).
32
+ * Use this to send logs to external services like Axiom, Loki, or custom endpoints.
33
+ * Errors are logged but never block the request.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * nitroApp.hooks.hook('evlog:drain', async (ctx) => {
38
+ * await fetch('https://api.axiom.co/v1/datasets/logs/ingest', {
39
+ * method: 'POST',
40
+ * headers: { Authorization: `Bearer ${process.env.AXIOM_TOKEN}` },
41
+ * body: JSON.stringify([ctx.event])
42
+ * })
43
+ * })
44
+ * ```
45
+ */
46
+ 'evlog:drain': (ctx: DrainContext) => void | Promise<void>;
47
+ }
48
+ }
49
+ declare module 'nitro/types' {
50
+ interface NitroRuntimeHooks {
51
+ 'evlog:emit:keep': (ctx: TailSamplingContext) => void | Promise<void>;
52
+ 'evlog:enrich': (ctx: EnrichContext) => void | Promise<void>;
53
+ 'evlog:drain': (ctx: DrainContext) => void | Promise<void>;
54
+ }
35
55
  }
36
56
  /**
37
57
  * Transport configuration for sending client logs to the server
38
58
  */
39
59
  interface TransportConfig {
40
- /**
41
- * Enable sending logs to the server API
42
- * @default false
43
- */
44
- enabled?: boolean;
45
- /**
46
- * API endpoint for log ingestion
47
- * @default '/api/_evlog/ingest'
48
- */
49
- endpoint?: string;
60
+ /**
61
+ * Enable sending logs to the server API
62
+ * @default false
63
+ */
64
+ enabled?: boolean;
65
+ /**
66
+ * API endpoint for log ingestion
67
+ * @default '/api/_evlog/ingest'
68
+ */
69
+ endpoint?: string;
50
70
  }
51
71
  /**
52
72
  * Payload sent from client to server for log ingestion
53
73
  */
54
74
  interface IngestPayload {
55
- timestamp: string;
56
- level: 'info' | 'error' | 'warn' | 'debug';
57
- [key: string]: unknown;
75
+ timestamp: string;
76
+ level: 'info' | 'error' | 'warn' | 'debug';
77
+ [key: string]: unknown;
58
78
  }
59
79
  /**
60
80
  * Sampling rates per log level (0-100 percentage)
61
81
  */
62
82
  interface SamplingRates {
63
- /** Percentage of info logs to keep (0-100). Default: 100 */
64
- info?: number;
65
- /** Percentage of warn logs to keep (0-100). Default: 100 */
66
- warn?: number;
67
- /** Percentage of debug logs to keep (0-100). Default: 100 */
68
- debug?: number;
69
- /** Percentage of error logs to keep (0-100). Default: 100 */
70
- error?: number;
83
+ /** Percentage of info logs to keep (0-100). Default: 100 */
84
+ info?: number;
85
+ /** Percentage of warn logs to keep (0-100). Default: 100 */
86
+ warn?: number;
87
+ /** Percentage of debug logs to keep (0-100). Default: 100 */
88
+ debug?: number;
89
+ /** Percentage of error logs to keep (0-100). Default: 100 */
90
+ error?: number;
71
91
  }
72
92
  /**
73
93
  * Tail sampling condition for forcing log retention based on request outcome.
74
94
  * All conditions use >= comparison (e.g., status: 400 means status >= 400).
75
95
  */
76
96
  interface TailSamplingCondition {
77
- /** Keep if HTTP status >= this value (e.g., 400 for all errors) */
78
- status?: number;
79
- /** Keep if request duration >= this value in milliseconds */
80
- duration?: number;
81
- /** Keep if path matches this glob pattern (e.g., '/api/critical/**') */
82
- path?: string;
97
+ /** Keep if HTTP status >= this value (e.g., 400 for all errors) */
98
+ status?: number;
99
+ /** Keep if request duration >= this value in milliseconds */
100
+ duration?: number;
101
+ /** Keep if path matches this glob pattern (e.g., '/api/critical/**') */
102
+ path?: string;
83
103
  }
84
104
  /**
85
105
  * Context passed to tail sampling evaluation and hooks.
86
106
  * Contains request outcome information for sampling decisions.
87
107
  */
88
108
  interface TailSamplingContext {
89
- /** HTTP response status code */
90
- status?: number;
91
- /** Request duration in milliseconds (raw number) */
92
- duration?: number;
93
- /** Request path */
94
- path?: string;
95
- /** HTTP method */
109
+ /** HTTP response status code */
110
+ status?: number;
111
+ /** Request duration in milliseconds (raw number) */
112
+ duration?: number;
113
+ /** Request path */
114
+ path?: string;
115
+ /** HTTP method */
116
+ method?: string;
117
+ /** Full accumulated context from the request logger */
118
+ context: Record<string, unknown>;
119
+ /**
120
+ * Set to true in evlog:emit:keep hook to force keep this log.
121
+ * Multiple hooks can set this - if any sets it to true, the log is kept.
122
+ */
123
+ shouldKeep?: boolean;
124
+ }
125
+ /**
126
+ * Context passed to the evlog:enrich hook.
127
+ * Called after emit, before drain.
128
+ */
129
+ interface EnrichContext {
130
+ /** The emitted wide event (mutable). */
131
+ event: WideEvent;
132
+ /** Request metadata (if available) */
133
+ request?: {
96
134
  method?: string;
97
- /** Full accumulated context from the request logger */
98
- context: Record<string, unknown>;
99
- /**
100
- * Set to true in evlog:emit:keep hook to force keep this log.
101
- * Multiple hooks can set this - if any sets it to true, the log is kept.
102
- */
103
- shouldKeep?: boolean;
135
+ path: string;
136
+ requestId?: string;
137
+ };
138
+ /** Safe HTTP request headers (sensitive headers filtered out) */
139
+ headers?: Record<string, string>;
140
+ /** Optional response metadata */
141
+ response?: {
142
+ status?: number;
143
+ headers?: Record<string, string>;
144
+ };
104
145
  }
105
146
  /**
106
147
  * Context passed to the evlog:drain hook.
107
148
  * Contains the complete wide event and request metadata for external transport.
108
149
  */
109
150
  interface DrainContext {
110
- /** The complete wide event to drain */
111
- event: WideEvent;
112
- /** Request metadata (if available) */
113
- request?: {
114
- method?: string;
115
- path?: string;
116
- requestId?: string;
117
- };
118
- /** HTTP headers from the original request (useful for correlation with external services) */
119
- headers?: Record<string, string>;
151
+ /** The complete wide event to drain */
152
+ event: WideEvent;
153
+ /** Request metadata (if available) */
154
+ request?: {
155
+ method?: string;
156
+ path?: string;
157
+ requestId?: string;
158
+ };
159
+ /** HTTP headers from the original request (useful for correlation with external services) */
160
+ headers?: Record<string, string>;
120
161
  }
121
162
  /**
122
163
  * Sampling configuration for filtering logs
123
164
  */
124
165
  interface SamplingConfig {
125
- /**
126
- * Sampling rates per log level (head sampling).
127
- * Values are percentages from 0 to 100.
128
- * Default: 100 for all levels (log everything).
129
- * Error defaults to 100 even if not specified.
130
- *
131
- * @example
132
- * ```ts
133
- * sampling: {
134
- * rates: {
135
- * info: 10, // Keep 10% of info logs
136
- * warn: 50, // Keep 50% of warning logs
137
- * debug: 5, // Keep 5% of debug logs
138
- * error: 100, // Always keep errors (default)
139
- * }
140
- * }
141
- * ```
142
- */
143
- rates?: SamplingRates;
144
- /**
145
- * Tail sampling conditions for forcing log retention (OR logic).
146
- * If ANY condition matches, the log is kept regardless of head sampling.
147
- * Use the `evlog:emit:keep` Nitro hook for custom conditions.
148
- *
149
- * @example
150
- * ```ts
151
- * sampling: {
152
- * rates: { info: 10 }, // Head sampling: keep 10% of info logs
153
- * keep: [
154
- * { status: 400 }, // Always keep if status >= 400
155
- * { duration: 1000 }, // Always keep if duration >= 1000ms
156
- * { path: '/api/critical/**' }, // Always keep critical paths
157
- * ]
158
- * }
159
- * ```
160
- */
161
- keep?: TailSamplingCondition[];
166
+ /**
167
+ * Sampling rates per log level (head sampling).
168
+ * Values are percentages from 0 to 100.
169
+ * Default: 100 for all levels (log everything).
170
+ * Error defaults to 100 even if not specified.
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * sampling: {
175
+ * rates: {
176
+ * info: 10, // Keep 10% of info logs
177
+ * warn: 50, // Keep 50% of warning logs
178
+ * debug: 5, // Keep 5% of debug logs
179
+ * error: 100, // Always keep errors (default)
180
+ * }
181
+ * }
182
+ * ```
183
+ */
184
+ rates?: SamplingRates;
185
+ /**
186
+ * Tail sampling conditions for forcing log retention (OR logic).
187
+ * If ANY condition matches, the log is kept regardless of head sampling.
188
+ * Use the `evlog:emit:keep` Nitro hook for custom conditions.
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * sampling: {
193
+ * rates: { info: 10 }, // Head sampling: keep 10% of info logs
194
+ * keep: [
195
+ * { status: 400 }, // Always keep if status >= 400
196
+ * { duration: 1000 }, // Always keep if duration >= 1000ms
197
+ * { path: '/api/critical/**' }, // Always keep critical paths
198
+ * ]
199
+ * }
200
+ * ```
201
+ */
202
+ keep?: TailSamplingCondition[];
162
203
  }
163
204
  /**
164
205
  * Route-based service configuration
165
206
  */
166
207
  interface RouteConfig {
167
- /** Service name to use for routes matching this pattern */
168
- service: string;
208
+ /** Service name to use for routes matching this pattern */
209
+ service: string;
169
210
  }
170
211
  /**
171
212
  * Environment context automatically included in every log event
172
213
  */
173
214
  interface EnvironmentContext {
174
- /** Service name (auto-detected from package.json or configurable) */
175
- service: string;
176
- /** Environment: 'development', 'production', 'test', etc. */
177
- environment: 'development' | 'production' | 'test' | string;
178
- /** Application version (auto-detected from package.json) */
179
- version?: string;
180
- /** Git commit hash (auto-detected from CI/CD env vars) */
181
- commitHash?: string;
182
- /** Deployment region (auto-detected from cloud provider env vars) */
183
- region?: string;
215
+ /** Service name (auto-detected from package.json or configurable) */
216
+ service: string;
217
+ /** Environment: 'development', 'production', 'test', etc. */
218
+ environment: 'development' | 'production' | 'test' | string;
219
+ /** Application version (auto-detected from package.json) */
220
+ version?: string;
221
+ /** Git commit hash (auto-detected from CI/CD env vars) */
222
+ commitHash?: string;
223
+ /** Deployment region (auto-detected from cloud provider env vars) */
224
+ region?: string;
184
225
  }
185
226
  /**
186
227
  * Logger configuration options
187
228
  */
188
229
  interface LoggerConfig {
189
- /** Environment context overrides */
190
- env?: Partial<EnvironmentContext>;
191
- /** Enable pretty printing (auto-detected: true in dev, false in prod) */
192
- pretty?: boolean;
193
- /** Sampling configuration for filtering logs */
194
- sampling?: SamplingConfig;
195
- /**
196
- * When pretty is disabled, emit JSON strings (default) or raw objects.
197
- * Set to false for environments like Cloudflare Workers that expect objects.
198
- * @default true
199
- */
200
- stringify?: boolean;
230
+ /**
231
+ * Enable or disable all logging globally.
232
+ * When false, all emits, tagged logs, and request logger operations become no-ops.
233
+ * @default true
234
+ */
235
+ enabled?: boolean;
236
+ /** Environment context overrides */
237
+ env?: Partial<EnvironmentContext>;
238
+ /** Enable pretty printing (auto-detected: true in dev, false in prod) */
239
+ pretty?: boolean;
240
+ /** Sampling configuration for filtering logs */
241
+ sampling?: SamplingConfig;
242
+ /**
243
+ * When pretty is disabled, emit JSON strings (default) or raw objects.
244
+ * Set to false for environments like Cloudflare Workers that expect objects.
245
+ * @default true
246
+ */
247
+ stringify?: boolean;
248
+ /**
249
+ * Drain callback called with every emitted event (fire-and-forget).
250
+ * Use this to send logs to external services outside of Nitro.
251
+ * Compatible with drain adapters (`createAxiomDrain()`) and pipeline-wrapped drains.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * import { initLogger, log } from 'evlog'
256
+ * import { createAxiomDrain } from 'evlog/axiom'
257
+ *
258
+ * initLogger({
259
+ * drain: createAxiomDrain({ dataset: 'logs', token: '...' }),
260
+ * })
261
+ *
262
+ * log.info({ action: 'user_login' }) // automatically drained
263
+ * ```
264
+ *
265
+ * @example
266
+ * ```ts
267
+ * // With pipeline for batching and retry
268
+ * import { createDrainPipeline } from 'evlog/pipeline'
269
+ *
270
+ * const pipeline = createDrainPipeline({ batch: { size: 25 } })
271
+ * const drain = pipeline(createAxiomDrain({ dataset: 'logs', token: '...' }))
272
+ *
273
+ * initLogger({ drain })
274
+ *
275
+ * // Flush on shutdown
276
+ * process.on('beforeExit', () => drain.flush())
277
+ * ```
278
+ */
279
+ drain?: (ctx: DrainContext) => void | Promise<void>;
201
280
  }
202
281
  /**
203
282
  * Base structure for all wide events
204
283
  */
205
284
  interface BaseWideEvent {
206
- timestamp: string;
207
- level: 'info' | 'error' | 'warn' | 'debug';
208
- service: string;
209
- environment: string;
210
- version?: string;
211
- commitHash?: string;
212
- region?: string;
285
+ timestamp: string;
286
+ level: 'info' | 'error' | 'warn' | 'debug';
287
+ service: string;
288
+ environment: string;
289
+ version?: string;
290
+ commitHash?: string;
291
+ region?: string;
213
292
  }
214
293
  /**
215
294
  * Wide event with arbitrary additional fields
216
295
  */
217
296
  type WideEvent = BaseWideEvent & Record<string, unknown>;
297
+ /**
298
+ * Recursively makes all properties optional.
299
+ * Arrays are kept as-is (not deeply partial).
300
+ */
301
+ type DeepPartial<T> = T extends Array<unknown> ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
302
+ /**
303
+ * Fields set internally by the evlog plugin (status, service, etc.).
304
+ * These are always accepted by `set()` regardless of the user-defined field type.
305
+ */
306
+ interface InternalFields {
307
+ status?: number;
308
+ service?: string;
309
+ requestLogs?: RequestLogEntry[];
310
+ }
311
+ /**
312
+ * Request-scoped log entry captured during a request lifecycle.
313
+ */
314
+ interface RequestLogEntry {
315
+ level: 'info' | 'warn';
316
+ message: string;
317
+ timestamp: string;
318
+ }
319
+ /**
320
+ * Resolved context type for logger methods.
321
+ * User fields are deeply partial (matching deep merge behavior) with internal
322
+ * field keys omitted to avoid intersection conflicts, then internal fields
323
+ * are added back with their canonical types.
324
+ */
325
+ type FieldContext<T extends object = Record<string, unknown>> = DeepPartial<Omit<T, keyof InternalFields>> & InternalFields;
218
326
  /**
219
327
  * Request-scoped logger for building wide events
220
328
  *
@@ -225,25 +333,50 @@ type WideEvent = BaseWideEvent & Record<string, unknown>;
225
333
  * logger.set({ cart: { items: 3 } })
226
334
  * // emit() is called automatically by the plugin
227
335
  * ```
336
+ *
337
+ * @example
338
+ * ```ts
339
+ * // With typed fields for compile-time safety
340
+ * interface MyFields {
341
+ * user: { id: string; plan: string }
342
+ * action: string
343
+ * }
344
+ * const logger = useLogger<MyFields>(event)
345
+ * logger.set({ user: { id: '123', plan: 'pro' } }) // OK
346
+ * logger.set({ user: { id: '123' } }) // OK (deep partial)
347
+ * logger.set({ action: 'checkout' }) // OK
348
+ * logger.set({ status: 200 }) // OK (internal field)
349
+ * logger.set({ account: '...' }) // TS error
350
+ * ```
228
351
  */
229
- interface RequestLogger {
230
- /**
231
- * Add context to the wide event (deep merge via defu)
232
- */
233
- set: <T extends Record<string, unknown>>(context: T) => void;
234
- /**
235
- * Log an error and capture its details
236
- */
237
- error: (error: Error | string, context?: Record<string, unknown>) => void;
238
- /**
239
- * Emit the final wide event with all accumulated context.
240
- * Returns the emitted WideEvent, or null if the log was sampled out.
241
- */
242
- emit: (overrides?: Record<string, unknown>) => WideEvent | null;
243
- /**
244
- * Get the current accumulated context
245
- */
246
- getContext: () => Record<string, unknown>;
352
+ interface RequestLogger<T extends object = Record<string, unknown>> {
353
+ /**
354
+ * Add context to the wide event (deep merge via defu)
355
+ */
356
+ set: (context: FieldContext<T>) => void;
357
+ /**
358
+ * Log an error and capture its details
359
+ */
360
+ error: (error: Error | string, context?: FieldContext<T>) => void;
361
+ /**
362
+ * Capture an informational message inside the request wide event.
363
+ */
364
+ info: (message: string, context?: FieldContext<T>) => void;
365
+ /**
366
+ * Capture a warning message inside the request wide event.
367
+ */
368
+ warn: (message: string, context?: FieldContext<T>) => void;
369
+ /**
370
+ * Emit the final wide event with all accumulated context.
371
+ * Returns the emitted WideEvent, or null if the log was sampled out.
372
+ */
373
+ emit: (overrides?: FieldContext<T> & {
374
+ _forceKeep?: boolean;
375
+ }) => WideEvent | null;
376
+ /**
377
+ * Get the current accumulated context
378
+ */
379
+ getContext: () => FieldContext<T> & Record<string, unknown>;
247
380
  }
248
381
  /**
249
382
  * Log level type
@@ -259,106 +392,105 @@ type LogLevel = 'info' | 'error' | 'warn' | 'debug';
259
392
  * ```
260
393
  */
261
394
  interface Log {
262
- /**
263
- * Log an info message or wide event
264
- * @example log.info('auth', 'User logged in')
265
- * @example log.info({ action: 'login', userId: '123' })
266
- */
267
- info(tag: string, message: string): void;
268
- info(event: Record<string, unknown>): void;
269
- /**
270
- * Log an error message or wide event
271
- * @example log.error('payment', 'Payment failed')
272
- * @example log.error({ action: 'payment', error: 'declined' })
273
- */
274
- error(tag: string, message: string): void;
275
- error(event: Record<string, unknown>): void;
276
- /**
277
- * Log a warning message or wide event
278
- * @example log.warn('api', 'Rate limit approaching')
279
- * @example log.warn({ action: 'api', remaining: 10 })
280
- */
281
- warn(tag: string, message: string): void;
282
- warn(event: Record<string, unknown>): void;
283
- /**
284
- * Log a debug message or wide event
285
- * @example log.debug('cache', 'Cache miss')
286
- * @example log.debug({ action: 'cache', key: 'user_123' })
287
- */
288
- debug(tag: string, message: string): void;
289
- debug(event: Record<string, unknown>): void;
395
+ /**
396
+ * Log an info message or wide event
397
+ * @example log.info('auth', 'User logged in')
398
+ * @example log.info({ action: 'login', userId: '123' })
399
+ */
400
+ info(tag: string, message: string): void;
401
+ info(event: Record<string, unknown>): void;
402
+ /**
403
+ * Log an error message or wide event
404
+ * @example log.error('payment', 'Payment failed')
405
+ * @example log.error({ action: 'payment', error: 'declined' })
406
+ */
407
+ error(tag: string, message: string): void;
408
+ error(event: Record<string, unknown>): void;
409
+ /**
410
+ * Log a warning message or wide event
411
+ * @example log.warn('api', 'Rate limit approaching')
412
+ * @example log.warn({ action: 'api', remaining: 10 })
413
+ */
414
+ warn(tag: string, message: string): void;
415
+ warn(event: Record<string, unknown>): void;
416
+ /**
417
+ * Log a debug message or wide event
418
+ * @example log.debug('cache', 'Cache miss')
419
+ * @example log.debug({ action: 'cache', key: 'user_123' })
420
+ */
421
+ debug(tag: string, message: string): void;
422
+ debug(event: Record<string, unknown>): void;
290
423
  }
291
424
  /**
292
425
  * Error options for creating structured errors
293
426
  */
294
427
  interface ErrorOptions {
295
- /** What actually happened */
296
- message: string;
297
- /** HTTP status code (default: 500) */
298
- status?: number;
299
- /** Why this error occurred */
300
- why?: string;
301
- /** How to fix this issue */
302
- fix?: string;
303
- /** Link to documentation or more information */
304
- link?: string;
305
- /** The original error that caused this */
306
- cause?: Error;
428
+ /** What actually happened */
429
+ message: string;
430
+ /** HTTP status code (default: 500) */
431
+ status?: number;
432
+ /** Why this error occurred */
433
+ why?: string;
434
+ /** How to fix this issue */
435
+ fix?: string;
436
+ /** Link to documentation or more information */
437
+ link?: string;
438
+ /** The original error that caused this */
439
+ cause?: Error;
307
440
  }
308
441
  /**
309
442
  * Options for creating a request logger
310
443
  */
311
444
  interface RequestLoggerOptions {
312
- method?: string;
313
- path?: string;
314
- requestId?: string;
445
+ method?: string;
446
+ path?: string;
447
+ requestId?: string;
315
448
  }
316
449
  /**
317
450
  * H3 event context with evlog logger attached
318
451
  */
319
452
  interface H3EventContext {
320
- log?: RequestLogger;
321
- requestId?: string;
322
- status?: number;
323
- /** Internal: start time for duration calculation in tail sampling */
324
- _evlogStartTime?: number;
325
- /** Internal: flag to prevent double emission on errors */
326
- _evlogEmitted?: boolean;
327
- [key: string]: unknown;
453
+ log?: RequestLogger;
454
+ requestId?: string;
455
+ status?: number;
456
+ /** Internal: start time for duration calculation in tail sampling */
457
+ _evlogStartTime?: number;
458
+ /** Internal: flag to prevent double emission on errors */
459
+ _evlogEmitted?: boolean;
460
+ [key: string]: unknown;
328
461
  }
329
462
  /**
330
463
  * Server event type for Nitro/h3 handlers
331
464
  */
332
465
  interface ServerEvent {
333
- method: string;
334
- path: string;
335
- context: H3EventContext & {
336
- /** Cloudflare Workers context (available when deployed to CF Workers) */
337
- cloudflare?: {
338
- context: {
339
- waitUntil: (promise: Promise<unknown>) => void;
340
- };
341
- };
342
- /** Vercel Edge context (available when deployed to Vercel Edge) */
343
- waitUntil?: (promise: Promise<unknown>) => void;
344
- };
345
- node?: {
346
- res?: {
347
- statusCode?: number;
348
- };
466
+ method: string;
467
+ path: string;
468
+ context: H3EventContext & {
469
+ /** Cloudflare Workers context (available when deployed to CF Workers) */cloudflare?: {
470
+ context: {
471
+ waitUntil: (promise: Promise<unknown>) => void;
472
+ };
473
+ }; /** Vercel Edge context (available when deployed to Vercel Edge) */
474
+ waitUntil?: (promise: Promise<unknown>) => void;
475
+ };
476
+ node?: {
477
+ res?: {
478
+ statusCode?: number;
349
479
  };
350
- response?: Response;
480
+ };
481
+ response?: Response;
351
482
  }
352
483
  /**
353
484
  * Parsed evlog error with all fields at the top level
354
485
  */
355
486
  interface ParsedError {
356
- message: string;
357
- status: number;
358
- why?: string;
359
- fix?: string;
360
- link?: string;
361
- raw: unknown;
487
+ message: string;
488
+ status: number;
489
+ why?: string;
490
+ fix?: string;
491
+ link?: string;
492
+ raw: unknown;
362
493
  }
363
-
364
- export type { BaseWideEvent, DrainContext, EnvironmentContext, ErrorOptions, H3EventContext, IngestPayload, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, RouteConfig, SamplingConfig, SamplingRates, ServerEvent, TailSamplingCondition, TailSamplingContext, TransportConfig, WideEvent };
494
+ //#endregion
495
+ export { BaseWideEvent, DeepPartial, DrainContext, EnrichContext, EnvironmentContext, ErrorOptions, FieldContext, H3EventContext, IngestPayload, InternalFields, Log, LogLevel, LoggerConfig, ParsedError, RequestLogEntry, RequestLogger, RequestLoggerOptions, RouteConfig, SamplingConfig, SamplingRates, ServerEvent, TailSamplingCondition, TailSamplingContext, TransportConfig, WideEvent };
496
+ //# sourceMappingURL=types.d.mts.map