@darksheep/logger 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +191 -0
  3. package/package.json +44 -0
  4. package/src/create-logger.js +36 -0
  5. package/src/formatter.js +14 -0
  6. package/src/formatters/formatter-console.js +243 -0
  7. package/src/formatters/formatter-json.js +7 -0
  8. package/src/index.js +25 -0
  9. package/src/logger.js +369 -0
  10. package/src/replacer.js +68 -0
  11. package/src/replacers/buffers.js +18 -0
  12. package/src/replacers/error.js +40 -0
  13. package/src/replacers/http-client-request.js +18 -0
  14. package/src/replacers/http-incoming-message.js +26 -0
  15. package/src/replacers/http-server-response.js +16 -0
  16. package/src/replacers/index.js +8 -0
  17. package/src/replacers/long-strings.js +11 -0
  18. package/src/replacers/net-socket.js +21 -0
  19. package/src/replacers/secrets.js +50 -0
  20. package/src/stdout-write.js +7 -0
  21. package/src/utilities/colour.js +152 -0
  22. package/src/utilities/environment.js +61 -0
  23. package/src/utilities/json-path.js +17 -0
  24. package/src/utilities/last-callsite.js +11 -0
  25. package/src/utilities/log-filters.js +22 -0
  26. package/src/utilities/log-types.js +46 -0
  27. package/src/utilities/parse-filters.js +47 -0
  28. package/src/utilities/parse-log-level.js +32 -0
  29. package/src/utilities/stacktrace.js +57 -0
  30. package/types/assert.zod.d.ts +18 -0
  31. package/types/create-logger.d.ts +5 -0
  32. package/types/formatter.d.ts +6 -0
  33. package/types/formatters/formatter-console.d.ts +9 -0
  34. package/types/formatters/formatter-json.d.ts +5 -0
  35. package/types/index.d.ts +11 -0
  36. package/types/logger.d.ts +184 -0
  37. package/types/logger.test.d.ts +1 -0
  38. package/types/replacer.d.ts +45 -0
  39. package/types/replacer.test.d.ts +1 -0
  40. package/types/replacers/buffers.d.ts +2 -0
  41. package/types/replacers/buffers.test.d.ts +1 -0
  42. package/types/replacers/error.d.ts +37 -0
  43. package/types/replacers/error.test.d.ts +1 -0
  44. package/types/replacers/http-client-request.d.ts +3 -0
  45. package/types/replacers/http-client-request.test.d.ts +1 -0
  46. package/types/replacers/http-incoming-message.d.ts +15 -0
  47. package/types/replacers/http-server-response.d.ts +3 -0
  48. package/types/replacers/index.d.ts +8 -0
  49. package/types/replacers/long-strings.d.ts +2 -0
  50. package/types/replacers/net-socket.d.ts +3 -0
  51. package/types/replacers/secrets.d.ts +4 -0
  52. package/types/stdout-write.d.ts +5 -0
  53. package/types/utilities/colour.d.ts +76 -0
  54. package/types/utilities/environment.d.ts +25 -0
  55. package/types/utilities/json-path.d.ts +5 -0
  56. package/types/utilities/last-callsite.d.ts +9 -0
  57. package/types/utilities/log-filters.d.ts +12 -0
  58. package/types/utilities/log-types.d.ts +126 -0
  59. package/types/utilities/parse-filters.d.ts +9 -0
  60. package/types/utilities/parse-log-level.d.ts +7 -0
  61. package/types/utilities/stacktrace.d.ts +59 -0
