evlog 0.1.0 → 1.0.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 (37) hide show
  1. package/README.md +75 -12
  2. package/dist/error.d.mts +21 -11
  3. package/dist/error.d.ts +21 -11
  4. package/dist/error.mjs +12 -8
  5. package/dist/index.d.mts +4 -4
  6. package/dist/index.d.ts +4 -4
  7. package/dist/index.mjs +3 -2
  8. package/dist/logger.d.mts +1 -6
  9. package/dist/logger.d.ts +1 -6
  10. package/dist/logger.mjs +1 -4
  11. package/dist/nitro/plugin.mjs +20 -8
  12. package/dist/nuxt/module.mjs +11 -7
  13. package/dist/runtime/client/log.d.mts +9 -0
  14. package/dist/runtime/client/log.d.ts +9 -0
  15. package/dist/runtime/{composables → client}/log.mjs +2 -3
  16. package/dist/runtime/{plugin.client.mjs → client/plugin.mjs} +4 -3
  17. package/dist/runtime/server/useLogger.d.mts +5 -0
  18. package/dist/runtime/server/useLogger.d.ts +5 -0
  19. package/dist/runtime/utils/parseError.d.mts +5 -0
  20. package/dist/runtime/utils/parseError.d.ts +5 -0
  21. package/dist/runtime/utils/parseError.mjs +28 -0
  22. package/dist/types.d.mts +40 -2
  23. package/dist/types.d.ts +40 -2
  24. package/dist/utils.d.mts +3 -22
  25. package/dist/utils.d.ts +3 -22
  26. package/dist/utils.mjs +4 -1
  27. package/package.json +7 -3
  28. package/dist/runtime/composables/index.d.mts +0 -4
  29. package/dist/runtime/composables/index.d.ts +0 -4
  30. package/dist/runtime/composables/index.mjs +0 -2
  31. package/dist/runtime/composables/log.d.mts +0 -18
  32. package/dist/runtime/composables/log.d.ts +0 -18
  33. package/dist/runtime/composables/useLogger.d.mts +0 -21
  34. package/dist/runtime/composables/useLogger.d.ts +0 -21
  35. /package/dist/runtime/{plugin.client.d.mts → client/plugin.d.mts} +0 -0
  36. /package/dist/runtime/{plugin.client.d.ts → client/plugin.d.ts} +0 -0
  37. /package/dist/runtime/{composables → server}/useLogger.mjs +0 -0
package/README.md CHANGED
@@ -104,7 +104,7 @@ That's it. Now use `useLogger(event)` in any API route:
104
104
 
105
105
  ```typescript
106
106
  // server/api/checkout.post.ts
107
- import { useLogger, defineError } from 'evlog'
107
+ import { useLogger, createError } from 'evlog'
108
108
 
109
109
  export default defineEventHandler(async (event) => {
110
110
  const log = useLogger(event)
@@ -124,8 +124,9 @@ export default defineEventHandler(async (event) => {
124
124
  } catch (error) {
125
125
  log.error(error, { step: 'payment' })
126
126
 
127
- throw defineError({
127
+ throw createError({
128
128
  message: 'Payment failed',
129
+ status: 402,
129
130
  why: error.message,
130
131
  fix: 'Try a different payment method or contact your bank',
131
132
  })
@@ -173,7 +174,7 @@ Same API, same wide events:
173
174
 
174
175
  ```typescript
175
176
  // routes/api/documents/[id]/export.post.ts
176
- import { useLogger, defineError } from 'evlog'
177
+ import { useLogger, createError } from 'evlog'
177
178
 
