@visulima/pail 4.0.0-alpha.6 → 4.0.0-alpha.8

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 (52) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE.md +2 -2
  3. package/README.md +323 -0
  4. package/dist/error.d.ts +104 -0
  5. package/dist/error.js +76 -0
  6. package/dist/index.browser.d.ts +2 -0
  7. package/dist/index.browser.js +4 -3
  8. package/dist/index.server.d.ts +2 -0
  9. package/dist/index.server.js +5 -3
  10. package/dist/middleware/elysia.d.ts +71 -0
  11. package/dist/middleware/elysia.js +70 -0
  12. package/dist/middleware/express.d.ts +86 -0
  13. package/dist/middleware/express.js +29 -0
  14. package/dist/middleware/fastify.d.ts +81 -0
  15. package/dist/middleware/fastify.js +46 -0
  16. package/dist/middleware/hono.d.ts +85 -0
  17. package/dist/middleware/hono.js +33 -0
  18. package/dist/middleware/next/handler.d.ts +36 -0
  19. package/dist/middleware/next/handler.js +53 -0
  20. package/dist/middleware/next/middleware.d.ts +59 -0
  21. package/dist/middleware/next/storage.d.ts +14 -0
  22. package/dist/middleware/shared/create-middleware-logger.d.ts +82 -0
  23. package/dist/middleware/shared/headers.d.ts +14 -0
  24. package/dist/middleware/shared/routes.d.ts +30 -0
  25. package/dist/middleware/shared/storage.d.ts +29 -0
  26. package/dist/middleware/sveltekit.d.ts +123 -0
  27. package/dist/middleware/sveltekit.js +43 -0
  28. package/dist/packem_shared/{AbstractJsonReporter-DWRpTtGw.js → AbstractJsonReporter-CGKHS8_M.js} +103 -21
  29. package/dist/packem_shared/{AbstractJsonReporter-BaZ33PlE.js → AbstractJsonReporter-DDjDkciI.js} +103 -21
  30. package/dist/packem_shared/{JsonReporter-BV5lMnJX.js → JsonReporter-B3XX8GHN.js} +1 -1
  31. package/dist/packem_shared/{JsonReporter-BRw4skd5.js → JsonReporter-p_BXg6Sj.js} +1 -1
  32. package/dist/packem_shared/{PrettyReporter-BjXCFQlo.js → PrettyReporter-CvBn-hxP.js} +2 -1
  33. package/dist/packem_shared/createPailError-B11aRfrT.js +76 -0
  34. package/dist/packem_shared/headers-Cp4uLtr4.js +123 -0
  35. package/dist/packem_shared/pailMiddleware-Ci88geIF.js +24 -0
  36. package/dist/packem_shared/storage-D0vqz8OX.js +36 -0
  37. package/dist/packem_shared/useLogger-D0rU3lcX.js +33 -0
  38. package/dist/processor/environment-processor.d.ts +124 -0
  39. package/dist/processor/environment-processor.js +78 -0
  40. package/dist/processor/message-formatter-processor.d.ts +1 -2
  41. package/dist/processor/sampling-processor.d.ts +111 -0
  42. package/dist/processor/sampling-processor.js +59 -0
  43. package/dist/reporter/file/json-file-reporter.js +1 -1
  44. package/dist/reporter/http/abstract-http-reporter.js +1 -1
  45. package/dist/reporter/http/http-reporter.edge-light.js +103 -21
  46. package/dist/reporter/json/index.browser.js +2 -2
  47. package/dist/reporter/json/index.js +2 -2
  48. package/dist/reporter/pretty/index.js +1 -1
  49. package/dist/reporter/simple/simple-reporter.server.js +2 -1
  50. package/dist/wide-event.d.ts +300 -0
  51. package/dist/wide-event.js +281 -0
  52. package/package.json +70 -5
