@warlock.js/logger 4.0.174 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -422
- package/cjs/index.cjs +1003 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/channels/console-log.d.mts +40 -0
- package/esm/channels/console-log.d.mts.map +1 -0
- package/esm/channels/console-log.mjs +51 -0
- package/esm/channels/console-log.mjs.map +1 -0
- package/esm/channels/file-log.d.mts +194 -0
- package/esm/channels/file-log.d.mts.map +1 -0
- package/esm/channels/file-log.mjs +267 -0
- package/esm/channels/file-log.mjs.map +1 -0
- package/esm/channels/index.mjs +5 -0
- package/esm/channels/json-file-log.d.mts +33 -0
- package/esm/channels/json-file-log.d.mts.map +1 -0
- package/esm/channels/json-file-log.mjs +137 -0
- package/esm/channels/json-file-log.mjs.map +1 -0
- package/esm/index.d.mts +11 -0
- package/esm/index.mjs +13 -0
- package/esm/log-channel.d.mts +78 -0
- package/esm/log-channel.d.mts.map +1 -0
- package/esm/log-channel.mjs +75 -0
- package/esm/log-channel.mjs.map +1 -0
- package/esm/logger.d.mts +184 -0
- package/esm/logger.d.mts.map +1 -0
- package/esm/logger.mjs +282 -0
- package/esm/logger.mjs.map +1 -0
- package/esm/redact/redact.d.mts +25 -0
- package/esm/redact/redact.d.mts.map +1 -0
- package/esm/redact/redact.mjs +109 -0
- package/esm/redact/redact.mjs.map +1 -0
- package/esm/types.d.mts +129 -0
- package/esm/types.d.mts.map +1 -0
- package/esm/utils/capture-unhandled-errors.d.mts +16 -0
- package/esm/utils/capture-unhandled-errors.d.mts.map +1 -0
- package/esm/utils/capture-unhandled-errors.mjs +26 -0
- package/esm/utils/capture-unhandled-errors.mjs.map +1 -0
- package/esm/utils/clear-message.d.mts +8 -0
- package/esm/utils/clear-message.d.mts.map +1 -0
- package/esm/utils/clear-message.mjs +12 -0
- package/esm/utils/clear-message.mjs.map +1 -0
- package/esm/utils/index.mjs +5 -0
- package/esm/utils/safe-json-stringify.d.mts +14 -0
- package/esm/utils/safe-json-stringify.d.mts.map +1 -0
- package/esm/utils/safe-json-stringify.mjs +35 -0
- package/esm/utils/safe-json-stringify.mjs.map +1 -0
- package/llms-full.txt +1296 -0
- package/llms.txt +19 -0
- package/package.json +39 -39
- package/skills/capture-unhandled-errors/SKILL.md +103 -0
- package/skills/configure-logger/SKILL.md +105 -0
- package/skills/filter-log-entries/SKILL.md +120 -0
- package/skills/flush-logs-on-shutdown/SKILL.md +91 -0
- package/skills/logger-basics/SKILL.md +85 -0
- package/skills/overview/SKILL.md +86 -0
- package/skills/pick-log-channel/SKILL.md +139 -0
- package/skills/redact-sensitive-log-fields/SKILL.md +122 -0
- package/skills/test-logging-code/SKILL.md +169 -0
- package/skills/use-log-helpers/SKILL.md +66 -0
- package/skills/write-custom-log-channel/SKILL.md +160 -0
- package/cjs/channels/console-log.d.ts +0 -17
- package/cjs/channels/console-log.d.ts.map +0 -1
- package/cjs/channels/console-log.js +0 -47
- package/cjs/channels/console-log.js.map +0 -1
- package/cjs/channels/file-log.d.ts +0 -171
- package/cjs/channels/file-log.d.ts.map +0 -1
- package/cjs/channels/file-log.js +0 -293
- package/cjs/channels/file-log.js.map +0 -1
- package/cjs/channels/index.d.ts +0 -4
- package/cjs/channels/index.d.ts.map +0 -1
- package/cjs/channels/json-file-log.d.ts +0 -33
- package/cjs/channels/json-file-log.d.ts.map +0 -1
- package/cjs/channels/json-file-log.js +0 -164
- package/cjs/channels/json-file-log.js.map +0 -1
- package/cjs/index.d.ts +0 -6
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -1
- package/cjs/index.js.map +0 -1
- package/cjs/log-channel.d.ts +0 -67
- package/cjs/log-channel.d.ts.map +0 -1
- package/cjs/log-channel.js +0 -88
- package/cjs/log-channel.js.map +0 -1
- package/cjs/logger.d.ts +0 -62
- package/cjs/logger.d.ts.map +0 -1
- package/cjs/logger.js +0 -124
- package/cjs/logger.js.map +0 -1
- package/cjs/types.d.ts +0 -104
- package/cjs/types.d.ts.map +0 -1
- package/cjs/utils/capture-unhandled-errors.d.ts +0 -2
- package/cjs/utils/capture-unhandled-errors.d.ts.map +0 -1
- package/cjs/utils/capture-unhandled-errors.js +0 -12
- package/cjs/utils/capture-unhandled-errors.js.map +0 -1
- package/cjs/utils/clear-message.d.ts +0 -5
- package/cjs/utils/clear-message.d.ts.map +0 -1
- package/cjs/utils/clear-message.js +0 -9
- package/cjs/utils/clear-message.js.map +0 -1
- package/cjs/utils/index.d.ts +0 -3
- package/cjs/utils/index.d.ts.map +0 -1
- package/esm/channels/console-log.d.ts +0 -17
- package/esm/channels/console-log.d.ts.map +0 -1
- package/esm/channels/console-log.js +0 -47
- package/esm/channels/console-log.js.map +0 -1
- package/esm/channels/file-log.d.ts +0 -171
- package/esm/channels/file-log.d.ts.map +0 -1
- package/esm/channels/file-log.js +0 -293
- package/esm/channels/file-log.js.map +0 -1
- package/esm/channels/index.d.ts +0 -4
- package/esm/channels/index.d.ts.map +0 -1
- package/esm/channels/json-file-log.d.ts +0 -33
- package/esm/channels/json-file-log.d.ts.map +0 -1
- package/esm/channels/json-file-log.js +0 -164
- package/esm/channels/json-file-log.js.map +0 -1
- package/esm/index.d.ts +0 -6
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -1
- package/esm/index.js.map +0 -1
- package/esm/log-channel.d.ts +0 -67
- package/esm/log-channel.d.ts.map +0 -1
- package/esm/log-channel.js +0 -88
- package/esm/log-channel.js.map +0 -1
- package/esm/logger.d.ts +0 -62
- package/esm/logger.d.ts.map +0 -1
- package/esm/logger.js +0 -124
- package/esm/logger.js.map +0 -1
- package/esm/types.d.ts +0 -104
- package/esm/types.d.ts.map +0 -1
- package/esm/utils/capture-unhandled-errors.d.ts +0 -2
- package/esm/utils/capture-unhandled-errors.d.ts.map +0 -1
- package/esm/utils/capture-unhandled-errors.js +0 -12
- package/esm/utils/capture-unhandled-errors.js.map +0 -1
- package/esm/utils/clear-message.d.ts +0 -5
- package/esm/utils/clear-message.d.ts.map +0 -1
- package/esm/utils/clear-message.js +0 -9
- package/esm/utils/clear-message.js.map +0 -1
- package/esm/utils/index.d.ts +0 -3
- package/esm/utils/index.d.ts.map +0 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
//#region ../../@warlock.js/logger/src/redact/redact.ts
|
|
2
|
+
/**
|
|
3
|
+
* Deep-clone a value with structural fidelity for log entries — handles plain
|
|
4
|
+
* objects, arrays, `Date`, `Error`, and primitives. Anything else is copied
|
|
5
|
+
* by reference (we only redact paths through plain objects/arrays anyway,
|
|
6
|
+
* and rebuilding e.g. a `Buffer` would change semantics).
|
|
7
|
+
*
|
|
8
|
+
* Purpose-built rather than reaching for `structuredClone`: `Error` instances
|
|
9
|
+
* lose their `message`/`stack` under `structuredClone` in some Node versions,
|
|
10
|
+
* and the logger pipeline carries them often.
|
|
11
|
+
*/
|
|
12
|
+
function cloneEntry(value, seen = /* @__PURE__ */ new WeakMap()) {
|
|
13
|
+
if (value === null || typeof value !== "object") return value;
|
|
14
|
+
if (seen.has(value)) return seen.get(value);
|
|
15
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
16
|
+
if (value instanceof Error) {
|
|
17
|
+
const copy = new value.constructor(value.message);
|
|
18
|
+
copy.stack = value.stack;
|
|
19
|
+
copy.name = value.name;
|
|
20
|
+
return copy;
|
|
21
|
+
}
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
const arr = [];
|
|
24
|
+
seen.set(value, arr);
|
|
25
|
+
for (const item of value) arr.push(cloneEntry(item, seen));
|
|
26
|
+
return arr;
|
|
27
|
+
}
|
|
28
|
+
const out = {};
|
|
29
|
+
seen.set(value, out);
|
|
30
|
+
for (const key of Object.keys(value)) out[key] = cloneEntry(value[key], seen);
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Apply a single censor decision to a value. String censors are returned
|
|
35
|
+
* verbatim; function censors receive the original value plus the dotted
|
|
36
|
+
* path so callers can implement value-aware redaction (mask all but the
|
|
37
|
+
* last 4 chars, hash, etc.).
|
|
38
|
+
*/
|
|
39
|
+
function applyCensor(value, censor, path) {
|
|
40
|
+
if (typeof censor === "function") return censor(value, path.join("."));
|
|
41
|
+
return censor;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Walk `target` following the remaining `segments` of a path pattern,
|
|
45
|
+
* replacing matched leaves via `censor`. Operates in place — the caller
|
|
46
|
+
* is responsible for cloning before calling.
|
|
47
|
+
*
|
|
48
|
+
* Wildcards:
|
|
49
|
+
* - `*` matches exactly one segment (any key on a plain object, any index
|
|
50
|
+
* on an array — stringified for the path that's passed to a function
|
|
51
|
+
* censor).
|
|
52
|
+
* - `**` matches zero or more segments greedily; the rest of the pattern
|
|
53
|
+
* is then attempted at the current level and at every descendant.
|
|
54
|
+
*/
|
|
55
|
+
function redactAtPath(target, segments, censor, pathTrail) {
|
|
56
|
+
if (target === null || typeof target !== "object") return;
|
|
57
|
+
if (segments.length === 0) return;
|
|
58
|
+
const [head, ...rest] = segments;
|
|
59
|
+
if (head === "**") {
|
|
60
|
+
if (rest.length > 0) redactAtPath(target, rest, censor, pathTrail);
|
|
61
|
+
const keys = Array.isArray(target) ? target.map((_, index) => String(index)) : Object.keys(target);
|
|
62
|
+
for (const key of keys) redactAtPath(target[key], segments, censor, [...pathTrail, key]);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const keysToVisit = head === "*" ? Array.isArray(target) ? target.map((_, index) => String(index)) : Object.keys(target) : Array.isArray(target) ? /^\d+$/.test(head) && Number(head) < target.length ? [head] : [] : Object.prototype.hasOwnProperty.call(target, head) ? [head] : [];
|
|
66
|
+
for (const key of keysToVisit) if (rest.length === 0) target[key] = applyCensor(target[key], censor, [...pathTrail, key]);
|
|
67
|
+
else redactAtPath(target[key], rest, censor, [...pathTrail, key]);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Produce a new `LoggingData` with every path in `config.paths` replaced
|
|
71
|
+
* by `config.censor`. The original entry is never mutated — channels and
|
|
72
|
+
* other call sites can hold references to the input safely.
|
|
73
|
+
*
|
|
74
|
+
* No-op (returns the input by reference) when `config` is `undefined` or
|
|
75
|
+
* its `paths` array is empty, so the fast path stays fast.
|
|
76
|
+
*/
|
|
77
|
+
function applyRedact(data, config) {
|
|
78
|
+
if (!config || config.paths.length === 0) return data;
|
|
79
|
+
const censor = config.censor ?? "[REDACTED]";
|
|
80
|
+
const cloned = cloneEntry(data);
|
|
81
|
+
for (const pattern of config.paths) {
|
|
82
|
+
const segments = pattern.split(".").filter((segment) => segment.length > 0);
|
|
83
|
+
if (segments.length === 0) continue;
|
|
84
|
+
redactAtPath(cloned, segments, censor, []);
|
|
85
|
+
}
|
|
86
|
+
return cloned;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Combine two redact configs into one effective config. Used to merge a
|
|
90
|
+
* channel's additive paths on top of the logger-wide floor.
|
|
91
|
+
*
|
|
92
|
+
* - `paths` are concatenated; duplicates are kept (the matcher tolerates
|
|
93
|
+
* them, and de-duping cross-config would mask a developer typo).
|
|
94
|
+
* - `censor` from the channel wins; falls back to the logger's; falls back
|
|
95
|
+
* to the default `"[REDACTED]"`.
|
|
96
|
+
*/
|
|
97
|
+
function mergeRedact(base, extra) {
|
|
98
|
+
if (!base && !extra) return void 0;
|
|
99
|
+
if (!base) return extra;
|
|
100
|
+
if (!extra) return base;
|
|
101
|
+
return {
|
|
102
|
+
paths: [...base.paths, ...extra.paths],
|
|
103
|
+
censor: extra.censor ?? base.censor
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { applyRedact, mergeRedact };
|
|
109
|
+
//# sourceMappingURL=redact.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.mjs","names":[],"sources":["../../../../../../@warlock.js/logger/src/redact/redact.ts"],"sourcesContent":["import type { LoggingData, RedactCensor, RedactConfig } from \"../types\";\n\n/**\n * Deep-clone a value with structural fidelity for log entries — handles plain\n * objects, arrays, `Date`, `Error`, and primitives. Anything else is copied\n * by reference (we only redact paths through plain objects/arrays anyway,\n * and rebuilding e.g. a `Buffer` would change semantics).\n *\n * Purpose-built rather than reaching for `structuredClone`: `Error` instances\n * lose their `message`/`stack` under `structuredClone` in some Node versions,\n * and the logger pipeline carries them often.\n */\nfunction cloneEntry<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (value === null || typeof value !== \"object\") {\n return value;\n }\n\n if (seen.has(value as unknown as object)) {\n return seen.get(value as unknown as object);\n }\n\n if (value instanceof Date) {\n return new Date(value.getTime()) as unknown as T;\n }\n\n if (value instanceof Error) {\n const copy = new (value.constructor as ErrorConstructor)(value.message);\n copy.stack = value.stack;\n copy.name = value.name;\n return copy as unknown as T;\n }\n\n if (Array.isArray(value)) {\n const arr: any[] = [];\n seen.set(value as unknown as object, arr);\n for (const item of value) {\n arr.push(cloneEntry(item, seen));\n }\n return arr as unknown as T;\n }\n\n const out: Record<string, any> = {};\n seen.set(value as unknown as object, out);\n for (const key of Object.keys(value as Record<string, any>)) {\n out[key] = cloneEntry((value as Record<string, any>)[key], seen);\n }\n return out as unknown as T;\n}\n\n/**\n * Apply a single censor decision to a value. String censors are returned\n * verbatim; function censors receive the original value plus the dotted\n * path so callers can implement value-aware redaction (mask all but the\n * last 4 chars, hash, etc.).\n */\nfunction applyCensor(value: any, censor: RedactCensor, path: string[]): any {\n if (typeof censor === \"function\") {\n return censor(value, path.join(\".\"));\n }\n return censor;\n}\n\n/**\n * Walk `target` following the remaining `segments` of a path pattern,\n * replacing matched leaves via `censor`. Operates in place — the caller\n * is responsible for cloning before calling.\n *\n * Wildcards:\n * - `*` matches exactly one segment (any key on a plain object, any index\n * on an array — stringified for the path that's passed to a function\n * censor).\n * - `**` matches zero or more segments greedily; the rest of the pattern\n * is then attempted at the current level and at every descendant.\n */\nfunction redactAtPath(\n target: any,\n segments: string[],\n censor: RedactCensor,\n pathTrail: string[],\n): void {\n if (target === null || typeof target !== \"object\") {\n return;\n }\n\n if (segments.length === 0) {\n return;\n }\n\n const [head, ...rest] = segments;\n\n if (head === \"**\") {\n // Try matching `rest` at the current level (the zero-segment match\n // case), then recurse into every child carrying the `**` forward so\n // it keeps matching at deeper levels too.\n if (rest.length > 0) {\n redactAtPath(target, rest, censor, pathTrail);\n }\n const keys = Array.isArray(target)\n ? target.map((_, index) => String(index))\n : Object.keys(target);\n for (const key of keys) {\n redactAtPath(target[key], segments, censor, [...pathTrail, key]);\n }\n return;\n }\n\n const keysToVisit =\n head === \"*\"\n ? Array.isArray(target)\n ? target.map((_, index) => String(index))\n : Object.keys(target)\n : Array.isArray(target)\n ? // Numeric segment can index into an array.\n /^\\d+$/.test(head) && Number(head) < target.length\n ? [head]\n : []\n : Object.prototype.hasOwnProperty.call(target, head)\n ? [head]\n : [];\n\n for (const key of keysToVisit) {\n if (rest.length === 0) {\n target[key] = applyCensor(target[key], censor, [...pathTrail, key]);\n } else {\n redactAtPath(target[key], rest, censor, [...pathTrail, key]);\n }\n }\n}\n\n/**\n * Produce a new `LoggingData` with every path in `config.paths` replaced\n * by `config.censor`. The original entry is never mutated — channels and\n * other call sites can hold references to the input safely.\n *\n * No-op (returns the input by reference) when `config` is `undefined` or\n * its `paths` array is empty, so the fast path stays fast.\n */\nexport function applyRedact(\n data: LoggingData,\n config: RedactConfig | undefined,\n): LoggingData {\n if (!config || config.paths.length === 0) {\n return data;\n }\n\n const censor = config.censor ?? \"[REDACTED]\";\n const cloned = cloneEntry(data);\n\n for (const pattern of config.paths) {\n const segments = pattern.split(\".\").filter((segment) => segment.length > 0);\n if (segments.length === 0) continue;\n redactAtPath(cloned, segments, censor, []);\n }\n\n return cloned;\n}\n\n/**\n * Combine two redact configs into one effective config. Used to merge a\n * channel's additive paths on top of the logger-wide floor.\n *\n * - `paths` are concatenated; duplicates are kept (the matcher tolerates\n * them, and de-duping cross-config would mask a developer typo).\n * - `censor` from the channel wins; falls back to the logger's; falls back\n * to the default `\"[REDACTED]\"`.\n */\nexport function mergeRedact(\n base: RedactConfig | undefined,\n extra: RedactConfig | undefined,\n): RedactConfig | undefined {\n if (!base && !extra) return undefined;\n if (!base) return extra;\n if (!extra) return base;\n\n return {\n paths: [...base.paths, ...extra.paths],\n censor: extra.censor ?? base.censor,\n };\n}\n"],"mappings":"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"}
|
package/esm/types.d.mts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
//#region ../../@warlock.js/logger/src/types.d.ts
|
|
2
|
+
type LogLevel = "debug" | "info" | "warn" | "error" | "success";
|
|
3
|
+
/**
|
|
4
|
+
* Process-level events that `Logger.enableAutoFlush()` can hook to drain
|
|
5
|
+
* buffered channels before the process terminates.
|
|
6
|
+
*
|
|
7
|
+
* - Signals (`SIGINT`, `SIGTERM`, `SIGHUP`, `SIGBREAK`, `SIGUSR2`) are flushed
|
|
8
|
+
* then re-raised so Node's default exit behavior runs.
|
|
9
|
+
* - `beforeExit` is flushed in place — Node exits on its own afterwards.
|
|
10
|
+
*/
|
|
11
|
+
type AutoFlushEvent = "SIGINT" | "SIGTERM" | "SIGHUP" | "SIGBREAK" | "SIGUSR2" | "beforeExit";
|
|
12
|
+
type DebugMode = "daily" | "monthly" | "yearly" | "hourly";
|
|
13
|
+
/**
|
|
14
|
+
* Replacement value used by `RedactConfig`. Either a literal string
|
|
15
|
+
* (e.g. `"[REDACTED]"`) or a function that receives the original value plus
|
|
16
|
+
* the dotted path it sits at and returns whatever should replace it.
|
|
17
|
+
*/
|
|
18
|
+
type RedactCensor = string | ((value: any, path: string) => any);
|
|
19
|
+
/**
|
|
20
|
+
* Strip sensitive fields from log entries before they reach a channel.
|
|
21
|
+
*
|
|
22
|
+
* Paths are dotted glob patterns evaluated against the `LoggingData` itself —
|
|
23
|
+
* use `context.password`, `message.token`, etc. Wildcards:
|
|
24
|
+
*
|
|
25
|
+
* - `*` — matches a single segment (any one key)
|
|
26
|
+
* - `**` — matches zero or more segments (any depth, any key)
|
|
27
|
+
*
|
|
28
|
+
* Configurable in two places:
|
|
29
|
+
*
|
|
30
|
+
* 1. **Logger-wide** via `Logger.configure({ redact })` — applied once before
|
|
31
|
+
* fan-out. This is the security floor; no channel can undo it.
|
|
32
|
+
* 2. **Per channel** via the channel's options. Channel paths are *additive*:
|
|
33
|
+
* they extend (never replace) the logger-wide list, so a channel can only
|
|
34
|
+
* redact more, never less.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* logger.configure({
|
|
38
|
+
* redact: {
|
|
39
|
+
* paths: ["context.password", "context.*.token", "context.headers.authorization"],
|
|
40
|
+
* censor: "[REDACTED]",
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
*/
|
|
44
|
+
type RedactConfig = {
|
|
45
|
+
/**
|
|
46
|
+
* Glob path patterns to redact. Paths are evaluated against the full
|
|
47
|
+
* `LoggingData` object — so prefix with `context.` or `message.` to scope
|
|
48
|
+
* to either field.
|
|
49
|
+
*/
|
|
50
|
+
paths: string[];
|
|
51
|
+
/**
|
|
52
|
+
* Replacement applied at each matched path.
|
|
53
|
+
*
|
|
54
|
+
* @default "[REDACTED]"
|
|
55
|
+
*/
|
|
56
|
+
censor?: RedactCensor;
|
|
57
|
+
};
|
|
58
|
+
type BasicLogConfigurations = {
|
|
59
|
+
/**
|
|
60
|
+
* Set what level of logs should be logged
|
|
61
|
+
*
|
|
62
|
+
* @default all
|
|
63
|
+
*/
|
|
64
|
+
levels?: LogLevel[];
|
|
65
|
+
/**
|
|
66
|
+
* Date and time format
|
|
67
|
+
*/
|
|
68
|
+
dateFormat?: {
|
|
69
|
+
date?: string;
|
|
70
|
+
time?: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Advanced filter to determine if the message should be logged or not
|
|
74
|
+
*/
|
|
75
|
+
filter?: (data: LoggingData) => boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Add additional context to the log
|
|
78
|
+
*/
|
|
79
|
+
context?: (data: LoggingData) => Promise<Record<string, any>>;
|
|
80
|
+
/**
|
|
81
|
+
* Channel-specific redaction. Additive on top of the logger-wide config —
|
|
82
|
+
* the channel's paths extend (never replace) the logger floor. The
|
|
83
|
+
* `censor` here, when omitted, falls back to the logger-wide censor.
|
|
84
|
+
*/
|
|
85
|
+
redact?: RedactConfig;
|
|
86
|
+
};
|
|
87
|
+
type LogMessage = {
|
|
88
|
+
content: string;
|
|
89
|
+
level: LogLevel;
|
|
90
|
+
date: string;
|
|
91
|
+
module: string;
|
|
92
|
+
action: string;
|
|
93
|
+
stack?: string;
|
|
94
|
+
context?: Record<string, any>;
|
|
95
|
+
timestamp?: string;
|
|
96
|
+
};
|
|
97
|
+
interface LogContract {
|
|
98
|
+
/**
|
|
99
|
+
* Channel name
|
|
100
|
+
*/
|
|
101
|
+
name: string;
|
|
102
|
+
/**
|
|
103
|
+
* Channel description
|
|
104
|
+
*/
|
|
105
|
+
description?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Determine if channel is logging in terminal
|
|
108
|
+
*/
|
|
109
|
+
terminal?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Log the given message
|
|
112
|
+
*/
|
|
113
|
+
log(data: LoggingData): void | Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Synchronously flush logs
|
|
116
|
+
*/
|
|
117
|
+
flushSync?(): void;
|
|
118
|
+
}
|
|
119
|
+
type LoggingData = {
|
|
120
|
+
type: "info" | "debug" | "warn" | "error" | "success";
|
|
121
|
+
module: string;
|
|
122
|
+
action: string;
|
|
123
|
+
message: any;
|
|
124
|
+
context?: Record<string, any>;
|
|
125
|
+
};
|
|
126
|
+
type OmittedLoggingData = Omit<LoggingData, "type">;
|
|
127
|
+
//#endregion
|
|
128
|
+
export { AutoFlushEvent, BasicLogConfigurations, DebugMode, LogContract, LogLevel, LogMessage, LoggingData, OmittedLoggingData, RedactCensor, RedactConfig };
|
|
129
|
+
//# sourceMappingURL=types.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../@warlock.js/logger/src/types.ts"],"mappings":";KAAY,QAAA;AAAZ;;;;AAAoB;AAUpB;;;AAVA,KAUY,cAAA;AAAA,KAQA,SAAA;AAAZ;;;;AAAqB;AAArB,KAOY,YAAA,cAEN,KAAA,OAAY,IAAY;;;;AAAA;AA2B9B;;;;;;;;AAYuB;AAGvB;;;;;;;;;;;;KAfY,YAAA;EAyBV;;;;;EAnBA,KAAA;EA8BA;;;;;EAxBA,MAAA,GAAS,YAAY;AAAA;AAAA,KAGX,sBAAA;EA2BW;AAGvB;;;;EAxBE,MAAA,GAAS,QAAA;EA0BT;;;EAtBA,UAAA;IACE,IAAA;IACA,IAAA;EAAA;EAyBQ;;;EApBV,MAAA,IAAU,IAAA,EAAM,WAAA;EAwBD;;;EApBf,OAAA,IAAW,IAAA,EAAM,WAAA,KAAgB,OAAA,CAAQ,MAAA;EAwBzC;;;;;EAlBA,MAAA,GAAS,YAAA;AAAA;AAAA,KAGC,UAAA;EACV,OAAA;EACA,KAAA,EAAO,QAAA;EACP,IAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA;EACA,OAAA,GAAU,MAAM;EAChB,SAAA;AAAA;AAAA,UAGe,WAAA;EAgCf;;;EA5BA,IAAA;EA+BU;;;EA1BV,WAAA;EA0B+C;;;EArB/C,QAAA;;;;EAKA,GAAA,CAAI,IAAA,EAAM,WAAA,UAAqB,OAAO;;;;EAKtC,SAAA;AAAA;AAAA,KAGU,WAAA;EACV,IAAA;EACA,MAAA;EACA,MAAA;EACA,OAAA;EACA,OAAA,GAAU,MAAM;AAAA;AAAA,KAGN,kBAAA,GAAqB,IAAI,CAAC,WAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region ../../@warlock.js/logger/src/utils/capture-unhandled-errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Route Node's process-level failure events through the logger so they land in
|
|
4
|
+
* every configured channel with full stack context. Registers one listener for
|
|
5
|
+
* `unhandledRejection` and one for `uncaughtException`; call once at startup
|
|
6
|
+
* after channels are configured. Pair with `autoFlushOn: ["beforeExit"]` so the
|
|
7
|
+
* final entry survives the process exit that follows an uncaught exception.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* log.configure({ channels: [new ConsoleLog(), new FileLog()] });
|
|
11
|
+
* captureAnyUnhandledRejection();
|
|
12
|
+
*/
|
|
13
|
+
declare function captureAnyUnhandledRejection(): void;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { captureAnyUnhandledRejection };
|
|
16
|
+
//# sourceMappingURL=capture-unhandled-errors.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-unhandled-errors.d.mts","names":[],"sources":["../../../../../../@warlock.js/logger/src/utils/capture-unhandled-errors.ts"],"mappings":";;AAaA;;;;AAA4C;;;;;;iBAA5B,4BAAA,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { log } from "../logger.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../../@warlock.js/logger/src/utils/capture-unhandled-errors.ts
|
|
4
|
+
/**
|
|
5
|
+
* Route Node's process-level failure events through the logger so they land in
|
|
6
|
+
* every configured channel with full stack context. Registers one listener for
|
|
7
|
+
* `unhandledRejection` and one for `uncaughtException`; call once at startup
|
|
8
|
+
* after channels are configured. Pair with `autoFlushOn: ["beforeExit"]` so the
|
|
9
|
+
* final entry survives the process exit that follows an uncaught exception.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* log.configure({ channels: [new ConsoleLog(), new FileLog()] });
|
|
13
|
+
* captureAnyUnhandledRejection();
|
|
14
|
+
*/
|
|
15
|
+
function captureAnyUnhandledRejection() {
|
|
16
|
+
process.on("unhandledRejection", (reason) => {
|
|
17
|
+
log.error("app", "unhandledRejection", reason);
|
|
18
|
+
});
|
|
19
|
+
process.on("uncaughtException", (error) => {
|
|
20
|
+
log.error("app", "uncaughtException", error);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { captureAnyUnhandledRejection };
|
|
26
|
+
//# sourceMappingURL=capture-unhandled-errors.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-unhandled-errors.mjs","names":[],"sources":["../../../../../../@warlock.js/logger/src/utils/capture-unhandled-errors.ts"],"sourcesContent":["import { log } from \"../logger\";\r\n\r\n/**\r\n * Route Node's process-level failure events through the logger so they land in\r\n * every configured channel with full stack context. Registers one listener for\r\n * `unhandledRejection` and one for `uncaughtException`; call once at startup\r\n * after channels are configured. Pair with `autoFlushOn: [\"beforeExit\"]` so the\r\n * final entry survives the process exit that follows an uncaught exception.\r\n *\r\n * @example\r\n * log.configure({ channels: [new ConsoleLog(), new FileLog()] });\r\n * captureAnyUnhandledRejection();\r\n */\r\nexport function captureAnyUnhandledRejection() {\r\n process.on(\"unhandledRejection\", (reason: any) => {\r\n log.error(\"app\", \"unhandledRejection\", reason);\r\n });\r\n\r\n process.on(\"uncaughtException\", (error) => {\r\n log.error(\"app\", \"uncaughtException\", error);\r\n });\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,+BAA+B;CAC7C,QAAQ,GAAG,uBAAuB,WAAgB;EAChD,IAAI,MAAM,OAAO,sBAAsB,MAAM;CAC/C,CAAC;CAED,QAAQ,GAAG,sBAAsB,UAAU;EACzC,IAAI,MAAM,OAAO,qBAAqB,KAAK;CAC7C,CAAC;AACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clear-message.d.mts","names":[],"sources":["../../../../../../@warlock.js/logger/src/utils/clear-message.ts"],"mappings":";;AAGA;;iBAAgB,YAAA,CAAa,OAAY"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region ../../@warlock.js/logger/src/utils/clear-message.ts
|
|
2
|
+
/**
|
|
3
|
+
* Clear message from any terminal codes
|
|
4
|
+
*/
|
|
5
|
+
function clearMessage(message) {
|
|
6
|
+
if (typeof message !== "string") return message;
|
|
7
|
+
return message.replace(/\u001b[^m]*?m/g, "");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { clearMessage };
|
|
12
|
+
//# sourceMappingURL=clear-message.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clear-message.mjs","names":[],"sources":["../../../../../../@warlock.js/logger/src/utils/clear-message.ts"],"sourcesContent":["/**\r\n * Clear message from any terminal codes\r\n */\r\nexport function clearMessage(message: any) {\r\n if (typeof message !== \"string\") return message;\r\n\r\n // eslint-disable-next-line no-control-regex\r\n return message.replace(/\\u001b[^m]*?m/g, \"\");\r\n}\r\n"],"mappings":";;;;AAGA,SAAgB,aAAa,SAAc;CACzC,IAAI,OAAO,YAAY,UAAU,OAAO;CAGxC,OAAO,QAAQ,QAAQ,kBAAkB,EAAE;AAC7C"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region ../../@warlock.js/logger/src/utils/safe-json-stringify.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* JSON-serialize log payloads safely. Circular refs, BigInt, and repeated
|
|
4
|
+
* non-tree references are handled by `safe-stable-stringify`; functions and
|
|
5
|
+
* symbols are dropped (standard JSON behavior); Errors are expanded via
|
|
6
|
+
* `errorReplacer`. Class instances serialize as their enumerable props.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* await fs.promises.writeFile(filePath, safeJsonStringify(payload, 2));
|
|
10
|
+
*/
|
|
11
|
+
declare function safeJsonStringify(value: unknown, space?: number): string;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { safeJsonStringify };
|
|
14
|
+
//# sourceMappingURL=safe-json-stringify.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-json-stringify.d.mts","names":[],"sources":["../../../../../../@warlock.js/logger/src/utils/safe-json-stringify.ts"],"mappings":";;AAyCA;;;;AAAgE;;;;iBAAhD,iBAAA,CAAkB,KAAA,WAAgB,KAAc"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { stringify } from "safe-stable-stringify";
|
|
2
|
+
|
|
3
|
+
//#region ../../@warlock.js/logger/src/utils/safe-json-stringify.ts
|
|
4
|
+
/**
|
|
5
|
+
* Replacer that surfaces Error data — `name`, `message`, and `stack` are
|
|
6
|
+
* non-enumerable on `Error`, so neither default JSON serialization nor an
|
|
7
|
+
* object spread captures them (both produce `{}`). They are copied explicitly;
|
|
8
|
+
* the trailing spread then captures any additional enumerable props the caller
|
|
9
|
+
* (or a subclass) attached, such as a `code` field.
|
|
10
|
+
*/
|
|
11
|
+
function errorReplacer(_key, value) {
|
|
12
|
+
if (value instanceof Error) return {
|
|
13
|
+
...value,
|
|
14
|
+
name: value.name,
|
|
15
|
+
message: value.message,
|
|
16
|
+
stack: value.stack
|
|
17
|
+
};
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* JSON-serialize log payloads safely. Circular refs, BigInt, and repeated
|
|
22
|
+
* non-tree references are handled by `safe-stable-stringify`; functions and
|
|
23
|
+
* symbols are dropped (standard JSON behavior); Errors are expanded via
|
|
24
|
+
* `errorReplacer`. Class instances serialize as their enumerable props.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* await fs.promises.writeFile(filePath, safeJsonStringify(payload, 2));
|
|
28
|
+
*/
|
|
29
|
+
function safeJsonStringify(value, space) {
|
|
30
|
+
return stringify(value, errorReplacer, space) ?? "";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { safeJsonStringify };
|
|
35
|
+
//# sourceMappingURL=safe-json-stringify.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-json-stringify.mjs","names":["safeStableStringify"],"sources":["../../../../../../@warlock.js/logger/src/utils/safe-json-stringify.ts"],"sourcesContent":["import { stringify as safeStableStringify } from \"safe-stable-stringify\";\n\n/**\n * Replacer that surfaces Error data — `name`, `message`, and `stack` are\n * non-enumerable on `Error`, so neither default JSON serialization nor an\n * object spread captures them (both produce `{}`). They are copied explicitly;\n * the trailing spread then captures any additional enumerable props the caller\n * (or a subclass) attached, such as a `code` field.\n */\nfunction errorReplacer<Value = unknown>(\n _key: string,\n value: Value,\n): Record<string, unknown> | Value {\n if (value instanceof Error) {\n // Spread first, then the explicit Error fields. `name`/`message`/`stack`\n // are non-enumerable on `Error`, so the spread never carries them — the\n // explicit assignments are what surface them. Placing the spread first\n // means those explicit keys legitimately take precedence over any\n // identically-named *enumerable* prop a subclass attached (resolving the\n // duplicate-key warning). Because `safe-stable-stringify` sorts keys, the\n // insertion order here does not affect the serialized bytes.\n return {\n ...value,\n name: value.name,\n message: value.message,\n stack: value.stack,\n };\n }\n\n return value;\n}\n\n/**\n * JSON-serialize log payloads safely. Circular refs, BigInt, and repeated\n * non-tree references are handled by `safe-stable-stringify`; functions and\n * symbols are dropped (standard JSON behavior); Errors are expanded via\n * `errorReplacer`. Class instances serialize as their enumerable props.\n *\n * @example\n * await fs.promises.writeFile(filePath, safeJsonStringify(payload, 2));\n */\nexport function safeJsonStringify(value: unknown, space?: number): string {\n return safeStableStringify(value, errorReplacer, space) ?? \"\";\n}\n"],"mappings":";;;;;;;;;;AASA,SAAS,cACP,MACA,OACiC;CACjC,IAAI,iBAAiB,OAQnB,OAAO;EACL,GAAG;EACH,MAAM,MAAM;EACZ,SAAS,MAAM;EACf,OAAO,MAAM;CACf;CAGF,OAAO;AACT;;;;;;;;;;AAWA,SAAgB,kBAAkB,OAAgB,OAAwB;CACxE,OAAOA,UAAoB,OAAO,eAAe,KAAK,KAAK;AAC7D"}
|