178
179
  export default defineEventHandler(async (event) => {
179
180
  const log = useLogger(event)
@@ -189,8 +190,9 @@ export default defineEventHandler(async (event) => {
189
190
  // Load document from database
190
191
  const document = await db.documents.findUnique({ where: { id: documentId } })
191
192
  if (!document) {
192
- throw defineError({
193
+ throw createError({
193
194
  message: 'Document not found',
195
+ status: 404,
194
196
  why: `No document with ID "${documentId}" exists`,
195
197
  fix: 'Check the document ID and try again',
196
198
  })
@@ -206,8 +208,9 @@ export default defineEventHandler(async (event) => {
206
208
  } catch (error) {
207
209
  log.error(error, { step: 'export-generation' })
208
210
 
209
- throw defineError({
211
+ throw createError({
210
212
  message: 'Export failed',
213
+ status: 500,
211
214
  why: `Failed to generate ${body.format} export: ${error.message}`,
212
215
  fix: 'Try a different format or contact support',
213
216
  })
@@ -238,7 +241,7 @@ Errors should tell you **what** happened, **why**, and **how to fix it**.
238
241
 
239
242
  ```typescript
240
243
  // server/api/repos/sync.post.ts
241
- import { useLogger, defineError } from 'evlog'
244
+ import { useLogger, createError } from 'evlog'
242
245
 
243
246
  export default defineEventHandler(async (event) => {
244
247
  const log = useLogger(event)
@@ -252,8 +255,9 @@ export default defineEventHandler(async (event) => {
252
255
  } catch (error) {
253
256
  log.error(error, { step: 'github-sync' })
254
257
 
255
- throw defineError({
258
+ throw createError({
256
259
  message: 'Failed to sync repository',
260
+ status: 503,
257
261
  why: 'GitHub API rate limit exceeded',
258
262
  fix: 'Wait 1 hour or use a different token',
259
263
  link: 'https://docs.github.com/en/rest/rate-limit',
@@ -302,7 +306,7 @@ migrationLog.emit()
302
306
 
303
307
  ```typescript
304
308
  // workers/sync-job.ts
305
- import { initLogger, createRequestLogger, defineError } from 'evlog'
309
+ import { initLogger, createRequestLogger, createError } from 'evlog'
306
310
 
307
311
  initLogger({
308
312
  env: {
@@ -378,13 +382,18 @@ log.emit() // Emit final event
378
382
  log.getContext() // Get current context
379
383
  ```
380
384
 
381
- ### `defineError(options)`
385
+ ### `createError(options)`
382
386
 
383
- Create a structured error.
387
+ Create a structured error with HTTP status support. Import from `evlog` directly to avoid conflicts with Nuxt/Nitro's `createError`.
388
+
389
+ > **Note**: `createEvlogError` is also available as an auto-imported alias in Nuxt/Nitro to avoid conflicts.
384
390
 
385
391
  ```typescript
386
- defineError({
392
+ import { createError } from 'evlog'
393
+
394
+ createError({
387
395
  message: string // What happened
396
+ status?: number // HTTP status code (default: 500)
388
397
  why?: string // Why it happened
389
398
  fix?: string // How to fix it
390
399
  link?: string // Documentation URL
@@ -392,6 +401,34 @@ defineError({
392
401
  })
393
402
  ```
394
403
 
404
+ ### `parseError(error)`
405
+
406
+ Parse a caught error into a flat structure with all evlog fields. Auto-imported in Nuxt.
407
+
408
+ ```typescript
409
+ import { parseError } from 'evlog'
410
+
411
+ try {
412
+ await $fetch('/api/checkout')
413
+ } catch (err) {
414
+ const error = parseError(err)
415
+
416
+ // Direct access to all fields
417
+ console.log(error.message) // "Payment failed"
418
+ console.log(error.status) // 402
419
+ console.log(error.why) // "Card declined"
420
+ console.log(error.fix) // "Try another card"
421
+ console.log(error.link) // "https://docs.example.com/..."
422
+
423
+ // Use with toast
424
+ toast.add({
425
+ title: error.message,
426
+ description: error.why,
427
+ color: 'error',
428
+ })
429
+ }
430
+ ```
431
+
395
432
  ## Framework Support
396
433
 
397
434
  evlog works with any framework powered by [Nitro](https://nitro.unjs.io/):
@@ -405,9 +442,35 @@ evlog works with any framework powered by [Nitro](https://nitro.unjs.io/):
405
442
  | **TanStack Start** | `plugins: ['evlog/nitro']` |
406
443
  | **Standalone Nitro** | `plugins: ['evlog/nitro']` |
407
444
 
445
+ ## Agent Skills
446
+
447
+ evlog provides [Agent Skills](https://github.com/boristane/agent-skills) to help AI coding assistants understand and implement proper logging patterns in your codebase.
448
+
449
+ ### Installation
450
+
451
+ ```bash
452
+ npx add-skill hugorcd/evlog
453
+ ```
454
+
455
+ ### What it does
456
+
457
+ Once installed, your AI assistant will:
458
+ - Review your logging code and suggest wide event patterns
459
+ - Help refactor scattered `console.log` calls into structured events
460
+ - Guide you to use `createError()` for self-documenting errors
461
+ - Ensure proper use of `useLogger(event)` in Nuxt/Nitro routes
462
+
463
+ ### Examples
464
+
465
+ ```
466
+ Add logging to this endpoint
467
+ Review my logging code
468
+ Help me set up logging for this service
469
+ ```
470
+
408
471
  ## Philosophy
409
472
 
410
- Inspired by [Logging Sucks](https://loggingsucks.com/) by [Boris Tane](https://github.com/boristane).
473
+ Inspired by [Logging Sucks](https://loggingsucks.com/) by [Boris Tane](https://x.com/boristane).
411
474
 
412
475
  1. **Wide Events**: One log per request with all context
413
476
  2. **Structured Errors**: Errors that explain themselves
package/dist/error.d.mts CHANGED
@@ -7,6 +7,7 @@ import { ErrorOptions } from './types.mjs';
7
7
  * ```ts
8
8
  * throw new EvlogError({
9
9
  * message: 'Failed to sync repository',
10
+ * status: 503,
10
11
  * why: 'GitHub API rate limit exceeded',
11
12
  * fix: 'Wait 1 hour or use a different token',
12
13
  * link: 'https://docs.github.com/en/rest/rate-limit',
@@ -15,33 +16,42 @@ import { ErrorOptions } from './types.mjs';
15
16
  * ```
16
17
  */
17
18
  declare class EvlogError extends Error {
19
+ readonly status: number;
18
20
  readonly why?: string;
19
21
  readonly fix?: string;
20
22
  readonly link?: string;
21
23
  constructor(options: ErrorOptions | string);
22
- /**
23
- * Format error for console output with colors
24
- */
24
+ get statusCode(): number;
25
+ get data(): {
26
+ why: string | undefined;
27
+ fix: string | undefined;
28
+ link: string | undefined;
29
+ } | undefined;
25
30
  toString(): string;
26
- /**
27
- * Convert to plain object for JSON serialization
28
- */
29
31
  toJSON(): Record<string, unknown>;
30
32
  }
31
33
  /**
32
- * Create an EvlogError (functional alternative to `new EvlogError()`)
34
+ * Create a structured error with context for debugging and user-facing messages.
33
35
  *
34
- * Named `defineError` to avoid conflict with Nuxt's built-in `createError`
36
+ * @param options - Error message string or full options object
37
+ * @returns EvlogError instance compatible with Nitro's error handling
35
38
  *
36
39
  * @example
37
40
  * ```ts
38
- * throw defineError({
41
+ * // Simple error
42
+ * throw createError('Something went wrong')
43
+ *
44
+ * // Structured error with context
45
+ * throw createError({
39
46
  * message: 'Payment failed',
47
+ * status: 402,
40
48
  * why: 'Card declined by issuer',
41
49
  * fix: 'Try a different payment method',
50
+ * link: 'https://docs.example.com/payments',
42
51
  * })
43
52
  * ```
44
53
  */
45
- declare function defineError(options: ErrorOptions | string): EvlogError;
54
+ declare function createError(options: ErrorOptions | string): EvlogError;
55
+ declare const createEvlogError: typeof createError;
46
56
 
47
- export { EvlogError, defineError };
57
+ export { EvlogError, createError, createEvlogError };
package/dist/error.d.ts CHANGED
@@ -7,6 +7,7 @@ import { ErrorOptions } from './types.js';
7
7
  * ```ts
8
8
  * throw new EvlogError({
9
9
  * message: 'Failed to sync repository',
10
+ * status: 503,
10
11
  * why: 'GitHub API rate limit exceeded',
11
12
  * fix: 'Wait 1 hour or use a different token',
12
13
  * link: 'https://docs.github.com/en/rest/rate-limit',
@@ -15,33 +16,42 @@ import { ErrorOptions } from './types.js';
15
16
  * ```
16
17
  */
17
18
  declare class EvlogError extends Error {
19
+ readonly status: number;
18
20
  readonly why?: string;
19
21
  readonly fix?: string;
20
22
  readonly link?: string;
21
23
  constructor(options: ErrorOptions | string);
22
- /**
23
- * Format error for console output with colors
24
- */
24
+ get statusCode(): number;
25
+ get data(): {
26
+ why: string | undefined;
27
+ fix: string | undefined;
28
+ link: string | undefined;
29
+ } | undefined;
25
30
  toString(): string;
26
- /**
27
- * Convert to plain object for JSON serialization
28
- */
29
31
  toJSON(): Record<string, unknown>;
30
32
  }
31
33
  /**
32
- * Create an EvlogError (functional alternative to `new EvlogError()`)
34
+ * Create a structured error with context for debugging and user-facing messages.
33
35
  *
34
- * Named `defineError` to avoid conflict with Nuxt's built-in `createError`
36
+ * @param options - Error message string or full options object
37
+ * @returns EvlogError instance compatible with Nitro's error handling
35
38
  *
36
39
  * @example
37
40
  * ```ts
38
- * throw defineError({
41
+ * // Simple error
42
+ * throw createError('Something went wrong')
43
+ *
44
+ * // Structured error with context
45
+ * throw createError({
39
46
  * message: 'Payment failed',
47
+ * status: 402,
40
48
  * why: 'Card declined by issuer',
41
49
  * fix: 'Try a different payment method',
50
+ * link: 'https://docs.example.com/payments',
42
51
  * })
43
52
  * ```
44
53
  */
45
- declare function defineError(options: ErrorOptions | string): EvlogError;
54
+ declare function createError(options: ErrorOptions | string): EvlogError;
55
+ declare const createEvlogError: typeof createError;
46
56
 
47
- export { EvlogError, defineError };
57
+ export { EvlogError, createError, createEvlogError };
package/dist/error.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { colors, isServer } from './utils.mjs';
2
2
 
3
3
  class EvlogError extends Error {
4
+ status;
4
5
  why;
5
6
  fix;
6
7
  link;
@@ -8,6 +9,7 @@ class EvlogError extends Error {
8
9
  const opts = typeof options === "string" ? { message: options } : options;
9
10
  super(opts.message, { cause: opts.cause });
10
11
  this.name = "EvlogError";
12
+ this.status = opts.status ?? 500;
11
13
  this.why = opts.why;
12
14
  this.fix = opts.fix;
13
15
  this.link = opts.link;
@@ -15,9 +17,12 @@ class EvlogError extends Error {
15
17
  Error.captureStackTrace(this, EvlogError);
16
18
  }
17
19
  }
18
- /**
19
- * Format error for console output with colors
20
- */
20
+ get statusCode() {
21
+ return this.status;
22
+ }
23
+ get data() {
24
+ return this.why || this.fix || this.link ? { why: this.why, fix: this.fix, link: this.link } : void 0;
25
+ }
21
26
  toString() {
22
27
  const useColors = isServer();
23
28
  const red = useColors ? colors.red : "";
@@ -42,13 +47,11 @@ class EvlogError extends Error {
42
47
  }
43
48
  return lines.join("\n");
44
49
  }
45
- /**
46
- * Convert to plain object for JSON serialization
47
- */
48
50
  toJSON() {
49
51
  return {
50
52
  name: this.name,
51
53
  message: this.message,
54
+ status: this.status,
52
55
  why: this.why,
53
56
  fix: this.fix,
54
57
  link: this.link,
@@ -57,8 +60,9 @@ class EvlogError extends Error {
57
60
  };
58
61
  }
59
62
  }
60
- function defineError(options) {
63
+ function createError(options) {
61
64
  return new EvlogError(options);
62
65
  }
66
+ const createEvlogError = createError;
63
67
 
64
- export { EvlogError, defineError };
68
+ export { EvlogError, createError, createEvlogError };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- export { EvlogError, defineError } from './error.mjs';
1
+ export { EvlogError, createError, createEvlogError } from './error.mjs';
2
2
  export { createRequestLogger, getEnvironment, initLogger, log } from './logger.mjs';
3
- export { useLogger } from './runtime/composables/useLogger.mjs';
4
- export { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent } from './types.mjs';
5
- import 'h3';
3
+ export { useLogger } from './runtime/server/useLogger.mjs';
4
+ export { parseError } from './runtime/utils/parseError.mjs';
5
+ export { BaseWideEvent, EnvironmentContext, ErrorOptions, H3EventContext, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, ServerEvent, WideEvent } from './types.mjs';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { EvlogError, defineError } from './error.js';
1
+ export { EvlogError, createError, createEvlogError } from './error.js';
2
2
  export { createRequestLogger, getEnvironment, initLogger, log } from './logger.js';
3
- export { useLogger } from './runtime/composables/useLogger.js';
4
- export { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent } from './types.js';
5
- import 'h3';
3
+ export { useLogger } from './runtime/server/useLogger.js';
4
+ export { parseError } from './runtime/utils/parseError.js';
5
+ export { BaseWideEvent, EnvironmentContext, ErrorOptions, H3EventContext, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, ServerEvent, WideEvent } from './types.js';
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
- export { EvlogError, defineError } from './error.mjs';
1
+ export { EvlogError, createError, createEvlogError } from './error.mjs';
2
2
  export { createRequestLogger, getEnvironment, initLogger, log } from './logger.mjs';
3
- export { useLogger } from './runtime/composables/useLogger.mjs';
3
+ export { useLogger } from './runtime/server/useLogger.mjs';
4
+ export { parseError } from './runtime/utils/parseError.mjs';
4
5
  import './utils.mjs';
package/dist/logger.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.mjs';
1
+ import { RequestLoggerOptions, RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.mjs';
2
2
 
3
3
  /**
4
4
  * Initialize the logger with configuration.
@@ -15,11 +15,6 @@ declare function initLogger(config?: LoggerConfig): void;
15
15
  * ```
16
16
  */
17
17
  declare const log: Log;
18
- interface RequestLoggerOptions {
19
- method?: string;
20
- path?: string;
21
- requestId?: string;
22
- }
23
18
  /**
24
19
  * Create a request-scoped logger for building wide events.
25
20
  *
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.js';
1
+ import { RequestLoggerOptions, RequestLogger, EnvironmentContext, LoggerConfig, Log } from './types.js';
2
2
 
3
3
  /**
4
4
  * Initialize the logger with configuration.
@@ -15,11 +15,6 @@ declare function initLogger(config?: LoggerConfig): void;
15
15
  * ```
16
16
  */
17
17
  declare const log: Log;
18
- interface RequestLoggerOptions {
19
- method?: string;
20
- path?: string;
21
- requestId?: string;
22
- }
23
18
  /**
24
19
  * Create a request-scoped logger for building wide events.
25
20
  *
package/dist/logger.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { isDev, detectEnvironment, formatDuration, colors, getLevelColor } from './utils.mjs';
1
+ import { isDev, detectEnvironment, formatDuration, getConsoleMethod, colors, getLevelColor } from './utils.mjs';
2
2
 
3
3
  let globalEnv = {
4
4
  service: "app",
@@ -16,9 +16,6 @@ function initLogger(config = {}) {
16
16
  };
17
17
  globalPretty = config.pretty ?? isDev();
18
18
  }
19
- function getConsoleMethod(level) {
20
- return level === "error" ? "error" : level === "warn" ? "warn" : "log";
21
- }
22
19
  function emitWideEvent(level, event) {
23
20
  const formatted = {
24
21
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3,27 +3,39 @@ import { initLogger, createRequestLogger } from '../logger.mjs';
3
3
  import '../utils.mjs';
4
4
 
5
5
  function getResponseStatus(event) {
6
- return event.node?.res?.statusCode ?? 200;
6
+ if (event.node?.res?.statusCode) {
7
+ return event.node.res.statusCode;
8
+ }
9
+ if (event.response?.status) {
10
+ return event.response.status;
11
+ }
12
+ if (typeof event.context.status === "number") {
13
+ return event.context.status;
14
+ }
15
+ return 200;
7
16
  }
8
17
  const plugin = defineNitroPlugin((nitroApp) => {
9
18
  initLogger();
10
19
  nitroApp.hooks.hook("request", (event) => {
20
+ const e = event;
11
21
  const log = createRequestLogger({
12
- method: event.method,
13
- path: event.path,
14
- requestId: event.context.requestId || crypto.randomUUID()
22
+ method: e.method,
23
+ path: e.path,
24
+ requestId: e.context.requestId || crypto.randomUUID()
15
25
  });
16
- event.context.log = log;
26
+ e.context.log = log;
17
27
  });
18
28
  nitroApp.hooks.hook("afterResponse", (event) => {
19
- const log = event.context.log;
29
+ const e = event;
30
+ const log = e.context.log;
20
31
  if (log) {
21
- log.set({ status: getResponseStatus(event) });
32
+ log.set({ status: getResponseStatus(e) });
22
33
  log.emit();
23
34
  }
24
35
  });
25
36
  nitroApp.hooks.hook("error", (error, { event }) => {
26
- const log = event?.context.log;
37
+ const e = event;
38
+ const log = e?.context.log;
27
39
  if (log) {
28
40
  log.error(error);
29
41
  }
@@ -4,7 +4,7 @@ const module$1 = defineNuxtModule({
4
4
  meta: {
5
5
  name: "evlog",
6
6
  configKey: "evlog",
7
- docs: "https://github.com/hugorcd/evlog"
7
+ docs: "https://evlog.dev"
8
8
  },
9
9
  defaults: {},
10
10
  setup(options, nuxt) {
@@ -15,30 +15,34 @@ const module$1 = defineNuxtModule({
15
15
  };
16
16
  addServerPlugin(resolver.resolve("../nitro/plugin"));
17
17
  addPlugin({
18
- src: resolver.resolve("../runtime/plugin.client"),
18
+ src: resolver.resolve("../runtime/client/plugin"),
19
19
  mode: "client"
20
20
  });
21
21
  addImports([
22
22
  {
23
23
  name: "log",
24
- from: resolver.resolve("../runtime/composables/log")
24
+ from: resolver.resolve("../runtime/client/log")
25
25
  },
26
26
  {
27
- name: "defineError",
27
+ name: "createEvlogError",
28
28
  from: resolver.resolve("../error")
29
+ },
30
+ {
31
+ name: "parseError",
32
+ from: resolver.resolve("../runtime/utils/parseError")
29
33
  }
30
34
  ]);
31
35
  addServerImports([
32
36
  {
33
37
  name: "useLogger",
34
- from: resolver.resolve("../runtime/composables/useLogger")
38
+ from: resolver.resolve("../runtime/server/useLogger")
35
39
  },
36
40
  {
37
41
  name: "log",
38
- from: resolver.resolve("../runtime/composables/log")
42
+ from: resolver.resolve("../runtime/client/log")
39
43
  },
40
44
  {
41
- name: "defineError",
45
+ name: "createEvlogError",
42
46
  from: resolver.resolve("../error")
43
47
  }
44
48
  ]);
@@ -0,0 +1,9 @@
1
+ import { Log } from '../../types.mjs';
2
+
3
+ declare function initLog(options?: {
4
+ pretty?: boolean;
5
+ service?: string;
6
+ }): void;
7
+ declare const log: Log;
8
+
9
+ export { initLog, log };
@@ -0,0 +1,9 @@
1
+ import { Log } from '../../types.js';
2
+
3
+ declare function initLog(options?: {
4
+ pretty?: boolean;
5
+ service?: string;
6
+ }): void;
7
+ declare const log: Log;
8
+
9
+ export { initLog, log };
@@ -1,3 +1,5 @@
1
+ import { getConsoleMethod } from '../../utils.mjs';
2
+
1
3
  const IS_CLIENT = typeof window !== "undefined";
2
4
  let clientPretty = true;
3
5
  let clientService = "client";
@@ -11,9 +13,6 @@ function initLog(options = {}) {
11
13
  clientPretty = options.pretty ?? true;
12
14
  clientService = options.service ?? "client";
13
15
  }
14
- function getConsoleMethod(level) {
15
- return level === "error" ? "error" : level === "warn" ? "warn" : "log";
16
- }
17
16
  function emitClientWideEvent(level, event) {
18
17
  const formatted = {
19
18
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1,7 +1,8 @@
1
- import { initLog } from './composables/log.mjs';
1
+ import { initLog } from './log.mjs';
2
2
  import { defineNuxtPlugin, useRuntimeConfig } from '#app';
3
+ import '../../utils.mjs';
3
4
 
4
- const plugin_client = defineNuxtPlugin((_nuxtApp) => {
5
+ const plugin = defineNuxtPlugin(() => {
5
6
  const config = useRuntimeConfig();
6
7
  const evlogConfig = config.public?.evlog;
7
8
  initLog({
@@ -10,4 +11,4 @@ const plugin_client = defineNuxtPlugin((_nuxtApp) => {
10
11
  });
11
12
  });
12
13
 
13
- export { plugin_client as default };
14
+ export { plugin as default };
@@ -0,0 +1,5 @@
1
+ import { ServerEvent, RequestLogger } from '../../types.mjs';
2
+
3
+ declare function useLogger(event: ServerEvent): RequestLogger;
4
+
5
+ export { useLogger };
@@ -0,0 +1,5 @@
1
+ import { ServerEvent, RequestLogger } from '../../types.js';
2
+
3
+ declare function useLogger(event: ServerEvent): RequestLogger;
4
+
5
+ export { useLogger };
@@ -0,0 +1,5 @@
1
+ import { ParsedError } from '../../types.mjs';
2
+
3
+ declare function parseError(error: unknown): ParsedError;
4
+
5
+ export { ParsedError, parseError };
@@ -0,0 +1,5 @@
1
+ import { ParsedError } from '../../types.js';
2
+
3
+ declare function parseError(error: unknown): ParsedError;
4
+
5
+ export { ParsedError, parseError };
@@ -0,0 +1,28 @@
1
+ function parseError(error) {
2
+ if (error && typeof error === "object" && "data" in error) {
3
+ const { data, message: fetchMessage, statusCode: fetchStatusCode } = error;
4
+ const evlogData = data?.data;
5
+ return {
6
+ message: data?.message || fetchMessage || "An error occurred",
7
+ status: data?.statusCode || fetchStatusCode || 500,
8
+ why: evlogData?.why,
9
+ fix: evlogData?.fix,
10
+ link: evlogData?.link,
11
+ raw: error
12
+ };
13
+ }
14
+ if (error instanceof Error) {
15
+ return {
16
+ message: error.message,
17
+ status: 500,
18
+ raw: error
19
+ };
20
+ }
21
+ return {
22
+ message: String(error),
23
+ status: 500,
24
+ raw: error
25
+ };
26
+ }
27
+
28
+ export { parseError };
package/dist/types.d.mts CHANGED
@@ -116,6 +116,8 @@ interface Log {
116
116
  interface ErrorOptions {
117
117
  /** What actually happened */
118
118
  message: string;
119
+ /** HTTP status code (default: 500) */
120
+ status?: number;
119
121
  /** Why this error occurred */
120
122
  why?: string;
121
123
  /** How to fix this issue */
@@ -125,11 +127,47 @@ interface ErrorOptions {
125
127
  /** The original error that caused this */
126
128
  cause?: Error;
127
129
  }
130
+ /**
131
+ * Options for creating a request logger
132
+ */
133
+ interface RequestLoggerOptions {
134
+ method?: string;
135
+ path?: string;
136
+ requestId?: string;
137
+ }
128
138
  /**
129
139
  * H3 event context with evlog logger attached
130
140
  */
131
- interface EvlogEventContext {
141
+ interface H3EventContext {
132
142
  log?: RequestLogger;
143
+ requestId?: string;
144
+ status?: number;
145
+ [key: string]: unknown;
146
+ }
147
+ /**
148
+ * Server event type for Nitro/h3 handlers
149
+ */
150
+ interface ServerEvent {
151
+ method: string;
152
+ path: string;
153
+ context: H3EventContext;
154
+ node?: {
155
+ res?: {
156
+ statusCode?: number;
157
+ };
158
+ };
159
+ response?: Response;
160
+ }
161
+ /**
162
+ * Parsed evlog error with all fields at the top level
163
+ */
164
+ interface ParsedError {
165
+ message: string;
166
+ status: number;
167
+ why?: string;
168
+ fix?: string;
169
+ link?: string;
170
+ raw: unknown;
133
171
  }
134
172
 
135
- export type { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent };
173
+ export type { BaseWideEvent, EnvironmentContext, ErrorOptions, H3EventContext, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, ServerEvent, WideEvent };
package/dist/types.d.ts CHANGED
@@ -116,6 +116,8 @@ interface Log {
116
116
  interface ErrorOptions {
117
117
  /** What actually happened */
118
118
  message: string;
119
+ /** HTTP status code (default: 500) */
120
+ status?: number;
119
121
  /** Why this error occurred */
120
122
  why?: string;
121
123
  /** How to fix this issue */
@@ -125,11 +127,47 @@ interface ErrorOptions {
125
127
  /** The original error that caused this */
126
128
  cause?: Error;
127
129
  }
130
+ /**
131
+ * Options for creating a request logger
132
+ */
133
+ interface RequestLoggerOptions {
134
+ method?: string;
135
+ path?: string;
136
+ requestId?: string;
137
+ }
128
138
  /**
129
139
  * H3 event context with evlog logger attached
130
140
  */
131
- interface EvlogEventContext {
141
+ interface H3EventContext {
132
142
  log?: RequestLogger;
143
+ requestId?: string;
144
+ status?: number;
145
+ [key: string]: unknown;
146
+ }
147
+ /**
148
+ * Server event type for Nitro/h3 handlers
149
+ */
150
+ interface ServerEvent {
151
+ method: string;
152
+ path: string;
153
+ context: H3EventContext;
154
+ node?: {
155
+ res?: {
156
+ statusCode?: number;
157
+ };
158
+ };
159
+ response?: Response;
160
+ }
161
+ /**
162
+ * Parsed evlog error with all fields at the top level
163
+ */
164
+ interface ParsedError {
165
+ message: string;
166
+ status: number;
167
+ why?: string;
168
+ fix?: string;
169
+ link?: string;
170
+ raw: unknown;
133
171
  }
134
172
 
135
- export type { BaseWideEvent, EnvironmentContext, ErrorOptions, EvlogEventContext, Log, LogLevel, LoggerConfig, RequestLogger, WideEvent };
173
+ export type { BaseWideEvent, EnvironmentContext, ErrorOptions, H3EventContext, Log, LogLevel, LoggerConfig, ParsedError, RequestLogger, RequestLoggerOptions, ServerEvent, WideEvent };
package/dist/utils.d.mts CHANGED
@@ -1,27 +1,11 @@
1
- import { EnvironmentContext } from './types.mjs';
1
+ import { EnvironmentContext, LogLevel } from './types.mjs';
2
2
 
3
- /**
4
- * Format duration for display
5
- * < 1s: shows milliseconds (e.g., "42ms")
6
- * >= 1s: shows seconds (e.g., "1.5s")
7
- */
8
3
  declare function formatDuration(ms: number): string;
9
- /**
10
- * Check if running on server
11
- */
12
4
  declare function isServer(): boolean;
13
- /**
14
- * Check if running on client
15
- */
16
5
  declare function isClient(): boolean;
17
- /**
18
- * Check if in development mode
19
- */
20
6
  declare function isDev(): boolean;
21
- /**
22
- * Auto-detect environment context from env variables
23
- */
24
7
  declare function detectEnvironment(): Partial<EnvironmentContext>;
8
+ declare function getConsoleMethod(level: LogLevel): 'log' | 'error' | 'warn';
25
9
  declare const colors: {
26
10
  readonly reset: "\u001B[0m";
27
11
  readonly bold: "\u001B[1m";
@@ -35,9 +19,6 @@ declare const colors: {
35
19
  readonly white: "\u001B[37m";
36
20
  readonly gray: "\u001B[90m";
37
21
  };
38
- /**
39
- * Get color for log level
40
- */
41
22
  declare function getLevelColor(level: string): string;
42
23
 
43
- export { colors, detectEnvironment, formatDuration, getLevelColor, isClient, isDev, isServer };
24
+ export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
package/dist/utils.d.ts CHANGED
@@ -1,27 +1,11 @@
1
- import { EnvironmentContext } from './types.js';
1
+ import { EnvironmentContext, LogLevel } from './types.js';
2
2
 
3
- /**
4
- * Format duration for display
5
- * < 1s: shows milliseconds (e.g., "42ms")
6
- * >= 1s: shows seconds (e.g., "1.5s")
7
- */
8
3
  declare function formatDuration(ms: number): string;
9
- /**
10
- * Check if running on server
11
- */
12
4
  declare function isServer(): boolean;
13
- /**
14
- * Check if running on client
15
- */
16
5
  declare function isClient(): boolean;
17
- /**
18
- * Check if in development mode
19
- */
20
6
  declare function isDev(): boolean;
21
- /**
22
- * Auto-detect environment context from env variables
23
- */
24
7
  declare function detectEnvironment(): Partial<EnvironmentContext>;
8
+ declare function getConsoleMethod(level: LogLevel): 'log' | 'error' | 'warn';
25
9
  declare const colors: {
26
10
  readonly reset: "\u001B[0m";
27
11
  readonly bold: "\u001B[1m";
@@ -35,9 +19,6 @@ declare const colors: {
35
19
  readonly white: "\u001B[37m";
36
20
  readonly gray: "\u001B[90m";
37
21
  };
38
- /**
39
- * Get color for log level
40
- */
41
22
  declare function getLevelColor(level: string): string;
42
23
 
43
- export { colors, detectEnvironment, formatDuration, getLevelColor, isClient, isDev, isServer };
24
+ export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
package/dist/utils.mjs CHANGED
@@ -26,6 +26,9 @@ function detectEnvironment() {
26
26
  region: env.VERCEL_REGION || env.AWS_REGION || env.FLY_REGION || env.CF_REGION
27
27
  };
28
28
  }
29
+ function getConsoleMethod(level) {
30
+ return level === "error" ? "error" : level === "warn" ? "warn" : "log";
31
+ }
29
32
  const colors = {
30
33
  reset: "\x1B[0m",
31
34
  bold: "\x1B[1m",
@@ -54,4 +57,4 @@ function getLevelColor(level) {
54
57
  }
55
58
  }
56
59
 
57
- export { colors, detectEnvironment, formatDuration, getLevelColor, isClient, isDev, isServer };
60
+ export { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isClient, isDev, isServer };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "evlog",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Wide event logging library with structured error handling. Inspired by LoggingSucks.",
5
5
  "author": "HugoRCD <contact@hrcd.fr>",
6
- "homepage": "https://github.com/HugoRCD/evlog",
6
+ "homepage": "https://evlog.dev",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/HugoRCD/evlog.git"
@@ -82,7 +82,8 @@
82
82
  },
83
83
  "peerDependencies": {
84
84
  "h3": "^1.13.0",
85
- "nitropack": "^2.10.0"
85
+ "nitropack": "^2.13.1",
86
+ "ofetch": "^1.5.1"
86
87
  },
87
88
  "peerDependenciesMeta": {
88
89
  "h3": {
@@ -90,6 +91,9 @@
90
91
  },
91
92
  "nitropack": {
92
93
  "optional": true
94
+ },
95
+ "ofetch": {
96
+ "optional": true
93
97
  }
94
98
  }
95
99
  }
@@ -1,4 +0,0 @@
1
- export { initLog, log } from './log.mjs';
2
- export { useLogger } from './useLogger.mjs';
3
- import '../../types.mjs';
4
- import 'h3';
@@ -1,4 +0,0 @@
1
- export { initLog, log } from './log.js';
2
- export { useLogger } from './useLogger.js';
3
- import '../../types.js';
4
- import 'h3';
@@ -1,2 +0,0 @@
1
- export { initLog, log } from './log.mjs';
2
- export { useLogger } from './useLogger.mjs';
@@ -1,18 +0,0 @@
1
- import { Log } from '../../types.mjs';
2
-
3
- declare function initLog(options?: {
4
- pretty?: boolean;
5
- service?: string;
6
- }): void;
7
- /**
8
- * Universal logging API - works on both client and server.
9
- *
10
- * @example
11
- * ```ts
12
- * log.info('auth', 'User logged in')
13
- * log.info({ action: 'checkout', items: 3 })
14
- * ```
15
- */
16
- declare const log: Log;
17
-
18
- export { initLog, log };
@@ -1,18 +0,0 @@
1
- import { Log } from '../../types.js';
2
-
3
- declare function initLog(options?: {
4
- pretty?: boolean;
5
- service?: string;
6
- }): void;
7
- /**
8
- * Universal logging API - works on both client and server.
9
- *
10
- * @example
11
- * ```ts
12
- * log.info('auth', 'User logged in')
13
- * log.info({ action: 'checkout', items: 3 })
14
- * ```
15
- */
16
- declare const log: Log;
17
-
18
- export { initLog, log };
@@ -1,21 +0,0 @@
1
- import { H3Event } from 'h3';
2
- import { RequestLogger } from '../../types.mjs';
3
-
4
- /**
5
- * Get the request-scoped logger from the event context.
6
- * Must be called within a server handler with an H3 event.
7
- *
8
- * @example
9
- * ```ts
10
- * export default defineEventHandler((event) => {
11
- * const logger = useLogger(event)
12
- * logger.set({ userId: '123' })
13
- * logger.set({ action: 'checkout' })
14
- * return { ok: true }
15
- * // emit() is called automatically by the Nitro plugin
16
- * })
17
- * ```
18
- */
19
- declare function useLogger(event: H3Event): RequestLogger;
20
-
21
- export { useLogger };
@@ -1,21 +0,0 @@
1
- import { H3Event } from 'h3';
2
- import { RequestLogger } from '../../types.js';
3
-
4
- /**
5
- * Get the request-scoped logger from the event context.
6
- * Must be called within a server handler with an H3 event.
7
- *
8
- * @example
9
- * ```ts
10
- * export default defineEventHandler((event) => {
11
- * const logger = useLogger(event)
12
- * logger.set({ userId: '123' })
13
- * logger.set({ action: 'checkout' })
14
- * return { ok: true }
15
- * // emit() is called automatically by the Nitro plugin
16
- * })
17
- * ```
18
- */
19
- declare function useLogger(event: H3Event): RequestLogger;
20
-
21
- export { useLogger };