@lakutata/logger 0.0.0-stage → 3.0.0-beta.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.
Files changed (41) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +137 -2
  3. package/THIRD_PARTY_NOTICES.md +158 -0
  4. package/dist/cjs/Logger.d.ts +291 -0
  5. package/dist/cjs/Logger.js +185 -0
  6. package/dist/cjs/exports/Logger.d.ts +2 -0
  7. package/dist/cjs/exports/Logger.js +20 -0
  8. package/dist/cjs/lib/Colors.d.ts +8 -0
  9. package/dist/cjs/lib/Colors.js +48 -0
  10. package/dist/cjs/lib/Destination.d.ts +38 -0
  11. package/dist/cjs/lib/Destination.js +103 -0
  12. package/dist/cjs/lib/Format.d.ts +7 -0
  13. package/dist/cjs/lib/Format.js +106 -0
  14. package/dist/cjs/lib/Pretty.d.ts +9 -0
  15. package/dist/cjs/lib/Pretty.js +265 -0
  16. package/dist/cjs/lib/Record.d.ts +25 -0
  17. package/dist/cjs/lib/Record.js +139 -0
  18. package/dist/cjs/lib/Serializers.d.ts +26 -0
  19. package/dist/cjs/lib/Serializers.js +150 -0
  20. package/dist/cjs/lib/Stringify.d.ts +5 -0
  21. package/dist/cjs/lib/Stringify.js +110 -0
  22. package/dist/cjs/package.json +1 -0
  23. package/dist/esm/Logger.js +182 -0
  24. package/dist/esm/exports/Logger.js +1 -0
  25. package/dist/esm/lib/Colors.js +45 -0
  26. package/dist/esm/lib/Destination.js +99 -0
  27. package/dist/esm/lib/Format.js +103 -0
  28. package/dist/esm/lib/Pretty.js +262 -0
  29. package/dist/esm/lib/Record.js +134 -0
  30. package/dist/esm/lib/Serializers.js +143 -0
  31. package/dist/esm/lib/Stringify.js +107 -0
  32. package/dist/types/Logger.d.ts +291 -0
  33. package/dist/types/exports/Logger.d.ts +2 -0
  34. package/dist/types/lib/Colors.d.ts +8 -0
  35. package/dist/types/lib/Destination.d.ts +38 -0
  36. package/dist/types/lib/Format.d.ts +7 -0
  37. package/dist/types/lib/Pretty.d.ts +9 -0
  38. package/dist/types/lib/Record.d.ts +25 -0
  39. package/dist/types/lib/Serializers.d.ts +26 -0
  40. package/dist/types/lib/Stringify.d.ts +5 -0
  41. package/package.json +43 -4
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pretty = pretty;
4
+ const EOL = '\n';
5
+ const IDENT = ' ';
6
+ const LEVEL_LABELS = { default: 'USERLVL', 60: 'FATAL', 50: 'ERROR', 40: 'WARN', 30: 'INFO', 20: 'DEBUG', 10: 'TRACE' };
7
+ const LEVEL_NUMBERS = { fatal: 60, error: 50, warn: 40, info: 30, debug: 20, trace: 10 };
8
+ //The keys of the logger, not printed as properties
9
+ const LOGGER_KEYS = ['pid', 'hostname', 'name', 'level', 'time', 'timestamp', 'caller'];
10
+ const ERROR_KEYS = new Set(['err', 'error']);
11
+ /**
12
+ * The JSON of a property value, indented by 2 spaces
13
+ * @param value
14
+ */
15
+ function propertyJSON(value) {
16
+ switch (typeof value) {
17
+ case 'number':
18
+ case 'boolean':
19
+ return String(value);
20
+ case 'string':
21
+ return JSON.stringify(value);
22
+ default:
23
+ return JSON.stringify(value, null, 2);
24
+ }
25
+ }
26
+ const hasOwn = Object.hasOwn;
27
+ //The prototype pollution suspects of a JSON text
28
+ const SUSPECT_PROTO = /"(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])"\s*:/;
29
+ const SUSPECT_CONSTRUCTOR = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
30
+ /**
31
+ * Remove the "__proto__" properties of a parsed log, false if it has a constructor with a prototype (not printed as a log)
32
+ * @param log
33
+ */
34
+ function sanitize(log) {
35
+ let next = [log];
36
+ try {
37
+ while (next.length) {
38
+ const nodes = next;
39
+ next = [];
40
+ for (const node of nodes) {
41
+ if (hasOwn(node, '__proto__'))
42
+ delete node.__proto__;
43
+ if (hasOwn(node, 'constructor') && Object.prototype.hasOwnProperty.call(node.constructor, 'prototype'))
44
+ return false;
45
+ for (const key in node) {
46
+ const value = node[key];
47
+ if (value && typeof value === 'object')
48
+ next.push(value);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+ /**
59
+ * Indent the lines after the first one
60
+ * @param input
61
+ * @param ident
62
+ */
63
+ function joinLines(input, ident = IDENT) {
64
+ const lines = input.split(/\r?\n/);
65
+ for (let index = 1; index < lines.length; index++)
66
+ lines[index] = ident + lines[index];
67
+ return lines.join(EOL);
68
+ }
69
+ /**
70
+ * Whether a value is a plain object
71
+ * @param value
72
+ */
73
+ function isObjectValue(value) {
74
+ return Object.prototype.toString.call(value) === '[object Object]';
75
+ }
76
+ /**
77
+ * Pad a number with zeros
78
+ * @param value
79
+ * @param length
80
+ */
81
+ function pad(value, length = 2) {
82
+ return String(value).padStart(length, '0');
83
+ }
84
+ /**
85
+ * The time of a log, HH:MM:ss.SSS in the local time zone
86
+ * @param log
87
+ */
88
+ function prettyTime(log) {
89
+ let time = null;
90
+ if ('time' in log) {
91
+ time = log.time;
92
+ }
93
+ else if ('timestamp' in log) {
94
+ time = log.timestamp;
95
+ }
96
+ if (time === null)
97
+ return undefined;
98
+ let date = new Date(time);
99
+ if (isNaN(date.getTime()))
100
+ date = new Date(+time);
101
+ if (isNaN(date.getTime()))
102
+ return `[${time}]`;
103
+ return `[${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}]`;
104
+ }
105
+ /**
106
+ * The label of the level of a log
107
+ * @param log
108
+ * @param palette
109
+ */
110
+ function prettyLevel(log, palette) {
111
+ if (!hasOwn(log, 'level'))
112
+ return undefined;
113
+ const level = log.level;
114
+ if (level === undefined)
115
+ return undefined;
116
+ let levelNumber = 'default';
117
+ if (Number.isInteger(+level)) {
118
+ levelNumber = hasOwn(LEVEL_LABELS, level) ? level : levelNumber;
119
+ }
120
+ else {
121
+ const name = String(level).toLowerCase();
122
+ levelNumber = hasOwn(LEVEL_NUMBERS, name) ? LEVEL_NUMBERS[name] : levelNumber;
123
+ }
124
+ const label = LEVEL_LABELS[levelNumber];
125
+ return hasOwn(palette.level, levelNumber) ? palette.level[levelNumber](label) : palette.level.default(label);
126
+ }
127
+ /**
128
+ * The metadata of a log: (name/pid on hostname) <caller>
129
+ * @param log
130
+ */
131
+ function prettyMetadata(log) {
132
+ let line = '';
133
+ if (log.name || log.pid || log.hostname) {
134
+ line += '(';
135
+ if (log.name)
136
+ line += log.name;
137
+ if (log.pid)
138
+ line += log.name ? `/${log.pid}` : log.pid;
139
+ if (log.hostname)
140
+ line += `${line === '(' ? 'on' : ' on'} ${log.hostname}`;
141
+ line += ')';
142
+ }
143
+ if (log.caller)
144
+ line += `${line === '' ? '' : ' '}<${log.caller}>`;
145
+ return line === '' ? undefined : line;
146
+ }
147
+ /**
148
+ * An error property, its stack is written on its own lines
149
+ * @param key
150
+ * @param lines
151
+ */
152
+ function prettyError(key, lines) {
153
+ let result = '';
154
+ const splitLines = `${IDENT}${key}: ${joinLines(lines)}${EOL}`.split(EOL);
155
+ for (let index = 0; index < splitLines.length; index++) {
156
+ if (index)
157
+ result += EOL;
158
+ const line = splitLines[index];
159
+ const matches = /^\s*"stack"/.test(line) ? /^(\s*"stack":)\s*(".*"),?$/.exec(line) : null;
160
+ if (matches) {
161
+ const indentation = ' '.repeat(/^\s*/.exec(line)[0].length + 4);
162
+ result += matches[1] + EOL + indentation + JSON.parse(matches[2]).replace(/\n/g, EOL + indentation);
163
+ }
164
+ else {
165
+ result += line;
166
+ }
167
+ }
168
+ return result;
169
+ }
170
+ /**
171
+ * The properties of a log, one per line
172
+ * @param log
173
+ * @param palette
174
+ * @param skipKeys
175
+ * @param excludeLoggerKeys
176
+ * @param ident
177
+ */
178
+ function prettyObject(log, palette, skipKeys, excludeLoggerKeys = true, ident = IDENT) {
179
+ let result = '';
180
+ let errors;
181
+ for (const key in log) {
182
+ if (!hasOwn(log, key) || skipKeys.includes(key) || (excludeLoggerKeys && LOGGER_KEYS.includes(key)))
183
+ continue;
184
+ const value = log[key];
185
+ if (ERROR_KEYS.has(key)) {
186
+ (errors ??= []).push([key, value]);
187
+ continue;
188
+ }
189
+ let lines = propertyJSON(value);
190
+ if (lines === undefined)
191
+ continue;
192
+ if (lines.includes('\\\\'))
193
+ lines = lines.replace(/\\\\/gi, '\\');
194
+ const joinedLines = lines.includes('\n') ? joinLines(lines, ident) : lines;
195
+ result += `${ident}${palette.property(key)}:${joinedLines.startsWith(EOL) ? '' : ' '}${joinedLines}${EOL}`;
196
+ }
197
+ if (!errors)
198
+ return result;
199
+ for (const [key, value] of errors) {
200
+ const lines = JSON.stringify(value, null, 2);
201
+ if (lines !== undefined)
202
+ result += prettyError(key, lines);
203
+ }
204
+ return result;
205
+ }
206
+ /**
207
+ * A log which is an error: its stack
208
+ * @param log
209
+ * @param palette
210
+ */
211
+ function prettyErrorLog(log, palette) {
212
+ let result = `${IDENT}${joinLines(log.stack)}${EOL}`;
213
+ //The error properties to print: none
214
+ if ('' in log) {
215
+ result += isObjectValue(log['']) ? `${IDENT}: {${EOL}${prettyObject(log[''], palette, [], false, IDENT + IDENT)}${IDENT}}${EOL}` : `${IDENT}: ${log['']}${EOL}`;
216
+ }
217
+ return result;
218
+ }
219
+ /**
220
+ * Format a JSON log line for humans:
221
+ * [time] LEVEL (name/pid): message, then the properties on their own lines
222
+ * @param line the JSON log line, without its line terminator
223
+ * @param palette
224
+ * @param parsed the parsed line, if it is known (the log is changed)
225
+ */
226
+ function pretty(line, palette, parsed) {
227
+ let log;
228
+ if (parsed) {
229
+ log = parsed;
230
+ }
231
+ else {
232
+ try {
233
+ log = JSON.parse(line);
234
+ }
235
+ catch {
236
+ return line + EOL;
237
+ }
238
+ if (!isObjectValue(log))
239
+ return line + EOL;
240
+ }
241
+ if ((SUSPECT_PROTO.test(line) || SUSPECT_CONSTRUCTOR.test(line)) && !sanitize(log))
242
+ return line + EOL;
243
+ const message = 'msg' in log && (typeof log.msg === 'string' || typeof log.msg === 'number' || typeof log.msg === 'boolean') ? palette.message(log.msg) : undefined;
244
+ //The host name is not printed
245
+ if (hasOwn(log, 'hostname'))
246
+ delete log.hostname;
247
+ const level = prettyLevel(log, palette);
248
+ const metadata = prettyMetadata(log);
249
+ const time = prettyTime(log);
250
+ let result = time ?? '';
251
+ if (level)
252
+ result = result ? `${result} ${level}` : level;
253
+ if (metadata)
254
+ result = result ? `${result} ${metadata}:` : metadata;
255
+ if (!result.endsWith(':') && result !== '')
256
+ result += ':';
257
+ if (message !== undefined)
258
+ result = result ? `${result} ${message}` : message;
259
+ if (result.length)
260
+ result += EOL;
261
+ if (log.type === 'Error' && typeof log.stack === 'string')
262
+ return result + prettyErrorLog(log, palette);
263
+ const skipKeys = ['msg', 'level', 'time'].filter((key) => typeof log[key] === 'string' || typeof log[key] === 'number' || typeof log[key] === 'boolean');
264
+ return result + prettyObject(log, palette, skipKeys);
265
+ }
@@ -0,0 +1,25 @@
1
+ export declare const LEVELS: Record<string, number>;
2
+ export type LogBindings = {
3
+ json: string;
4
+ name: string;
5
+ };
6
+ export type LogRecord = {
7
+ line: string;
8
+ log?: Record<string, unknown>;
9
+ };
10
+ /**
11
+ * The bindings of the log lines of an application: pid, hostname and name
12
+ * @param name
13
+ */
14
+ export declare function logBindings(name: string): LogBindings;
15
+ /**
16
+ * Build a JSON log line from the arguments of a log method:
17
+ * an object (or error, HTTP request or response) with an optional message and its format arguments,
18
+ * or a message with its format arguments
19
+ * @param level
20
+ * @param bindings
21
+ * @param args
22
+ * @param parse whether to also build the parsed line
23
+ * @param redacted the lower-case names of the headers of an HTTP request or response written as [Redacted]
24
+ */
25
+ export declare function buildRecord(level: number, bindings: LogBindings, args: unknown[], parse?: boolean, redacted?: ReadonlySet<string>): LogRecord;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LEVELS = void 0;
4
+ exports.logBindings = logBindings;
5
+ exports.buildRecord = buildRecord;
6
+ //Derived from the upstream projects listed in THIRD_PARTY_NOTICES.md (rewritten in TypeScript)
7
+ const node_os_1 = require("node:os");
8
+ const Format_js_1 = require("./Format.js");
9
+ const Serializers_js_1 = require("./Serializers.js");
10
+ const Stringify_js_1 = require("./Stringify.js");
11
+ exports.LEVELS = { trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60, silent: Infinity };
12
+ const HOSTNAME = (0, node_os_1.hostname)();
13
+ /**
14
+ * The JSON of a log value, undefined if it is not logged
15
+ * @param value
16
+ */
17
+ function valueJSON(value) {
18
+ switch (typeof value) {
19
+ case 'undefined':
20
+ case 'function':
21
+ return undefined;
22
+ case 'number':
23
+ return Number.isFinite(value) ? String(value) : 'null';
24
+ case 'boolean':
25
+ return String(value);
26
+ case 'string':
27
+ return JSON.stringify(value);
28
+ default:
29
+ return (0, Stringify_js_1.stringify)(value);
30
+ }
31
+ }
32
+ /**
33
+ * The bindings of the log lines of an application: pid, hostname and name
34
+ * @param name
35
+ */
36
+ function logBindings(name) {
37
+ return { json: `,"pid":${process.pid},"hostname":${JSON.stringify(HOSTNAME)},"name":${JSON.stringify(name)}`, name: name };
38
+ }
39
+ /**
40
+ * The value of a JSON text, as parsed
41
+ * @param value
42
+ * @param json
43
+ */
44
+ function parsedValue(value, json) {
45
+ switch (typeof value) {
46
+ case 'string':
47
+ case 'boolean':
48
+ return value;
49
+ case 'number':
50
+ return Number.isFinite(value) ? value + 0 : null;
51
+ default:
52
+ return JSON.parse(json);
53
+ }
54
+ }
55
+ /**
56
+ * Set a property of a parsed log, as JSON.parse does (the first position and the last value of a key)
57
+ * @param log
58
+ * @param key
59
+ * @param value
60
+ */
61
+ function setParsed(log, key, value) {
62
+ key === '__proto__' ? Object.defineProperty(log, key, { value: value, enumerable: true, writable: true, configurable: true }) : log[key] = value;
63
+ }
64
+ /**
65
+ * Build a JSON log line from the arguments of a log method:
66
+ * an object (or error, HTTP request or response) with an optional message and its format arguments,
67
+ * or a message with its format arguments
68
+ * @param level
69
+ * @param bindings
70
+ * @param args
71
+ * @param parse whether to also build the parsed line
72
+ * @param redacted the lower-case names of the headers of an HTTP request or response written as [Redacted]
73
+ */
74
+ function buildRecord(level, bindings, args, parse = false, redacted = new Set()) {
75
+ let object = null;
76
+ let message;
77
+ let [first, ...rest] = args;
78
+ if (typeof first === 'object') {
79
+ message = first;
80
+ if (first !== null) {
81
+ if (first.method && first.headers && first.socket) {
82
+ first = { req: (0, Serializers_js_1.serializeRequest)(first, redacted) };
83
+ }
84
+ else if (typeof first.setHeader === 'function') {
85
+ first = { res: (0, Serializers_js_1.serializeResponse)(first, redacted) };
86
+ }
87
+ }
88
+ let formatArgs;
89
+ if (message === null && !rest.length) {
90
+ formatArgs = [null];
91
+ }
92
+ else {
93
+ message = rest.shift();
94
+ formatArgs = rest;
95
+ }
96
+ object = first;
97
+ message = (0, Format_js_1.format)(message, formatArgs);
98
+ }
99
+ else {
100
+ message = (0, Format_js_1.format)(first === undefined ? rest.shift() : first, rest);
101
+ }
102
+ if (object === undefined || object === null) {
103
+ object = {};
104
+ }
105
+ else if (object instanceof Error) {
106
+ if (message === undefined)
107
+ message = object.message;
108
+ object = { err: object };
109
+ }
110
+ else if (message === undefined && object.msg === undefined && object.err) {
111
+ message = object.err.message;
112
+ }
113
+ const time = Date.now();
114
+ let line = `{"level":${level},"time":${time}${bindings.json}`;
115
+ const log = parse ? { level: level, time: time, pid: process.pid, hostname: HOSTNAME, name: bindings.name } : undefined;
116
+ for (const key in object) {
117
+ let value = object[key];
118
+ if (!Object.prototype.hasOwnProperty.call(object, key) || value === undefined)
119
+ continue;
120
+ if (key === 'err')
121
+ value = (0, Serializers_js_1.serializeError)(value);
122
+ const json = valueJSON(value);
123
+ if (json === undefined)
124
+ continue;
125
+ line += `,${JSON.stringify(key)}:${json}`;
126
+ if (log)
127
+ setParsed(log, key, parsedValue(value, json));
128
+ }
129
+ if (message !== undefined && typeof message !== 'function') {
130
+ const json = typeof message === 'string' ? JSON.stringify(message) : valueJSON(message);
131
+ //A message which cannot be serialized (a symbol) is omitted
132
+ if (json !== undefined) {
133
+ line += `,"msg":${json}`;
134
+ if (log)
135
+ setParsed(log, 'msg', parsedValue(message, json));
136
+ }
137
+ }
138
+ return { line: `${line}}\n`, log: log };
139
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Whether a value looks like an error
3
+ * @param value
4
+ */
5
+ export declare function isErrorLike(value: any): boolean;
6
+ /**
7
+ * Serialize an error: type, message and stack (with the causes), aggregated errors and the other enumerable properties
8
+ * @param error
9
+ */
10
+ export declare function serializeError(error: any): any;
11
+ /**
12
+ * The headers written as [Redacted] by default: the credentials of the requests and of the responses
13
+ */
14
+ export declare const REDACTED_HEADERS: readonly string[];
15
+ /**
16
+ * Serialize an HTTP request
17
+ * @param request
18
+ * @param redacted the lower-case names of the headers written as [Redacted]
19
+ */
20
+ export declare function serializeRequest(request: any, redacted?: ReadonlySet<string>): Record<string, unknown>;
21
+ /**
22
+ * Serialize an HTTP response
23
+ * @param response
24
+ * @param redacted the lower-case names of the headers written as [Redacted]
25
+ */
26
+ export declare function serializeResponse(response: any, redacted?: ReadonlySet<string>): Record<string, unknown>;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.REDACTED_HEADERS = void 0;
4
+ exports.isErrorLike = isErrorLike;
5
+ exports.serializeError = serializeError;
6
+ exports.serializeRequest = serializeRequest;
7
+ exports.serializeResponse = serializeResponse;
8
+ //Derived from the upstream projects listed in THIRD_PARTY_NOTICES.md (rewritten in TypeScript)
9
+ const SEEN = Symbol('seen');
10
+ /**
11
+ * Whether a value looks like an error
12
+ * @param value
13
+ */
14
+ function isErrorLike(value) {
15
+ return !!value && typeof value.message === 'string';
16
+ }
17
+ /**
18
+ * The cause of an error
19
+ * @param error
20
+ */
21
+ function causeOf(error) {
22
+ if (!error)
23
+ return undefined;
24
+ const cause = typeof error.cause === 'function' ? error.cause() : error.cause;
25
+ return isErrorLike(cause) ? cause : undefined;
26
+ }
27
+ /**
28
+ * The stack of an error followed by the stacks of its causes
29
+ * @param error
30
+ * @param seen
31
+ */
32
+ function stackWithCauses(error, seen = new Set()) {
33
+ if (!isErrorLike(error))
34
+ return '';
35
+ const stack = error.stack || '';
36
+ if (seen.has(error))
37
+ return `${stack}\ncauses have become circular...`;
38
+ const cause = causeOf(error);
39
+ if (!cause)
40
+ return stack;
41
+ seen.add(error);
42
+ return `${stack}\ncaused by: ${stackWithCauses(cause, seen)}`;
43
+ }
44
+ /**
45
+ * The message of an error followed by the messages of its causes
46
+ * @param error
47
+ * @param seen
48
+ * @param skip
49
+ */
50
+ function messageWithCauses(error, seen = new Set(), skip = false) {
51
+ if (!isErrorLike(error))
52
+ return '';
53
+ const message = skip ? '' : (error.message || '');
54
+ if (seen.has(error))
55
+ return `${message}: ...`;
56
+ const cause = causeOf(error);
57
+ if (!cause)
58
+ return message;
59
+ seen.add(error);
60
+ //A cause function (VError) already includes the message of its cause, as an error wrapping a cause of the same
61
+ //message (an exception keeping the original error)
62
+ const included = typeof error.cause === 'function' || (!skip && !seen.has(cause) && message === cause.message);
63
+ return message + (included ? '' : ': ') + messageWithCauses(cause, seen, included);
64
+ }
65
+ /**
66
+ * Serialize an error: type, message and stack (with the causes), aggregated errors and the other enumerable properties
67
+ * @param error
68
+ */
69
+ function serializeError(error) {
70
+ if (!isErrorLike(error))
71
+ return error;
72
+ error[SEEN] = undefined;
73
+ const serialized = {};
74
+ serialized.type = Object.prototype.toString.call(error.constructor) === '[object Function]' ? error.constructor.name : error.name;
75
+ serialized.message = messageWithCauses(error);
76
+ serialized.stack = stackWithCauses(error);
77
+ if (Array.isArray(error.errors))
78
+ serialized.aggregateErrors = error.errors.map((item) => serializeError(item));
79
+ for (const key in error) {
80
+ //"raw" is reserved for the original error (not serialized)
81
+ if (serialized[key] !== undefined || key === 'raw')
82
+ continue;
83
+ const value = error[key];
84
+ if (isErrorLike(value)) {
85
+ if (key !== 'cause' && !Object.prototype.hasOwnProperty.call(value, SEEN))
86
+ serialized[key] = serializeError(value);
87
+ }
88
+ else {
89
+ serialized[key] = value;
90
+ }
91
+ }
92
+ delete error[SEEN];
93
+ return serialized;
94
+ }
95
+ /**
96
+ * The headers written as [Redacted] by default: the credentials of the requests and of the responses
97
+ */
98
+ exports.REDACTED_HEADERS = Object.freeze(['authorization', 'cookie', 'set-cookie', 'proxy-authorization']);
99
+ /**
100
+ * A copy of headers, the values of the redacted ones (lower-case names) replaced by [Redacted]
101
+ * @param headers
102
+ * @param redacted
103
+ */
104
+ function redactHeaders(headers, redacted) {
105
+ if (!redacted.size || !headers || typeof headers !== 'object')
106
+ return headers;
107
+ //A copy: the headers of the request or the response are not changed
108
+ const copy = { ...headers };
109
+ for (const name of Object.keys(copy))
110
+ if (redacted.has(name.toLowerCase()))
111
+ copy[name] = '[Redacted]';
112
+ return copy;
113
+ }
114
+ /**
115
+ * Serialize an HTTP request
116
+ * @param request
117
+ * @param redacted the lower-case names of the headers written as [Redacted]
118
+ */
119
+ function serializeRequest(request, redacted = new Set()) {
120
+ const connection = request.info || request.socket;
121
+ const serialized = {};
122
+ serialized.id = typeof request.id === 'function' ? request.id() : (request.id || (request.info ? request.info.id : undefined));
123
+ serialized.method = request.method;
124
+ if (request.originalUrl) {
125
+ serialized.url = request.originalUrl;
126
+ }
127
+ else {
128
+ const path = request.path;
129
+ serialized.url = typeof path === 'string' ? path : (request.url ? request.url.path || request.url : undefined);
130
+ }
131
+ if (request.query)
132
+ serialized.query = request.query;
133
+ if (request.params)
134
+ serialized.params = request.params;
135
+ serialized.headers = redactHeaders(request.headers, redacted);
136
+ serialized.remoteAddress = connection && connection.remoteAddress;
137
+ serialized.remotePort = connection && connection.remotePort;
138
+ return serialized;
139
+ }
140
+ /**
141
+ * Serialize an HTTP response
142
+ * @param response
143
+ * @param redacted the lower-case names of the headers written as [Redacted]
144
+ */
145
+ function serializeResponse(response, redacted = new Set()) {
146
+ return {
147
+ statusCode: response.headersSent ? response.statusCode : null,
148
+ headers: redactHeaders(response.getHeaders ? response.getHeaders() : response._headers, redacted)
149
+ };
150
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Serialize a value to JSON, the values JSON cannot serialize are serialized safely
3
+ * @param value
4
+ */
5
+ export declare function stringify(value: unknown): string | undefined;