@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
@@ -12,7 +12,7 @@ const ErrorProto = Object.create(
12
12
  {},
13
13
  {
14
14
  cause: {
15
- enumerable: true,
15
+ enumerable: false,
16
16
  value: void 0,
17
17
  writable: true
18
18
  },
@@ -22,32 +22,55 @@ const ErrorProto = Object.create(
22
22
  writable: true
23
23
  },
24
24
  errors: {
25
- enumerable: true,
25
+ enumerable: false,
26
26
  value: void 0,
27
27
  writable: true
28
28
  },
29
29
  message: {
30
- enumerable: true,
30
+ enumerable: false,
31
31
  value: void 0,
32
32
  writable: true
33
33
  },
34
34
  name: {
35
- enumerable: true,
35
+ enumerable: false,
36
36
  value: void 0,
37
37
  writable: true
38
38
  },
39
39
  stack: {
40
- enumerable: true,
40
+ enumerable: false,
41
41
  value: void 0,
42
42
  writable: true
43
43
  }
44
44
  }
45
45
  );
46
46
  const toJsonWasCalled = /* @__PURE__ */ new WeakSet();
47
+ const makePropertiesEnumerable = (object) => {
48
+ if (!object || typeof object !== "object") {
49
+ return;
50
+ }
51
+ const props = Object.getOwnPropertyNames(object);
52
+ for (const prop of props) {
53
+ const descriptor = Object.getOwnPropertyDescriptor(object, prop);
54
+ if (descriptor) {
55
+ if (!descriptor.enumerable) {
56
+ Object.defineProperty(object, prop, {
57
+ ...descriptor,
58
+ enumerable: true
59
+ });
60
+ }
61
+ if (descriptor.value && typeof descriptor.value === "object" && !Array.isArray(descriptor.value) && (Object.getPrototypeOf(descriptor.value) === Object.prototype || Object.getPrototypeOf(descriptor.value) === null)) {
62
+ makePropertiesEnumerable(descriptor.value);
63
+ }
64
+ }
65
+ }
66
+ };
47
67
  const toJSON = (from) => {
48
68
  toJsonWasCalled.add(from);
49
69
  const json = from.toJSON();
50
70
  toJsonWasCalled.delete(from);
71
+ if (json && typeof json === "object" && Object.isExtensible(json)) {
72
+ makePropertiesEnumerable(json);
73
+ }
51
74
  return json;
52
75
  };