package/src/logger.js ADDED
@@ -0,0 +1,369 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { deepmerge } from 'deepmerge-ts';
3
+
4
+ import { environment } from './utilities/environment.js';
5
+ import { getLastCallsite } from './utilities/last-callsite.js';
6
+ import { LogLevels } from './utilities/log-types.js';
7
+ import { checkLogLevel, checkLogFilters } from './utilities/log-filters.js';
8
+
9
+ import { replace } from './replacer.js';
10
+ import { ErrorReplacer } from './replacers/error.js';
11
+ import { stdoutWrite } from './stdout-write.js';
12
+ import { formatter } from './formatter.js';
13
+
14
+ /**
15
+ * @typedef {Object} Options
16
+ * @property {typeof replace} [replace] The replacer function to use
17
+ * @property {import('./replacer.js').Replacer<any, any>[]} [replacers] The replacers to use in the replacer
18
+ * @property {typeof formatter} [format] The formatter function to use (eg console or json formatter)
19
+ * @property {typeof stdoutWrite} [write] The place to write the log to
20
+ */
21
+
22
+ export class Logger {
23
+ /** @type {AsyncLocalStorage<import('./utilities/log-types.js').LogContext[]>} */
24
+ static storage = new AsyncLocalStorage();
25
+
26
+ /**
27
+ * @template [R=unknown]
28
+ * @overload
29
+ * @param {() => R} callback The function to wrap the context in
30
+ * @returns {R}
31
+ */
32
+ /**
33
+ * @template [R=unknown]
34
+ * @overload
35
+ * @param {import('./utilities/log-types.js').LogContext} context Context to bind ot the async context
36
+ * @param {() => R} callback The function to wrap the context in
37
+ * @returns {R}
38
+ */
39
+ /**
40
+ * @template [R=unknown]
41
+ * @param {unknown} context Context to bind ot the async context
42
+ * @param {unknown} [callback] The function to wrap the context in
43
+ * @returns {R}
44
+ */
45
+ static wrap(context, callback) {
46
+ // If there is already some context, keep it around
47
+ const store = Logger.storage.getStore() ?? [];
48
+
49
+ if (typeof context === 'function') {
50
+ return Logger.storage.run(
51
+ [ ...store ],
52
+ /** @type {() => R} */ (context),
53
+ );
54
+ }
55
+
56
+ return Logger.storage.run(
57
+ [ ...store, /** @type {import('./utilities/log-types.js').LogContext} */ (context) ],
58
+ /** @type {() => R} */ (callback),
59
+ );
60
+ }
61
+
62
+ /**
63
+ * @template [R=unknown]
64
+ * @param {() => R} callback The function to wrap the context in
65
+ * @returns {R}
66
+ */
67
+ wrap(callback) {
68
+ return Logger.wrap(this.#context, callback);
69
+ }
70
+
71
+ /**
72
+ * @param {import('./utilities/log-types.js').LogContext} context Context to bind ot the async context
73
+ * @returns {void}
74
+ */
75
+ static addAsyncContext(context) {
76
+ const store = Logger.storage.getStore();
77
+
78
+ if (store == null) {
79
+ throw new Error('Context not inside Context#wrap');
80
+ }
81
+
82
+ store.push(context);
83
+ }
84
+
85
+ /** @type {import('./replacer.js').Replacer[]} */
86
+ replacers = [];
87
+
88
+ /** @type {typeof formatter} */
89
+ #format = formatter;
90
+ /** @type {typeof replace} */
91
+ #replace = replace;
92
+ /** @type {typeof stdoutWrite} */
93
+ #write = stdoutWrite;
94
+ /** @type {import('./utilities/log-types.js').LogContext} */
95
+ #context;
96
+
97
+ /**
98
+ * @param {import('./utilities/log-types.js').LogContext} [context] Context to add to the logger
99
+ * @param {Options} [options] Options for the loggers output
100
+ */
101
+ constructor(context, options) {
102
+ this.#context = context ?? {};
103
+ if (options?.replace != null) {
104
+ this.#replace = options.replace;
105
+ }
106
+ if (options?.replacers != null) {
107
+ this.replacers = options.replacers;
108
+ }
109
+ if (options?.format != null) {
110
+ this.#format = options.format;
111
+ }
112
+ if (options?.write != null) {
113
+ this.#write = options.write;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Write a new log entry
119
+ * @param {import('./utilities/log-types.js').LogLevel} level the log level of the message to log
120
+ * @param {Error | string} message The message to log
121
+ * @param {import('./utilities/log-types.js').LogContext} [context] Context to add to the log (merged with this.context, and Logger.contexts)
122
+ * @returns {void}
123
+ */
124
+ write(level, message, context) {
125
+ /** @type {import('./utilities/log-types.js').LogContext[]} */
126
+ const contexts = [];
127
+
128
+ const asyncContexts = Logger.storage.getStore();
129
+ if (asyncContexts instanceof Array) {
130
+ contexts.push(...asyncContexts);
131
+ }
132
+
133
+ contexts.push(this.#context);
134
+ if (context instanceof Object) {
135
+ contexts.push(context);
136
+ }
137
+
138
+ const fullContext =
139
+ /** @type {import('./utilities/log-types.js').LogContext} */
140
+ (deepmerge(...contexts));
141
+
142
+ if (
143
+ checkLogLevel(level) === false ||
144
+ checkLogFilters(fullContext?.channel) === false
145
+ ) {
146
+ return;
147
+ }
148
+
149
+ /** @type {import('./utilities/log-types.js').LogEntry} */
150
+ const entry = (
151
+ typeof message === 'string'
152
+ ? { ...fullContext, message, level }
153
+ : { ...fullContext, ...ErrorReplacer.replace(message), level }
154
+ );
155
+
156
+ if (environment.includeCallsite === true) {
157
+ entry.source = getLastCallsite();
158
+ }
159
+
160
+ const normalised =
161
+ /** @type {import('./utilities/log-types.js').LogEntry} */
162
+ (this.#replace(entry, this.replacers));
163
+
164
+ const formatted = this.#format(normalised);
165
+
166
+ return this.#write(formatted);
167
+ }
168
+
169
+ // =========
170
+ // Level
171
+ // =========
172
+
173
+ /**
174
+ * Write a log with the level 'critical' - A crucial part of the application is not working
175
+ *
176
+ * @param {Error | string} message The message to log
177
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
178
+ * @returns {void}
179
+ */
180
+ critical(message, context) {
181
+ return this.write(LogLevels.critical, message, context);
182
+ }
183
+
184
+ /**
185
+ * Write a log with the level 'error' - A non critical operation fails
186
+ * @param {Error | string} message The message to log
187
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
188
+ * @returns {void}
189
+ */
190
+ error(message, context) {
191
+ return this.write(LogLevels.error, message, context);
192
+ }
193
+
194
+ /**
195
+ * Write a log with the level 'warning' - An operation might fail in the future
196
+ * @param {Error | string} message The message to log
197
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
198
+ * @returns {void}
199
+ */
200
+ warning(message, context) {
201
+ return this.write(LogLevels.warning, message, context);
202
+ }
203
+
204
+ /**
205
+ * Write a log with the level 'notice' - Information about events that may be unusual
206
+ * @param {Error | string} message The message to log
207
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
208
+ * @returns {void}
209
+ */
210
+ notice(message, context) {
211
+ return this.write(LogLevels.notice, message, context);
212
+ }
213
+
214
+ /**
215
+ * Write a log with the level 'info' - Information about successful operations
216
+ * @param {Error | string} message The message to log
217
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
218
+ * @returns {void}
219
+ */
220
+ info(message, context) {
221
+ return this.write(LogLevels.info, message, context);
222
+ }
223
+
224
+ /**
225
+ * Write a log with the level 'debug' - Information that is unlikely to help in production
226
+ * @param {Error | string} message The message to log
227
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
228
+ * @returns {void}
229
+ */
230
+ debug(message, context) {
231
+ return this.write(LogLevels.debug, message, context);
232
+ }
233
+
234
+ /**
235
+ * Write a log with the level 'silly' - Information to help resolve complex logic issues
236
+ * @param {Error | string} message The message to log
237
+ * @param {import('./utilities/log-types.js').LogContext} [context] Additional context
238
+ * @returns {void}
239
+ */
240
+ silly(message, context) {
241
+ return this.write(LogLevels.silly, message, context);
242
+ }
243
+
244
+ // =========
245
+ // Context
246
+ // =========
247
+
248
+ /**
249
+ * @param {import('./utilities/log-types.js').LogContext} context The new context
250
+ * @param {import('./utilities/log-types.js').LogLevel} [level] The log level at which we allow this context
251
+ * @returns {Logger}
252
+ */
253
+ #overrideContext(context, level) {
254
+ if (
255
+ typeof level === 'number' &&
256
+ checkLogLevel(level) === false
257
+ ) {
258
+ return this;
259
+ }
260
+
261
+ return new Logger(
262
+ deepmerge(this.#context, context),
263
+ {
264
+ replace: this.#replace,
265
+ replacers: this.replacers,
266
+ format: this.#format,
267
+ write: this.#write,
268
+ },
269
+ );
270
+ }
271
+
272
+ /**
273
+ * Creating a new logger with {context, ...this.context}
274
+ * @overload
275
+ * @param {import('./utilities/log-types.js').LogContext} context The new context
276
+ * @returns {Logger}
277
+ */
278
+ /**
279
+ * Creating a new logger with {context, ...this.context}
280
+ * @overload
281
+ * @param {import('./utilities/log-types.js').LogContext} context The new context
282
+ * @param {import('./utilities/log-types.js').LogLevel} level The log level at which we allow this context
283
+ * @returns {Logger}
284
+ */
285
+ /**
286
+ * Creating a new logger with {context, ...this.context}
287
+ * @overload
288
+ * @param {import('./utilities/log-types.js').LogLevel} level The log level at which we allow this context
289
+ * @param {import('./utilities/log-types.js').LogContext} context The new context
290
+ * @returns {Logger}
291
+ */
292
+ /**
293
+ * Creating a new logger with {context, ...this.context}
294
+ * @param {import('./utilities/log-types.js').LogContext | import('./utilities/log-types.js').LogLevel} contextOrLevel The log level at which we allow this context
295
+ * @param {import('./utilities/log-types.js').LogLevel | import('./utilities/log-types.js').LogContext} [levelOrContext] The new context
296
+ * @returns {Logger}
297
+ */
298
+ context(contextOrLevel, levelOrContext) {
299
+ if (
300
+ levelOrContext instanceof Object &&
301
+ (
302
+ contextOrLevel == null ||
303
+ typeof contextOrLevel === 'number'
304
+ )
305
+ ) {
306
+ return this.#overrideContext(
307
+ levelOrContext,
308
+ contextOrLevel,
309
+ );
310
+ }
311
+
312
+ if (
313
+ contextOrLevel instanceof Object &&
314
+ (
315
+ levelOrContext == null ||
316
+ typeof levelOrContext === 'number'
317
+ )
318
+ ) {
319
+ return this.#overrideContext(
320
+ contextOrLevel,
321
+ levelOrContext,
322
+ );
323
+ }
324
+
325
+ throw new TypeError('Invalid LogContext in Logger#context()');
326
+ }
327
+
328
+ /**
329
+ * Get a value from the context by key
330
+ * @param {string} key The key to get from the context
331
+ * @returns {unknown}
332
+ */
333
+ get(key) {
334
+ return this.#context[key];
335
+ }
336
+
337
+ /**
338
+ * Set a value into a new context by key
339
+ * @param {string} key The key to set
340
+ * @param {unknown} value The value to set
341
+ * @returns {Logger}
342
+ */
343
+ set(key, value) {
344
+ return this.context({ [key]: value });
345
+ }
346
+
347
+ /**
348
+ * Set the channel in the new logger
349
+ * @param {string} channel The channel name
350
+ * @returns {Logger}
351
+ */
352
+ channel(channel) {
353
+ return this.set('channel', channel);
354
+ }
355
+
356
+ /**
357
+ * Set the trail in the new logger
358
+ * @param {string} [trail] The trail id
359
+ * @returns {Logger}
360
+ */
361
+ trail(trail) {
362
+ trail ??= Math
363
+ .random()
364
+ .toString(16)
365
+ .slice(2, 10);
366
+
367
+ return this.set('trail', trail);
368
+ }
369
+ }
@@ -0,0 +1,68 @@
1
+ /* eslint no-invalid-this: 0 */
2
+
3
+ import Traverse from 'traverse';
4
+ import { nodesToPath } from './utilities/json-path.js';
5
+
6
+ /**
7
+ * @template [T=unknown]
8
+ * @template [O=unknown]
9
+ * @typedef {Object} Replacer
10
+ * @property {string} name The name of the replacer
11
+ * @property {(input: unknown, path?: string[]) => boolean} shouldReplace Check to see if input can be replaced
12
+ * @property {(input: T, path?: string[]) => O} replace The replace function if shouldReplace returns true
13
+ * @property {boolean} [stopHere] Should the traverse stop here
14
+ */
15
+
16
+ /**
17
+ * @param {unknown} object The object we're replacing
18
+ * @param {Replacer[]} replacers The Replacer array to execute
19
+ * @returns {unknown}
20
+ */
21
+ export function replace(object, replacers = []) {
22
+ /** @type {import('traverse').Traverse<unknown>} */
23
+ const traverse = Traverse(object);
24
+
25
+ return traverse.forEach(function (value) {
26
+ if (value == null) {
27
+ return this.remove();
28
+ }
29
+
30
+ if (this.circular != null) {
31
+ return this.update({ $ref: nodesToPath(this.circular.path) });
32
+ }
33
+
34
+ for (const replacer of replacers) {
35
+ if (replacer.shouldReplace(value, this.path)) {
36
+ const replaced = replacer.replace(value, this.path);
37
+
38
+ if (replaced == null) {
39
+ return this.remove(replacer.stopHere);
40
+ }
41
+
42
+ return this.update(replaced, replacer.stopHere);
43
+ }
44
+ }
45
+
46
+ if (typeof value.toJSON === 'function') {
47
+ try {
48
+ return this.update(value.toJSON());
49
+ } catch { }
50
+ }
51
+
52
+ if (
53
+ value instanceof Object &&
54
+ Object.getPrototypeOf(value) !== Object.getPrototypeOf({}) &&
55
+ Object.getPrototypeOf(value) !== Object.getPrototypeOf([])
56
+ ) {
57
+ return this.update({ $class: value.constructor.name });
58
+ }
59
+
60
+ if (Object.getPrototypeOf(value).constructor === Object) {
61
+ this.update({ ...value });
62
+ }
63
+
64
+ if (Object.getPrototypeOf(value).constructor === Array) {
65
+ this.update([ ...value ]);
66
+ }
67
+ });
68
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @param {Buffer} buffer the buffer to convert to a string
3
+ * @returns {string}
4
+ */
5
+ function stringBuffer(buffer) {
6
+ return buffer.toString('utf8');
7
+ }
8
+
9
+ /** @type {import('../replacer.js').Replacer<Buffer>} */
10
+ export const BufferReplacer = {
11
+ name: 'Buffer',
12
+ stopHere: true,
13
+ shouldReplace: (input) => input instanceof Buffer,
14
+ replace: (value) => ({
15
+ $class: 'Buffer',
16
+ text: stringBuffer(value),
17
+ }),
18
+ };
@@ -0,0 +1,40 @@
1
+ import { parseStack } from '../utilities/stacktrace.js';
2
+
3
+ /**
4
+ * @typedef {Object} BaseError
5
+ * @property {string} type The error type or name
6
+ * @property {string} message The error message
7
+ * @property {import('../utilities/stacktrace.js').Callsite[]} stack The stacktrace of the error
8
+ */
9
+
10
+ /**
11
+ * @typedef {BaseError & { [k: string]: unknown }} NormalisedError
12
+ */
13
+
14
+ /** @type {import('../replacer.js').Replacer<Error, NormalisedError>} */
15
+ export const ErrorReplacer = {
16
+ name: 'Error',
17
+ shouldReplace: (input) => input instanceof Error,
18
+ replace: (value) => {
19
+ /** @type {Partial<NormalisedError>} */
20
+ const error = {
21
+ type: undefined,
22
+ message: undefined,
23
+ stack: undefined,
24
+ };
25
+
26
+ for (const key of Object.getOwnPropertyNames(value)) {
27
+ if (Object.hasOwn(value, key)) {
28
+ // @ts-ignore - This is always going to be defined
29
+ error[key] = value[key];
30
+ }
31
+ }
32
+
33
+ const type = Object.getPrototypeOf(value).constructor.name;
34
+ error.type ??= type;
35
+ error.message = `(${type}) ${value.message}`;
36
+ error.stack = parseStack(value.stack);
37
+
38
+ return /** @type {NormalisedError} */ (error);
39
+ },
40
+ };
@@ -0,0 +1,18 @@
1
+ import { ClientRequest } from 'node:http';
2
+
3
+ /** @type {import('../replacer.js').Replacer<ClientRequest>} */
4
+ export const HttpClientRequestReplacer = {
5
+ name: 'HttpClientRequest',
6
+ shouldReplace: (input) => input instanceof ClientRequest,
7
+ replace: (request) => ({
8
+ method: request.method,
9
+ protocol: request.protocol,
10
+ host: request.host ?? request.getHeaders()?.host,
11
+ url: request.path,
12
+
13
+ headers: { ...request.getHeaders() },
14
+ headersSent: request.headersSent,
15
+ writableEnded: request.writableEnded,
16
+ destroyed: request.destroyed,
17
+ }),
18
+ };
@@ -0,0 +1,26 @@
1
+ import { IncomingMessage } from 'node:http';
2
+
3
+ /**
4
+ * @type {import('../replacer.js').Replacer<
5
+ * IncomingMessage & {
6
+ * hostname?: string,
7
+ * originalUrl?: string,
8
+ * req?: import('node:http').ClientRequest
9
+ * }
10
+ * >}
11
+ */
12
+ export const HttpIncomingMessageReplacer = {
13
+ name: 'HttpIncomingMessage',
14
+ shouldReplace: (input) => input instanceof IncomingMessage,
15
+ replace: (input) => ({
16
+ httpVersion: input.httpVersion,
17
+ method: input.method,
18
+ host: input?.hostname ?? input?.req?.host,
19
+ url: input.originalUrl ?? input?.req?.path ?? input.url,
20
+
21
+ headers: input.headers,
22
+ complete: input.complete,
23
+ destroyed: input.destroyed,
24
+ })
25
+ ,
26
+ };
@@ -0,0 +1,16 @@
1
+ import { ServerResponse, STATUS_CODES } from 'node:http';
2
+
3
+ /** @type {import('../replacer.js').Replacer<ServerResponse>} */
4
+ export const HttpServerResponseReplacer = {
5
+ name: 'HttpServerResponse',
6
+ shouldReplace: (input) => input instanceof ServerResponse,
7
+ replace: (response) => ({
8
+ statusCode: response.statusCode,
9
+ statusMessage: response.statusMessage ?? STATUS_CODES[response.statusCode],
10
+
11
+ headers: response.getHeaders(),
12
+ headersSent: response.headersSent,
13
+ writableEnded: response.writableEnded,
14
+ destroyed: response.destroyed,
15
+ }),
16
+ };
@@ -0,0 +1,8 @@
1
+ export { BufferReplacer } from './buffers.js';
2
+ export { ErrorReplacer } from './error.js';
3
+ export { HttpClientRequestReplacer } from './http-client-request.js';
4
+ export { HttpIncomingMessageReplacer } from './http-incoming-message.js';
5
+ export { HttpServerResponseReplacer } from './http-server-response.js';
6
+ export { LongStringReplacer } from './long-strings.js';
7
+ export { SecretDelete, SecretObscure } from './secrets.js';
8
+ export { NetSocketReplacer } from './net-socket.js';
@@ -0,0 +1,11 @@
1
+ import { environment } from '../utilities/environment.js';
2
+
3
+ /** @type {import('../replacer.js').Replacer<string>} */
4
+ export const LongStringReplacer = {
5
+ name: 'LongString',
6
+ shouldReplace: (input) => (
7
+ typeof input === 'string' &&
8
+ input.length > environment.stringMaxLength
9
+ ),
10
+ replace: (value) => `${value.slice(0, environment.stringMaxLength)} ... (truncated)`,
11
+ };
@@ -0,0 +1,21 @@
1
+ import { Socket } from 'node:net';
2
+
3
+ /** @type {import('../replacer.js').Replacer<Socket>} */
4
+ export const NetSocketReplacer = {
5
+ name: 'NetSocket',
6
+ shouldReplace: (input) => input instanceof Socket,
7
+ replace: (socket) => ({
8
+ bytesRead: socket.bytesRead,
9
+ bytesWritten: socket.bytesWritten,
10
+
11
+ connecting: socket.connecting,
12
+ pending: socket.pending,
13
+ destroyed: socket.destroyed,
14
+ local: typeof socket.localAddress === 'string' && typeof socket.localPort === 'number'
15
+ ? `${socket.localAddress}:${socket.localPort}`
16
+ : null,
17
+ remote: typeof socket.remoteAddress === 'string' && typeof socket.remotePort === 'number'
18
+ ? `${socket.remoteAddress}:${socket.remotePort}`
19
+ : null,
20
+ }),
21
+ };
@@ -0,0 +1,50 @@
1
+ import { environment } from '../utilities/environment.js';
2
+ import { nodesToPath } from '../utilities/json-path.js';
3
+
4
+ /** @type {import('../replacer.js').Replacer} */
5
+ export const SecretDelete = {
6
+ name: 'SecretDelete',
7
+ stopHere: true,
8
+ shouldReplace: (_, original) => {
9
+ if (original == null) {
10
+ return false;
11
+ }
12
+
13
+ const path = original.join('.');
14
+ for (const filter of environment.secretFilters.blocked) {
15
+ if (filter.test(path)) {
16
+ return true;
17
+ }
18
+ }
19
+
20
+ return false;
21
+ },
22
+ replace: () => null,
23
+ };
24
+
25
+ /** @type {import('../replacer.js').Replacer} */
26
+ export const SecretObscure = {
27
+ name: 'SecretObscure',
28
+ stopHere: true,
29
+ shouldReplace: (_, original) => {
30
+ if (original == null) {
31
+ return false;
32
+ }
33
+
34
+ const path = original.join('.');
35
+ for (const filter of environment.secretFilters.allowed) {
36
+ if (filter.test(path)) {
37
+ return true;
38
+ }
39
+ }
40
+
41
+ return false;
42
+ },
43
+ replace: (_, path) => {
44
+ if (path == null) {
45
+ return `[secret]`;
46
+ }
47
+
48
+ return `[secret ${nodesToPath(path)}]`;
49
+ },
50
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @param {string} output What should be written to stdout
3
+ * @returns {void}
4
+ */
5
+ export function stdoutWrite(output) {
6
+ console.info(output);
7
+ }