package/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ ## @visulima/pail [4.0.0-alpha.8](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.7...@visulima/pail@4.0.0-alpha.8) (2026-03-26)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **pail:** use workspace:* for internal [@visulima](https://github.com/visulima) deps ([8423dd7](https://github.com/visulima/visulima/commit/8423dd779fbea0acff8f090e548a423e46d761fd))
6
+ * **web:** improve build setup with incremental stats caching and prod install ([fe33e75](https://github.com/visulima/visulima/commit/fe33e75827586779b4b3a0c6d57b39f889ee6207))
7
+
8
+ ### Miscellaneous Chores
9
+
10
+ * **pail:** migrate deps to pnpm catalogs ([b2acd86](https://github.com/visulima/visulima/commit/b2acd864a40d0b2cc8eaf4e531cb2dedfd54181a))
11
+ * update license ([b2d306c](https://github.com/visulima/visulima/commit/b2d306cfeb1eabfd9e24880cb9198f6360724d82))
12
+
13
+
14
+ ### Dependencies
15
+
16
+ * **@visulima/colorize:** upgraded to 2.0.0-alpha.6
17
+ * **@visulima/error:** upgraded to 6.0.0-alpha.6
18
+ * **@visulima/fmt:** upgraded to 2.0.0-alpha.6
19
+ * **@visulima/inspector:** upgraded to 2.0.0-alpha.5
20
+ * **@visulima/redact:** upgraded to 3.0.0-alpha.6
21
+ * **@visulima/string:** upgraded to 3.0.0-alpha.7
22
+
23
+ ## @visulima/pail [4.0.0-alpha.7](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.6...@visulima/pail@4.0.0-alpha.7) (2026-03-16)
24
+
25
+ ### Features
26
+
27
+ * **pail:** add self-documenting errors, sampling processor, and environment processor ([3ec79de](https://github.com/visulima/visulima/commit/3ec79de3034cee4d1514dc93e78da2d05df4f9a7))
28
+ * **pail:** add wide events, framework middleware, tests, and docs ([6d1a4f3](https://github.com/visulima/visulima/commit/6d1a4f3ef4455da78371b8754cdbca9c939bc8b4))
29
+
30
+ ### Miscellaneous Chores
31
+
32
+ * **pail:** update dependencies ([72b9561](https://github.com/visulima/visulima/commit/72b9561a3416a121198ea47cdcbe0202c0d8cbef))
33
+ * remove exit 0 ([b6d2408](https://github.com/visulima/visulima/commit/b6d2408fab8b5299a5ae902021b229909c84f184))
34
+ * visulima website ([#591](https://github.com/visulima/visulima/issues/591)) ([59ab2e2](https://github.com/visulima/visulima/commit/59ab2e2befb03e51cd2088956f83d9b87de6d033))
35
+
1
36
  ## @visulima/pail [4.0.0-alpha.6](https://github.com/visulima/visulima/compare/@visulima/pail@4.0.0-alpha.5...@visulima/pail@4.0.0-alpha.6) (2026-03-06)
2
37
 
3
38
  ### Bug Fixes
package/LICENSE.md CHANGED
@@ -34,7 +34,7 @@ Repository: git+https://github.com/visulima/visulima.git
34
34
 
35
35
  > MIT License
36
36
  >
37
- > Copyright (c) 2025 visulima
37
+ > Copyright (c) 2026 visulima
38
38
  >
39
39
  > Permission is hereby granted, free of charge, to any person obtaining a copy
40
40
  > of this software and associated documentation files (the "Software"), to deal
@@ -68,7 +68,7 @@ Repository: git+https://github.com/visulima/visulima.git
68
68
  >
69
69
  > > MIT License
70
70
  > >
71
- > > Copyright (c) 2025 visulima
71
+ > > Copyright (c) 2026 visulima
72
72
  > >
73
73
  > > Permission is hereby granted, free of charge, to any person obtaining a copy
74
74
  > > of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -63,6 +63,8 @@ If you're upgrading from an earlier version of pail, check out our [Migration Gu
63
63
  - ESM‑only with tree‑shaking support (Node.js ≥ 20.19)
64
64
  - Supports circular structures
65
65
  - Fast and powerful, see the [benchmarks](__bench__/README.md)
66
+ - [Wide events](#wide-events) — accumulate context across an operation and emit once
67
+ - [Framework middleware](#framework-middleware) for Express, Fastify, Hono, Elysia, SvelteKit, and Next.js
66
68
 
67
69
  ## Install
68
70
 
@@ -1064,6 +1066,327 @@ When a log entry exceeds `maxLogSize`, a `LogSizeError` is thrown. When a batch
1064
1066
  | `onDebugRequestResponse` | `(reqRes: {...}) => void` | `undefined` | Debug callback for HTTP requests/responses |
1065
1067
  | `payloadTemplate` | `(data: {...}) => string` | `undefined` | Custom payload formatter |
1066
1068
 
1069
+ ## Wide Events
1070
+
1071
+ Wide events let you accumulate structured context throughout an operation and emit a single, comprehensive log event at the end — instead of scattering multiple log calls.
1072
+
1073
+ Inspired by [Charity Majors' wide events](https://charity.wtf/2019/02/05/logs-vs-structured-events/) pattern.
1074
+
1075
+ ### Basic Usage
1076
+
1077
+ ```typescript
1078
+ import { createPail } from "@visulima/pail";
1079
+ import { createWideEvent } from "@visulima/pail/wide-event";
1080
+
1081
+ const logger = createPail();
1082
+
1083
+ const ev = createWideEvent({ pail: logger, name: "api.checkout" });
1084
+
1085
+ ev.set({ user: { id: 1, plan: "pro" } });
1086
+ ev.info("Validated cart", { itemCount: 3 });
1087
+ ev.set({ cart: { id: 42, items: 3, total: 9999 } });
1088
+ ev.info("Payment processed");
1089
+ ev.finish({ status: 200 });
1090
+
1091
+ // Emits a single structured log:
1092
+ // {
1093
+ // event: "api.checkout",
1094
+ // timestamp: "2026-03-14T10:23:45.612Z",
1095
+ // duration: "127ms",
1096
+ // duration_ms: 127,
1097
+ // status: 200,
1098
+ // user: { id: 1, plan: "pro" },
1099
+ // cart: { id: 42, items: 3, total: 9999 },
1100
+ // requestLogs: [
1101
+ // { level: "info", message: "Validated cart", context: { itemCount: 3 }, timestamp: "..." },
1102
+ // { level: "info", message: "Payment processed", timestamp: "..." }
1103
+ // ]
1104
+ // }
1105
+ ```
1106
+
1107
+ ### Auto-Emit with Explicit Resource Management
1108
+
1109
+ Wide events implement `Disposable` for use with TC39 Explicit Resource Management (`using`):
1110
+
1111
+ ```typescript
1112
+ {
1113
+ using ev = createWideEvent({ pail: logger, name: "api.checkout" });
1114
+ ev.set({ user: { id: 1 } });
1115
+ // auto-emits when scope exits
1116
+ }
1117
+ ```
1118
+
1119
+ ### Typed Data Shape
1120
+
1121
+ ```typescript
1122
+ interface CheckoutData {
1123
+ user: { id: number; plan: string };
1124
+ cart: { items: number; total: number };
1125
+ }
1126
+
1127
+ const ev = createWideEvent<CheckoutData>({ pail: logger, name: "api.checkout" });
1128
+ ev.set({ user: { id: 1, plan: "pro" } }); // fully typed
1129
+ ```
1130
+
1131
+ ### Level Escalation
1132
+
1133
+ The event level auto-escalates based on lifecycle log entries. If you call `warn()` or `error()`, the final emission uses the highest severity:
1134
+
1135
+ ```typescript
1136
+ const ev = createWideEvent({ pail: logger, name: "api.checkout" });
1137
+
1138
+ ev.info("Cart validated"); // level stays "info"
1139
+ ev.warn("Rate limit approaching"); // level escalates to "warn"
1140
+ ev.info("Payment processed"); // level stays "warn" (no de-escalation)
1141
+ ev.finish({ status: 200 }); // emits at "warn" level
1142
+ ```
1143
+
1144
+ ### Error Handling
1145
+
1146
+ ```typescript
1147
+ const ev = createWideEvent({ pail: logger, name: "api.checkout" });
1148
+
1149
+ try {
1150
+ await processPayment();
1151
+ } catch (error) {
1152
+ ev.error("Payment failed", error);
1153
+ ev.finish({ status: 500, error });
1154
+ // Emits with serialized error (name, message, stack, status, cause chain)
1155
+ }
1156
+ ```
1157
+
1158
+ ### Options
1159
+
1160
+ | Option | Type | Default | Description |
1161
+ | ---------- | ------------- | -------- | --------------------------------- |
1162
+ | `name` | `string` | required | Event name, e.g. `"api.checkout"` |
1163
+ | `pail` | Pail instance | required | The pail logger to emit through |
1164
+ | `service` | `string` | — | Service name override |
1165
+ | `type` | `string` | `"info"` | Base log type (may be escalated) |
1166
+ | `autoEmit` | `boolean` | `true` | Auto-emit on `Symbol.dispose` |
1167
+
1168
+ ### Methods
1169
+
1170
+ | Method | Description |
1171
+ | ------------------------ | --------------------------------------------- |
1172
+ | `set(data)` | Deep-merge partial data into the event |
1173
+ | `info(msg, ctx?)` | Record an info-level lifecycle entry |
1174
+ | `warn(msg, ctx?)` | Record a warn-level entry (escalates level) |
1175
+ | `error(msg, err?, ctx?)` | Record an error-level entry (escalates level) |
1176
+ | `debug(msg, ctx?)` | Record a debug-level entry |
1177
+ | `setError(error)` | Attach an error (escalates to "error") |
1178
+ | `setStatus(code)` | Set HTTP status code |
1179
+ | `finish(opts?)` | Set status/error and emit |
1180
+ | `emit(typeOverride?)` | Emit the event (only fires once) |
1181
+ | `getData()` | Get read-only snapshot of accumulated data |
1182
+ | `getLevel()` | Get current severity level |
1183
+ | `getRequestLogs()` | Get read-only lifecycle log entries |
1184
+
1185
+ ## Framework Middleware
1186
+
1187
+ Pail provides first-class middleware/plugin adapters for popular web frameworks. Each adapter creates a request-scoped [Wide Event](#wide-events) that:
1188
+
1189
+ - Accumulates context throughout the request lifecycle
1190
+ - Auto-emits a single structured log when the response completes or an error occurs
1191
+ - Is accessible via `req.log` / `request.log` / `context.log` and `useLogger()`
1192
+ - Supports route inclusion/exclusion via glob patterns
1193
+ - Supports per-route service name overrides
1194
+
1195
+ ### Shared Options
1196
+
1197
+ All middleware adapters accept these options:
1198
+
1199
+ | Option | Type | Description |
1200
+ | --------- | ------------------------------ | ----------------------------------- |
1201
+ | `pail` | Pail instance | The pail logger (required) |
1202
+ | `service` | `string` | Default service name |
1203
+ | `include` | `string[]` | Glob patterns for paths to include |
1204
+ | `exclude` | `string[]` | Glob patterns for paths to exclude |
1205
+ | `routes` | `Record<string, { service? }>` | Per-route config (first match wins) |
1206
+
1207
+ ### Express
1208
+
1209
+ ```typescript
1210
+ import express from "express";
1211
+ import { createPail } from "@visulima/pail";
1212
+ import { pailMiddleware, useLogger } from "@visulima/pail/middleware/express";
1213
+
1214
+ const app = express();
1215
+ const logger = createPail();
1216
+
1217
+ app.use(pailMiddleware({ pail: logger }));
1218
+
1219
+ app.get("/api/users", (req, res) => {
1220
+ req.log.set({ user: { id: 1 } });
1221
+ req.log.info("Fetched user list");
1222
+ // or from anywhere in the async call stack:
1223
+ // useLogger().set({ user: { id: 1 } });
1224
+ res.json({ ok: true });
1225
+ });
1226
+ ```
1227
+
1228
+ **Exports:** `pailMiddleware`, `useLogger`, `PailRequest`, `PailResponse`, `PailNextFunction`, `PailExpressMiddleware`, `WideEvent`
1229
+
1230
+ ### Fastify
1231
+
1232
+ ```typescript
1233
+ import Fastify from "fastify";
1234
+ import { createPail } from "@visulima/pail";
1235
+ import { pailPlugin, useLogger } from "@visulima/pail/middleware/fastify";
1236
+
1237
+ const app = Fastify();
1238
+ const logger = createPail();
1239
+
1240
+ pailPlugin(app, { pail: logger });
1241
+
1242
+ app.get("/api/users", async (request, reply) => {
1243
+ request.log.set({ user: { id: 1 } });
1244
+ return { ok: true };
1245
+ });
1246
+ ```
1247
+
1248
+ **Exports:** `pailPlugin`, `useLogger`, `PailFastifyRequest`, `PailFastifyReply`, `PailFastifyInstance`, `WideEvent`
1249
+
1250
+ ### Hono
1251
+
1252
+ ```typescript
1253
+ import { Hono } from "hono";
1254
+ import { createPail } from "@visulima/pail";
1255
+ import { pailMiddleware, useLogger } from "@visulima/pail/middleware/hono";
1256
+
1257
+ const app = new Hono();
1258
+ const logger = createPail();
1259
+
1260
+ app.use("*", pailMiddleware({ pail: logger }));
1261
+
1262
+ app.get("/api/users", (c) => {
1263
+ const log = useLogger(c);
1264
+ log.set({ user: { id: 1 } });
1265
+ return c.json({ ok: true });
1266
+ });
1267
+ ```
1268
+
1269
+ > **Note:** Hono's `useLogger()` takes the context `c` as an argument (the logger is stored on the context, not in AsyncLocalStorage).
1270
+
1271
+ **Exports:** `pailMiddleware`, `useLogger`, `PailHonoContext`, `PailHonoNext`, `PailHonoMiddleware`, `WideEvent`
1272
+
1273
+ ### Elysia
1274
+
1275
+ ```typescript
1276
+ import { Elysia } from "elysia";
1277
+ import { createPail } from "@visulima/pail";
1278
+ import { pailPlugin, useLogger } from "@visulima/pail/middleware/elysia";
1279
+
1280
+ const logger = createPail();
1281
+ const app = new Elysia();
1282
+
1283
+ pailPlugin(app, { pail: logger });
1284
+
1285
+ app.get("/api/users", ({ log }) => {
1286
+ log.set({ user: { id: 1 } });
1287
+ return { ok: true };
1288
+ });
1289
+ ```
1290
+
1291
+ **Exports:** `pailPlugin`, `useLogger`, `PailElysiaInstance`, `WideEvent`
1292
+
1293
+ ### SvelteKit
1294
+
1295
+ ```typescript
1296
+ // src/hooks.server.ts
1297
+ import { createPail } from "@visulima/pail";
1298
+ import { createPailHooks, useLogger } from "@visulima/pail/middleware/sveltekit";
1299
+
1300
+ const logger = createPail();
1301
+ const { handle, handleError } = createPailHooks({ pail: logger });
1302
+
1303
+ export { handle, handleError };
1304
+ ```
1305
+
1306
+ ```typescript
1307
+ // In a load function or action:
1308
+ import { useLogger } from "@visulima/pail/middleware/sveltekit";
1309
+
1310
+ export const load = async (event) => {
1311
+ event.locals.log.set({ user: { id: 1 } });
1312
+ // or from anywhere in the async call stack:
1313
+ // useLogger().set({ user: { id: 1 } });
1314
+ };
1315
+ ```
1316
+
1317
+ **Exports:** `pailHandle`, `pailHandleError`, `createPailHooks`, `useLogger`, `PailSvelteKitEvent`, `PailSvelteKitHandle`, `PailSvelteKitHandleError`, `WideEvent`
1318
+
1319
+ ### Next.js
1320
+
1321
+ Next.js integration uses two parts: an edge middleware for request ID injection and a handler wrapper for wide event logging.
1322
+
1323
+ ```typescript
1324
+ // middleware.ts
1325
+ import { NextResponse } from "next/server";
1326
+ import { pailMiddleware } from "@visulima/pail/middleware/next";
1327
+
1328
+ export default pailMiddleware(NextResponse, {
1329
+ exclude: ["/_next/**", "/favicon.ico"],
1330
+ });
1331
+ ```
1332
+
1333
+ ```typescript
1334
+ // lib/pail.ts
1335
+ import { createPail } from "@visulima/pail";
1336
+ import { createWithPail } from "@visulima/pail/middleware/next";
1337
+
1338
+ const logger = createPail();
1339
+ export const withPail = createWithPail({ pail: logger });
1340
+ ```
1341
+
1342
+ ```typescript
1343
+ // app/api/users/route.ts
1344
+ import { withPail } from "@/lib/pail";
1345
+ import { useLogger } from "@visulima/pail/middleware/next";
1346
+
1347
+ export const GET = withPail(async (request: Request) => {
1348
+ const log = useLogger();
1349
+ log.set({ user: { id: 1 } });
1350
+ return Response.json({ ok: true });
1351
+ });
1352
+ ```
1353
+
1354
+ **Exports:** `pailMiddleware`, `createWithPail`, `useLogger`, `WideEvent`
1355
+
1356
+ ### Route Configuration
1357
+
1358
+ All adapters support glob-based route configuration:
1359
+
1360
+ ```typescript
1361
+ pailMiddleware({
1362
+ pail: logger,
1363
+ service: "api-gateway",
1364
+ exclude: ["/health", "/metrics", "/_next/**"],
1365
+ include: ["/api/**"],
1366
+ routes: {
1367
+ "/api/auth/**": { service: "auth-service" },
1368
+ "/api/payments/**": { service: "payments-service" },
1369
+ },
1370
+ });
1371
+ ```
1372
+
1373
+ - **Exclusions take precedence** over inclusions
1374
+ - **Glob patterns** support `*` (any non-`/`), `**` (any including `/`), and `?` (single char)
1375
+ - **Route service overrides** apply to the first matching pattern
1376
+
1377
+ ### Sensitive Header Filtering
1378
+
1379
+ All adapters automatically filter sensitive headers before logging:
1380
+
1381
+ | Filtered Headers |
1382
+ | --------------------- |
1383
+ | `authorization` |
1384
+ | `cookie` |
1385
+ | `set-cookie` |
1386
+ | `x-api-key` |
1387
+ | `x-auth-token` |
1388
+ | `proxy-authorization` |
1389
+
1067
1390
  ## Integrations
1068
1391
 
1069
1392
  ### Use with @visulima/boxen
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Options for creating a PailError.
3
+ *
4
+ * Extends standard error properties with self-documenting fields
5
+ * that provide actionable context for debugging, particularly useful
6
+ * for AI-assisted log analysis.
7
+ */
8
+ interface PailErrorOptions {
9
+ /** The root cause of this error */
10
+ cause?: unknown;
11
+ /** A suggested resolution or steps to fix the issue */
12
+ fix?: string;
13
+ /** A link to relevant documentation or issue tracker */
14
+ link?: string;
15
+ /** The error message */
16
+ message: string;
17
+ /** HTTP status code associated with this error */
18
+ status?: number;
19
+ /** An explanation of what caused the failure */
20
+ why?: string;
21
+ }
22
+ /**
23
+ * Self-documenting error class for structured logging.
24
+ *
25
+ * Inspired by evlog's structured error approach, PailError extends the native
26
+ * Error class with additional fields that make log entries immediately actionable:
27
+ * - `why`: Explains what caused the failure
28
+ * - `fix`: Suggests how to resolve the issue
29
+ * - `link`: Points to relevant documentation
30
+ * - `status`: HTTP status code for request-related errors
31
+ *
32
+ * These fields are preserved through the logging pipeline and displayed by reporters,
33
+ * making it easier for both humans and AI agents to understand and resolve issues.
34
+ * @example
35
+ * ```typescript
36
+ * import { PailError, createPailError } from "@visulima/pail/error";
37
+ *
38
+ * // Using the class directly
39
+ * throw new PailError({
40
+ * message: "Payment processing failed",
41
+ * status: 402,
42
+ * why: "The customer's card was declined by the payment provider",
43
+ * fix: "Retry with a different payment method or contact the card issuer",
44
+ * link: "https://docs.example.com/payments/declined",
45
+ * });
46
+ *
47
+ * // Using the factory function
48
+ * throw createPailError("Connection timeout");
49
+ *
50
+ * // With full options
51
+ * throw createPailError({
52
+ * message: "Database connection failed",
53
+ * why: "Connection pool exhausted after 30s timeout",
54
+ * fix: "Increase pool size or check for connection leaks",
55
+ * });
56
+ * ```
57
+ */
58
+ declare class PailError extends Error {
59
+ /** HTTP status code (defaults to 500) */
60
+ readonly status: number;
61
+ /** Explanation of what caused the failure */
62
+ readonly why: string | undefined;
63
+ /** Suggested resolution steps */
64
+ readonly fix: string | undefined;
65
+ /** Link to relevant documentation */
66
+ readonly link: string | undefined;
67
+ constructor(options: PailErrorOptions | string);
68
+ /**
69
+ * Converts the error to a JSON-serializable object.
70
+ *
71
+ * Includes all self-documenting fields in the output for structured logging.
72
+ * @returns A plain object representation of the error
73
+ */
74
+ toJSON(): Record<string, unknown>;
75
+ /**
76
+ * Returns a formatted string representation including self-documenting fields.
77
+ * @returns Formatted error string with why/fix/link context
78
+ */
79
+ toString(): string;
80
+ }
81
+ /**
82
+ * Factory function for creating PailError instances.
83
+ *
84
+ * A convenient shorthand for creating PailError objects. Accepts either
85
+ * a string message or a full PailErrorOptions object.
86
+ * @param options Error message string or PailErrorOptions object
87
+ * @returns A new PailError instance
88
+ * @example
89
+ * ```typescript
90
+ * import { createPailError } from "@visulima/pail/error";
91
+ *
92
+ * throw createPailError("Something went wrong");
93
+ *
94
+ * throw createPailError({
95
+ * message: "Auth failed",
96
+ * status: 401,
97
+ * why: "Token expired",
98
+ * fix: "Refresh the authentication token",
99
+ * });
100
+ * ```
101
+ */
102
+ declare const createPailError: (options: PailErrorOptions | string) => PailError;
103
+ export { createPailError, PailError };
104
+ export type { PailErrorOptions };
package/dist/error.js ADDED
@@ -0,0 +1,76 @@
1
+ class PailError extends Error {
2
+ /** HTTP status code (defaults to 500) */
3
+ status;
4
+ /** Explanation of what caused the failure */
5
+ why;
6
+ /** Suggested resolution steps */
7
+ fix;
8
+ /** Link to relevant documentation */
9
+ link;
10
+ constructor(options) {
11
+ const resolvedOptions = typeof options === "string" ? { message: options } : options;
12
+ super(resolvedOptions.message, resolvedOptions.cause === void 0 ? void 0 : { cause: resolvedOptions.cause });
13
+ this.name = "PailError";
14
+ this.status = resolvedOptions.status ?? 500;
15
+ this.why = resolvedOptions.why;
16
+ this.fix = resolvedOptions.fix;
17
+ this.link = resolvedOptions.link;
18
+ }
19
+ /**
20
+ * Converts the error to a JSON-serializable object.
21
+ *
22
+ * Includes all self-documenting fields in the output for structured logging.
23
+ * @returns A plain object representation of the error
24
+ */
25
+ toJSON() {
26
+ const json = {
27
+ message: this.message,
28
+ name: this.name,
29
+ status: this.status
30
+ };
31
+ if (this.why) {
32
+ json.why = this.why;
33
+ }
34
+ if (this.fix) {
35
+ json.fix = this.fix;
36
+ }
37
+ if (this.link) {
38
+ json.link = this.link;
39
+ }
40
+ if (this.stack) {
41
+ json.stack = this.stack;
42
+ }
43
+ if (this.cause !== void 0) {
44
+ json.cause = this.cause instanceof Error ? { message: this.cause.message, name: this.cause.name, stack: this.cause.stack } : this.cause;
45
+ }
46
+ return json;
47
+ }
48
+ /**
49
+ * Returns a formatted string representation including self-documenting fields.
50
+ * @returns Formatted error string with why/fix/link context
51
+ */
52
+ toString() {
53
+ let output = `${this.name} [${this.status}]: ${this.message}`;
54
+ if (this.why) {
55
+ output += `
56
+ Why: ${this.why}`;
57
+ }
58
+ if (this.fix) {
59
+ output += `
60
+ Fix: ${this.fix}`;
61
+ }
62
+ if (this.link) {
63
+ output += `
64
+ Link: ${this.link}`;
65
+ }
66
+ if (this.cause !== void 0) {
67
+ const causeMessage = this.cause instanceof Error ? this.cause.message : String(this.cause);
68
+ output += `
69
+ Cause: ${causeMessage}`;
70
+ }
71
+ return output;
72
+ }
73
+ }
74
+ const createPailError = (options) => new PailError(options);
75
+
76
+ export { PailError, createPailError };
@@ -44,4 +44,6 @@ export declare const createPail: <T extends string = string, L extends string =
44
44
  */
45
45
  export declare const pail: PailBrowserType<string, string>;
46
46
  export type { PailBrowserType as Pail } from "./pail.d.ts";
47
+ export type { PailErrorOptions } from "./error.d.ts";
48
+ export { createPailError, PailError } from "./error.d.ts";
47
49
  export type { ConstructorOptions, DefaultLoggerTypes, DefaultLogTypes, ExtendedRfc5424LogLevels, LoggerConfiguration, LoggerFunction, LoggerTypesAwareReporter, LoggerTypesConfig, Processor, Reporter, StreamAwareReporter, } from "./types.d.ts";
@@ -1,10 +1,11 @@
1
1
  import { a as EXTENDED_RFC_5424_LOG_LEVELS, E as EMPTY_SYMBOL, L as LOG_TYPES } from './packem_shared/constants-omsTHUWB.js';
2
2
  import { w as writeConsoleLogBasedOnLevel } from './packem_shared/write-console-log-based-on-level-DBmRYXpj.js';
3
3
  import { g as getLongestLabel, b as build } from './packem_shared/index-DnkF86LQ.js';
4
- import JsonReporter from './packem_shared/JsonReporter-BRw4skd5.js';
4
+ import JsonReporter from './packem_shared/JsonReporter-p_BXg6Sj.js';
5
+ export { PailError, createPailError } from './packem_shared/createPailError-B11aRfrT.js';
5
6
 
6
- function getDefaultExportFromCjs (x) {
7
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
7
+ function getDefaultExportFromCjs(x) {
8
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
8
9
  }
9
10
 
10
11
  var safeStableStringify = {exports: {}};
@@ -62,4 +62,6 @@ export type { MultiBarOptions, ProgressBarOptions, ProgressBarPayload, ProgressB
62
62
  export { getBarChar, MultiProgressBar, ProgressBar } from "./progress-bar.d.ts";
63
63
  export type { SpinnerIcons, SpinnerOptions, SpinnerStartOptions, SpinnerStyle } from "./spinner.d.ts";
64
64
  export { MultiSpinner, Spinner } from "./spinner.d.ts";
65
+ export type { PailErrorOptions } from "./error.d.ts";
66
+ export { createPailError, PailError } from "./error.d.ts";
65
67
  export type { ConstructorOptions, DefaultLoggerTypes, DefaultLogTypes, ExtendedRfc5424LogLevels, LoggerConfiguration, LoggerFunction, LoggerTypesAwareReporter, LoggerTypesConfig, Processor, Reporter, StreamAwareReporter, } from "./types.d.ts";
@@ -30,6 +30,7 @@ import { ProgressBar, applyStyleToOptions, MultiProgressBar } from './packem_sha
30
30
  export { getBarChar } from './packem_shared/getBarChar-BWj1UrH3.js';
31
31
  import { Spinner, MultiSpinner } from './packem_shared/Spinner-DIdVcfWq.js';
32
32
  import colorize, { red, greenBright, cyan, green, grey, bgGrey, underline, white } from '@visulima/colorize';
33
+ export { PailError, createPailError } from './packem_shared/createPailError-B11aRfrT.js';
33
34
 
34
35
  class InteractiveManager {
35
36
  #stream;
@@ -317,8 +318,8 @@ class InteractiveStreamHook {
317
318
  }
318
319
  }
319
320
 
320
- function getDefaultExportFromCjs (x) {
321
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
321
+ function getDefaultExportFromCjs(x) {
322
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
322
323
  }
323
324
 
324
325
  var safeStableStringify = {exports: {}};
@@ -2946,7 +2947,8 @@ class MessageFormatterProcessor {
2946
2947
  }
2947
2948
  }
2948
2949
 
2949
- const pailFileFilter = (line) => !/[\\/]pail[\\/]dist/.test(line);
2950
+ const PAIL_DIST_REGEX = /[\\/]pail[\\/]dist/;
2951
+ const pailFileFilter = (line) => !PAIL_DIST_REGEX.test(line);
2950
2952
  class PrettyReporter extends AbstractPrettyReporter {
2951
2953
  #stdout;
2952
2954
  #stderr;