53
76
  const serializeValue = (value, seen, depth, options) => {
@@ -58,7 +81,7 @@ const serializeValue = (value, seen, depth, options) => {
58
81
  return "[object Stream]";
59
82
  }
60
83
  if (value instanceof Error) {
61
- if (seen.includes(value)) {
84
+ if (seen.has(value)) {
62
85
  return "[Circular]";
63
86
  }
64
87
  depth += 1;
@@ -73,11 +96,14 @@ const serializeValue = (value, seen, depth, options) => {
73
96
  if (typeof value === "function") {
74
97
  return `[Function: ${value.name || "anonymous"}]`;
75
98
  }
99
+ if (typeof value === "bigint") {
100
+ return `${value}n`;
101
+ }
76
102
  if (isPlainObject(value)) {
77
- depth += 1;
78
- if (options.maxDepth && depth >= options.maxDepth) {
103
+ if (options.maxDepth !== void 0 && options.maxDepth !== Number.POSITIVE_INFINITY && depth + 1 >= options.maxDepth) {
79
104
  return {};
80
105
  }
106
+ depth += 1;
81
107
  const plainObject = {};
82
108
  for (const key in value) {
83
109
  plainObject[key] = serializeValue(value[key], seen, depth, options);
@@ -91,7 +117,7 @@ const serializeValue = (value, seen, depth, options) => {
91
117
  }
92
118
  };
93
119
  const _serialize = (error, options, seen, depth) => {
94
- seen.push(error);
120
+ seen.add(error);
95
121
  if (options.maxDepth === 0) {
96
122
  return {};
97
123
  }
@@ -99,31 +125,87 @@ const _serialize = (error, options, seen, depth) => {
99
125
  return toJSON(error);
100
126
  }
101
127
  const protoError = Object.create(ErrorProto);
102
- protoError.name = Object.prototype.toString.call(error.constructor) === "[object Function]" ? error.constructor.name : error.name;
103
- protoError.message = error.message;
104
- protoError.stack = error.stack;
128
+ Object.defineProperty(protoError, "name", {
129
+ configurable: true,
130
+ enumerable: true,
131
+ value: Object.prototype.toString.call(error.constructor) === "[object Function]" ? error.constructor.name : error.name,
132
+ writable: true
133
+ });
134
+ Object.defineProperty(protoError, "message", {
135
+ configurable: true,
136
+ enumerable: true,
137
+ value: error.message,
138
+ writable: true
139
+ });
140
+ Object.defineProperty(protoError, "stack", {
141
+ configurable: true,
142
+ enumerable: true,
143
+ value: error.stack,
144
+ writable: true
145
+ });
105
146
  if (Array.isArray(error.errors)) {
106
147
  const aggregateErrors = [];
107
148
  for (const aggregateError of error.errors) {
108
149
  if (!(aggregateError instanceof Error)) {
109
150
  throw new TypeError("All errors in the 'errors' property must be instances of Error");
110
151
  }
111
- if (seen.includes(aggregateError)) {
112
- protoError.errors = [];
152
+ if (seen.has(aggregateError)) {
153
+ Object.defineProperty(protoError, "errors", {
154
+ configurable: true,
155
+ enumerable: true,
156
+ value: [],
157
+ writable: true
158
+ });
113
159
  return protoError;
114
160
  }
115
161
  aggregateErrors.push(_serialize(aggregateError, options, seen, depth));
116
162
  }
117
- protoError.errors = aggregateErrors;
163
+ Object.defineProperty(protoError, "errors", {
164
+ configurable: true,
165
+ enumerable: true,
166
+ value: aggregateErrors,
167
+ writable: true
168
+ });
118
169
  }
119
- if (error.cause instanceof Error && !seen.includes(error.cause)) {
120
- protoError.cause = _serialize(error.cause, options, seen, depth);
170
+ if (error.cause !== void 0 && error.cause !== null) {
171
+ if (error.cause instanceof Error) {
172
+ if (seen.has(error.cause)) {
173
+ Object.defineProperty(protoError, "cause", {
174
+ configurable: true,
175
+ enumerable: true,
176
+ value: "[Circular]",
177
+ writable: true
178
+ });
179
+ } else {
180
+ Object.defineProperty(protoError, "cause", {
181
+ configurable: true,
182
+ enumerable: true,
183
+ value: _serialize(error.cause, options, seen, depth),
184
+ writable: true
185
+ });
186
+ }
187
+ } else {
188
+ const serializedCause = serializeValue(error.cause, seen, depth, options);
189
+ Object.defineProperty(protoError, "cause", {
190
+ configurable: true,
191
+ enumerable: true,
192
+ value: serializedCause,
193
+ writable: true
194
+ });
195
+ }
121
196
  }
122
197
  for (const key in error) {
123
- if (protoError[key] === void 0) {
124
- const value = error[key];
125
- protoError[key] = serializeValue(value, seen, depth, options);
198
+ if (key === "name" || key === "message" || key === "stack" || key === "cause" || key === "errors") {
199
+ continue;
126
200
  }
201
+ const value = error[key];
202
+ const serializedValue = serializeValue(value, seen, depth, options);
203
+ Object.defineProperty(protoError, key, {
204
+ configurable: true,
205
+ enumerable: true,
206
+ value: serializedValue,
207
+ writable: true
208
+ });
127
209
  }
128
210
  if (Array.isArray(options.exclude) && options.exclude.length > 0) {
129
211
  for (const key of options.exclude) {
@@ -142,7 +224,7 @@ const serialize = (error, options = {}) => _serialize(
142
224
  maxDepth: options.maxDepth ?? Number.POSITIVE_INFINITY,
143
225
  useToJSON: options.useToJSON ?? false
144
226
  },
145
- [],
227
+ /* @__PURE__ */ new Set(),
146
228
  0
147
229
  );
148
230
 
@@ -9,7 +9,7 @@ const {
9
9
  stderr
10
10
  } = __cjs_getProcess;
11
11
  import { w as writeStream } from './write-stream-BG8fhcs3.js';
12
- import { AbstractJsonReporter } from './AbstractJsonReporter-DWRpTtGw.js';
12
+ import { AbstractJsonReporter } from './AbstractJsonReporter-CGKHS8_M.js';
13
13
 
14
14
  class JsonReporter extends AbstractJsonReporter {
15
15
  /** Standard output stream */
@@ -1,4 +1,4 @@
1
- import { AbstractJsonReporter } from './AbstractJsonReporter-BaZ33PlE.js';
1
+ import { AbstractJsonReporter } from './AbstractJsonReporter-DDjDkciI.js';
2
2
  import { w as writeConsoleLogBasedOnLevel } from './write-console-log-based-on-level-DBmRYXpj.js';
3
3
 
4
4
  class JsonReporter extends AbstractJsonReporter {
@@ -1252,7 +1252,8 @@ class AbstractPrettyReporter {
1252
1252
  }
1253
1253
  }
1254
1254
 
1255
- const pailFileFilter = (line) => !/[\\/]pail[\\/]dist/.test(line);
1255
+ const PAIL_DIST_REGEX = /[\\/]pail[\\/]dist/;
1256
+ const pailFileFilter = (line) => !PAIL_DIST_REGEX.test(line);
1256
1257
  class PrettyReporter extends AbstractPrettyReporter {
1257
1258
  #stdout;
1258
1259
  #stderr;
@@ -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 };
@@ -0,0 +1,123 @@
1
+ import { WideEvent } from '../wide-event.js';
2
+
3
+ const patternToRegex = (pattern) => {
4
+ let regex = "^";
5
+ let index = 0;
6
+ while (index < pattern.length) {
7
+ const char = pattern[index];
8
+ if (char === "*" && pattern[index + 1] === "*") {
9
+ index += 2;
10
+ if (pattern[index] === "/") {
11
+ index += 1;
12
+ }
13
+ if (index >= pattern.length && regex.endsWith("/")) {
14
+ regex = `${regex.slice(0, -1)}(/.*)?`;
15
+ } else if (index >= pattern.length) {
16
+ regex += ".*";
17
+ } else {
18
+ regex += "(.*/)?";
19
+ }
20
+ } else {
21
+ switch (char) {
22
+ case "*": {
23
+ regex += "[^/]*";
24
+ index += 1;
25
+ break;
26
+ }
27
+ case ".": {
28
+ regex += String.raw`\.`;
29
+ index += 1;
30
+ break;
31
+ }
32
+ case "?": {
33
+ regex += "[^/]";
34
+ index += 1;
35
+ break;
36
+ }
37
+ default: {
38
+ regex += char;
39
+ index += 1;
40
+ }
41
+ }
42
+ }
43
+ }
44
+ regex += "$";
45
+ return new RegExp(regex);
46
+ };
47
+ const matchesPattern = (path, pattern) => patternToRegex(pattern).test(path);
48
+ const shouldLog = (path, include, exclude) => {
49
+ if (exclude?.some((pattern) => matchesPattern(path, pattern))) {
50
+ return false;
51
+ }
52
+ if (!include?.length) {
53
+ return true;
54
+ }
55
+ return include.some((pattern) => matchesPattern(path, pattern));
56
+ };
57
+ const getServiceForPath = (path, routes) => {
58
+ if (!routes) {
59
+ return void 0;
60
+ }
61
+ for (const [pattern, config] of Object.entries(routes)) {
62
+ if (matchesPattern(path, pattern)) {
63
+ return config.service;
64
+ }
65
+ }
66
+ return void 0;
67
+ };
68
+
69
+ const createMiddlewareLogger = (options, request) => {
70
+ const { exclude, include, pail, routes, service } = options;
71
+ const { headers, method, path, requestId } = request;
72
+ if (!shouldLog(path, include, exclude)) {
73
+ const noopLogger = new WideEvent({ autoEmit: false, name: `${method} ${path}`, pail });
74
+ return {
75
+ finish: () => {
76
+ },
77
+ logger: noopLogger,
78
+ skipped: true
79
+ };
80
+ }
81
+ const routeService = getServiceForPath(path, routes) ?? service;
82
+ const logger = new WideEvent({
83
+ name: `${method} ${path}`,
84
+ pail,
85
+ service: routeService
86
+ });
87
+ logger.set({
88
+ method,
89
+ path,
90
+ requestId,
91
+ ...headers ? { headers } : {}
92
+ });
93
+ const finish = (finishOptions) => {
94
+ logger.finish(finishOptions);
95
+ };
96
+ return {
97
+ finish,
98
+ logger,
99
+ skipped: false
100
+ };
101
+ };
102
+
103
+ const SENSITIVE_HEADERS = /* @__PURE__ */ new Set(["authorization", "cookie", "proxy-authorization", "set-cookie", "x-api-key", "x-auth-token"]);
104
+ const extractSafeHeaders = (headers) => {
105
+ const safe = {};
106
+ headers.forEach((value, key) => {
107
+ if (!SENSITIVE_HEADERS.has(key.toLowerCase())) {
108
+ safe[key.toLowerCase()] = value;
109
+ }
110
+ });
111
+ return safe;
112
+ };
113
+ const extractSafeNodeHeaders = (headers) => {
114
+ const safe = {};
115
+ for (const [key, value] of Object.entries(headers)) {
116
+ if (!SENSITIVE_HEADERS.has(key.toLowerCase()) && value !== void 0) {
117
+ safe[key.toLowerCase()] = Array.isArray(value) ? value.join(", ") : value;
118
+ }
119
+ }
120
+ return safe;
121
+ };
122
+
123
+ export { extractSafeHeaders as a, createMiddlewareLogger as c, extractSafeNodeHeaders as e };
@@ -0,0 +1,24 @@
1
+ const GLOB_STRIP_RE = /\*+/g;
2
+ const pailMiddleware = (NextResponseClass, options) => {
3
+ const { exclude, include } = options ?? {};
4
+ return (request) => {
5
+ const path = request.nextUrl.pathname;
6
+ if (exclude?.some((p) => path.startsWith(p.replaceAll(GLOB_STRIP_RE, "")))) {
7
+ return NextResponseClass.next();
8
+ }
9
+ if (include?.length && !include.some((p) => path.startsWith(p.replaceAll(GLOB_STRIP_RE, "")))) {
10
+ return NextResponseClass.next();
11
+ }
12
+ const requestId = request.headers.get("x-request-id") ?? crypto.randomUUID();
13
+ const requestHeaders = new Headers(request.headers);
14
+ requestHeaders.set("x-request-id", requestId);
15
+ requestHeaders.set("x-pail-start", String(Date.now()));
16
+ const response = NextResponseClass.next({
17
+ request: { headers: requestHeaders }
18
+ });
19
+ response.headers.set("x-request-id", requestId);
20
+ return response;
21
+ };
22
+ };
23
+
24
+ export { pailMiddleware };
@@ -0,0 +1,36 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ const {
21
+ AsyncLocalStorage
22
+ } = __cjs_getBuiltinModule("node:async_hooks");
23
+
24
+ const createLoggerStorage = (contextHint) => {
25
+ const storage = new AsyncLocalStorage();
26
+ const useLogger = () => {
27
+ const logger = storage.getStore();
28
+ if (!logger) {
29
+ throw new Error(`[pail] useLogger() called outside of ${contextHint}`);
30
+ }
31
+ return logger;
32
+ };
33
+ return { storage, useLogger };
34
+ };
35
+
36
+ export { createLoggerStorage as c };
@@ -0,0 +1,33 @@
1
+ import { createRequire as __cjs_createRequire } from "node:module";
2
+
3
+ const __cjs_require = __cjs_createRequire(import.meta.url);
4
+
5
+ const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
+
7
+ const __cjs_getBuiltinModule = (module) => {
8
+ // Check if we're in Node.js and version supports getBuiltinModule
9
+ if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
+ const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
+ // Node.js 20.16.0+ and 22.3.0+
12
+ if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
+ return __cjs_getProcess.getBuiltinModule(module);
14
+ }
15
+ }
16
+ // Fallback to createRequire
17
+ return __cjs_require(module);
18
+ };
19
+
20
+ const {
21
+ AsyncLocalStorage
22
+ } = __cjs_getBuiltinModule("node:async_hooks");
23
+
24
+ const pailStorage = new AsyncLocalStorage();
25
+ const useLogger = () => {
26
+ const logger = pailStorage.getStore();
27
+ if (!logger) {
28
+ throw new Error("[pail] useLogger() called outside of withPail() context. Wrap your route handler or server action with withPail().");
29
+ }
30
+ return logger;
31
+ };
32
+
33
+ export { pailStorage, useLogger };
@@ -0,0 +1,124 @@
1
+ import type { Meta, Processor } from "../types.d.ts";
2
+ /**
3
+ * Detected environment information.
4
+ *
5
+ * Contains runtime environment details that are automatically detected
6
+ * from environment variables and process information.
7
+ */
8
+ interface EnvironmentInfo {
9
+ /** Git commit hash or short SHA */
10
+ commit?: string;
11
+ /** Runtime environment (e.g., "production", "development", "test") */
12
+ environment?: string;
13
+ /** Hostname of the machine */
14
+ hostname?: string;
15
+ /** Process ID */
16
+ pid?: number;
17
+ /** Cloud region (e.g., "us-east-1") */
18
+ region?: string;
19
+ /** Application/service name */
20
+ service?: string;
21
+ /** Application version */
22
+ version?: string;
23
+ }
24
+ /**
25
+ * Environment processor configuration options.
26
+ */
27
+ interface EnvironmentProcessorOptions {
28
+ /**
29
+ * Whether to include the hostname. Defaults to false.
30
+ */
31
+ includeHostname?: boolean;
32
+ /**
33
+ * Whether to include the process ID. Defaults to false.
34
+ */
35
+ includePid?: boolean;
36
+ /**
37
+ * Static environment info to use instead of or in addition to auto-detection.
38
+ * Values provided here take precedence over auto-detected values.
39
+ */
40
+ overrides?: Partial<EnvironmentInfo>;
41
+ }
42
+ /**
43
+ * Detects the runtime environment from environment variables.
44
+ *
45
+ * Checks common environment variable patterns used by various hosting
46
+ * platforms (Vercel, AWS, GCP, Heroku, Railway, Fly.io, Render, etc.)
47
+ * to automatically determine service name, version, environment, region,
48
+ * and commit hash.
49
+ * @returns Detected environment information
50
+ */
51
+ declare const detectEnvironment: () => EnvironmentInfo;
52
+ /**
53
+ * Environment Enrichment Processor.
54
+ *
55
+ * Inspired by evlog's automatic environment detection, this processor
56
+ * enriches log metadata with runtime environment information. It auto-detects
57
+ * details like service name, version, environment, region, and commit hash
58
+ * from common environment variables used by popular hosting platforms.
59
+ *
60
+ * The detected info is added to the log's context, making it easier to
61
+ * correlate logs across services and deployments in production.
62
+ * @template L - The log level type
63
+ * @example
64
+ * ```typescript
65
+ * import { createPail } from "@visulima/pail";
66
+ * import EnvironmentProcessor from "@visulima/pail/processor/environment";
67
+ *
68
+ * const logger = createPail({
69
+ * processors: [
70
+ * new EnvironmentProcessor({
71
+ * overrides: { service: "my-api" },
72
+ * includePid: true,
73
+ * }),
74
+ * ],
75
+ * });
76
+ *
77
+ * logger.info("Server started");
78
+ * // Log metadata will include: { __env: { service: "my-api", environment: "production", ... } }
79
+ * ```
80
+ * @example
81
+ * ```typescript
82
+ * // With static configuration only (no auto-detection)
83
+ * new EnvironmentProcessor({
84
+ * overrides: {
85
+ * service: "payment-service",
86
+ * version: "2.1.0",
87
+ * environment: "staging",
88
+ * },
89
+ * });
90
+ * ```
91
+ */
92
+ declare class EnvironmentProcessor<L extends string = string> implements Processor<L> {
93
+ #private;
94
+ /**
95
+ * Creates a new EnvironmentProcessor instance.
96
+ *
97
+ * Auto-detects environment information on construction and merges
98
+ * with any provided overrides. Undefined values in overrides are
99
+ * filtered out so they don't overwrite detected values.
100
+ * @param options Processor configuration options
101
+ */
102
+ constructor(options?: EnvironmentProcessorOptions);
103
+ /**
104
+ * Processes log metadata to add environment information.
105
+ *
106
+ * Adds a `__env` property to the meta containing detected and
107
+ * configured environment details. Each call receives a shallow
108
+ * clone of the environment info to prevent cross-record mutation.
109
+ * @param meta The log metadata to process
110
+ * @returns The processed metadata with environment info added
111
+ */
112
+ process(meta: Meta<L>): Meta<L>;
113
+ /**
114
+ * Returns the detected environment information.
115
+ *
116
+ * Useful for inspecting what environment details were auto-detected.
117
+ * Returns a shallow clone to prevent external mutation.
118
+ * @returns The environment information object
119
+ */
120
+ getEnvironmentInfo(): Readonly<EnvironmentInfo>;
121
+ }
122
+ export { detectEnvironment };
123
+ export default EnvironmentProcessor;
124
+ export type { EnvironmentInfo, EnvironmentProcessorOptions };