@geekmidas/logger 0.3.0 → 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 (41) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/console.cjs.map +1 -1
  3. package/dist/console.d.cts +2 -1
  4. package/dist/console.d.cts.map +1 -0
  5. package/dist/console.d.mts +2 -1
  6. package/dist/console.d.mts.map +1 -0
  7. package/dist/console.mjs.map +1 -1
  8. package/dist/index.d.cts +1 -1
  9. package/dist/index.d.mts +1 -1
  10. package/dist/pino.cjs.map +1 -1
  11. package/dist/pino.d.cts +3 -2
  12. package/dist/pino.d.cts.map +1 -0
  13. package/dist/pino.d.mts +3 -2
  14. package/dist/pino.d.mts.map +1 -0
  15. package/dist/pino.mjs.map +1 -1
  16. package/dist/{redact-paths-Br-tI2GZ.d.cts → redact-paths-CsK0_uz-.d.cts} +2 -1
  17. package/dist/redact-paths-CsK0_uz-.d.cts.map +1 -0
  18. package/dist/{redact-paths-CIsuxHH7.d.mts → redact-paths-D07eTMlH.d.mts} +2 -1
  19. package/dist/redact-paths-D07eTMlH.d.mts.map +1 -0
  20. package/dist/redact-paths-D0m0DIuQ.cjs.map +1 -1
  21. package/dist/redact-paths-DQoIXhkS.mjs.map +1 -1
  22. package/dist/redact-paths.d.cts +1 -1
  23. package/dist/redact-paths.d.mts +1 -1
  24. package/dist/{types-Bga8WDuP.d.mts → types-BDdpcrpy.d.mts} +2 -1
  25. package/dist/types-BDdpcrpy.d.mts.map +1 -0
  26. package/dist/{types-JxCFymH0.d.cts → types-Dk_k2-f_.d.cts} +2 -1
  27. package/dist/types-Dk_k2-f_.d.cts.map +1 -0
  28. package/dist/types-ag_0Cvbg.cjs.map +1 -1
  29. package/dist/types-yQ6XOihF.mjs.map +1 -1
  30. package/dist/types.d.cts +1 -1
  31. package/dist/types.d.mts +1 -1
  32. package/package.json +1 -1
  33. package/src/__tests__/console.spec.ts +648 -648
  34. package/src/__tests__/pino-redaction.integration.spec.ts +270 -270
  35. package/src/__tests__/pino.spec.ts +305 -305
  36. package/src/console.ts +100 -100
  37. package/src/index.ts +5 -5
  38. package/src/pino.ts +50 -50
  39. package/src/redact-paths.ts +52 -52
  40. package/src/types.ts +87 -87
  41. package/tsconfig.json +9 -0
@@ -4,352 +4,352 @@ import { LogLevel } from '../types';
4
4
 
5
5
  // Mock pino module
6
6
  vi.mock('pino', () => ({
7
- pino: vi.fn((options) => ({
8
- _options: options,
9
- debug: vi.fn(),
10
- info: vi.fn(),
11
- warn: vi.fn(),
12
- error: vi.fn(),
13
- fatal: vi.fn(),
14
- trace: vi.fn(),
15
- })),
7
+ pino: vi.fn((options) => ({
8
+ _options: options,
9
+ debug: vi.fn(),
10
+ info: vi.fn(),
11
+ warn: vi.fn(),
12
+ error: vi.fn(),
13
+ fatal: vi.fn(),
14
+ trace: vi.fn(),
15
+ })),
16
16
  }));
17
17
 
18
18
  describe('Pino Logger', () => {
19
- let pinoMock: any;
19
+ let pinoMock: any;
20
20
 
21
- beforeEach(async () => {
22
- vi.clearAllMocks();
23
- const pinoModule = await import('pino');
24
- pinoMock = pinoModule.pino;
25
- });
21
+ beforeEach(async () => {
22
+ vi.clearAllMocks();
23
+ const pinoModule = await import('pino');
24
+ pinoMock = pinoModule.pino;
25
+ });
26
26
 
27
- describe('createLogger', () => {
28
- it('should create pino logger with default options', async () => {
29
- const { createLogger } = await import('../pino');
27
+ describe('createLogger', () => {
28
+ it('should create pino logger with default options', async () => {
29
+ const { createLogger } = await import('../pino');
30
30
 
31
- const logger = createLogger({});
31
+ const logger = createLogger({});
32
32
 
33
- expect(pinoMock).toHaveBeenCalled();
34
- expect(logger).toBeDefined();
35
- });
33
+ expect(pinoMock).toHaveBeenCalled();
34
+ expect(logger).toBeDefined();
35
+ });
36
36
 
37
- it('should create logger with pretty formatting in development', async () => {
38
- const { createLogger } = await import('../pino');
39
- const originalEnv = process.env.NODE_ENV;
40
- process.env.NODE_ENV = 'development';
37
+ it('should create logger with pretty formatting in development', async () => {
38
+ const { createLogger } = await import('../pino');
39
+ const originalEnv = process.env.NODE_ENV;
40
+ process.env.NODE_ENV = 'development';
41
41
 
42
- createLogger({ pretty: true });
42
+ createLogger({ pretty: true });
43
43
 
44
- expect(pinoMock).toHaveBeenCalledWith(
45
- expect.objectContaining({
46
- transport: {
47
- target: 'pino-pretty',
48
- options: { colorize: true },
49
- },
50
- }),
51
- );
44
+ expect(pinoMock).toHaveBeenCalledWith(
45
+ expect.objectContaining({
46
+ transport: {
47
+ target: 'pino-pretty',
48
+ options: { colorize: true },
49
+ },
50
+ }),
51
+ );
52
52
 
53
- process.env.NODE_ENV = originalEnv;
54
- });
53
+ process.env.NODE_ENV = originalEnv;
54
+ });
55
55
 
56
- it('should not use pretty formatting when pretty is false', async () => {
57
- const { createLogger } = await import('../pino');
56
+ it('should not use pretty formatting when pretty is false', async () => {
57
+ const { createLogger } = await import('../pino');
58
58
 
59
- createLogger({ pretty: false });
59
+ createLogger({ pretty: false });
60
60
 
61
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
62
- expect(callArgs.transport).toBeUndefined();
63
- });
61
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
62
+ expect(callArgs.transport).toBeUndefined();
63
+ });
64
64
 
65
- it('should configure formatters', async () => {
66
- const { createLogger } = await import('../pino');
65
+ it('should configure formatters', async () => {
66
+ const { createLogger } = await import('../pino');
67
67
 
68
- createLogger({});
68
+ createLogger({});
69
69
 
70
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
71
- expect(callArgs.formatters).toBeDefined();
72
- expect(callArgs.formatters.bindings).toBeInstanceOf(Function);
73
- expect(callArgs.formatters.level).toBeInstanceOf(Function);
74
- });
70
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
71
+ expect(callArgs.formatters).toBeDefined();
72
+ expect(callArgs.formatters.bindings).toBeInstanceOf(Function);
73
+ expect(callArgs.formatters.level).toBeInstanceOf(Function);
74
+ });
75
75
 
76
- it('should format bindings with node version', async () => {
77
- const { createLogger } = await import('../pino');
76
+ it('should format bindings with node version', async () => {
77
+ const { createLogger } = await import('../pino');
78
78
 
79
- createLogger({});
79
+ createLogger({});
80
80
 
81
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
82
- const bindings = callArgs.formatters.bindings();
81
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
82
+ const bindings = callArgs.formatters.bindings();
83
83
 
84
- expect(bindings).toEqual({
85
- nodeVersion: process.version,
86
- });
87
- });
84
+ expect(bindings).toEqual({
85
+ nodeVersion: process.version,
86
+ });
87
+ });
88
88
 
89
- it('should format level labels to uppercase', async () => {
90
- const { createLogger } = await import('../pino');
89
+ it('should format level labels to uppercase', async () => {
90
+ const { createLogger } = await import('../pino');
91
91
 
92
- createLogger({});
92
+ createLogger({});
93
93
 
94
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
95
- const levelFormatter = callArgs.formatters.level;
94
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
95
+ const levelFormatter = callArgs.formatters.level;
96
96
 
97
- expect(levelFormatter('info')).toEqual({ level: 'INFO' });
98
- expect(levelFormatter('debug')).toEqual({ level: 'DEBUG' });
99
- expect(levelFormatter('error')).toEqual({ level: 'ERROR' });
100
- expect(levelFormatter('warn')).toEqual({ level: 'WARN' });
101
- });
97
+ expect(levelFormatter('info')).toEqual({ level: 'INFO' });
98
+ expect(levelFormatter('debug')).toEqual({ level: 'DEBUG' });
99
+ expect(levelFormatter('error')).toEqual({ level: 'ERROR' });
100
+ expect(levelFormatter('warn')).toEqual({ level: 'WARN' });
101
+ });
102
102
 
103
- it('should accept log level option', async () => {
104
- const { createLogger } = await import('../pino');
103
+ it('should accept log level option', async () => {
104
+ const { createLogger } = await import('../pino');
105
105
 
106
- const logger = createLogger({ level: LogLevel.Debug });
106
+ const logger = createLogger({ level: LogLevel.Debug });
107
107
 
108
- expect(logger).toBeDefined();
109
- // Note: The actual level setting depends on pino's implementation
110
- });
108
+ expect(logger).toBeDefined();
109
+ // Note: The actual level setting depends on pino's implementation
110
+ });
111
111
 
112
- it('should handle undefined options', async () => {
113
- const { createLogger } = await import('../pino');
112
+ it('should handle undefined options', async () => {
113
+ const { createLogger } = await import('../pino');
114
114
 
115
- const logger = createLogger({});
115
+ const logger = createLogger({});
116
116
 
117
- expect(logger).toBeDefined();
118
- expect(pinoMock).toHaveBeenCalled();
119
- });
120
- });
117
+ expect(logger).toBeDefined();
118
+ expect(pinoMock).toHaveBeenCalled();
119
+ });
120
+ });
121
121
 
122
- describe('Logger options', () => {
123
- it('should support pretty option', async () => {
124
- const { createLogger } = await import('../pino');
122
+ describe('Logger options', () => {
123
+ it('should support pretty option', async () => {
124
+ const { createLogger } = await import('../pino');
125
125
 
126
- const logger1 = createLogger({ pretty: true });
127
- const logger2 = createLogger({ pretty: false });
126
+ const logger1 = createLogger({ pretty: true });
127
+ const logger2 = createLogger({ pretty: false });
128
128
 
129
- expect(logger1).toBeDefined();
130
- expect(logger2).toBeDefined();
131
- });
129
+ expect(logger1).toBeDefined();
130
+ expect(logger2).toBeDefined();
131
+ });
132
132
 
133
- it('should support level option', async () => {
134
- const { createLogger } = await import('../pino');
133
+ it('should support level option', async () => {
134
+ const { createLogger } = await import('../pino');
135
135
 
136
- const logger1 = createLogger({ level: LogLevel.Info });
137
- const logger2 = createLogger({ level: LogLevel.Debug });
138
- const logger3 = createLogger({ level: LogLevel.Error });
136
+ const logger1 = createLogger({ level: LogLevel.Info });
137
+ const logger2 = createLogger({ level: LogLevel.Debug });
138
+ const logger3 = createLogger({ level: LogLevel.Error });
139
139
 
140
- expect(logger1).toBeDefined();
141
- expect(logger2).toBeDefined();
142
- expect(logger3).toBeDefined();
143
- });
140
+ expect(logger1).toBeDefined();
141
+ expect(logger2).toBeDefined();
142
+ expect(logger3).toBeDefined();
143
+ });
144
144
 
145
- it('should support combined options', async () => {
146
- const { createLogger } = await import('../pino');
145
+ it('should support combined options', async () => {
146
+ const { createLogger } = await import('../pino');
147
147
 
148
- const logger = createLogger({
149
- pretty: true,
150
- level: LogLevel.Debug,
151
- });
148
+ const logger = createLogger({
149
+ pretty: true,
150
+ level: LogLevel.Debug,
151
+ });
152
152
 
153
- expect(logger).toBeDefined();
154
- expect(pinoMock).toHaveBeenCalled();
155
- });
156
- });
153
+ expect(logger).toBeDefined();
154
+ expect(pinoMock).toHaveBeenCalled();
155
+ });
156
+ });
157
157
 
158
- describe('Redaction', () => {
159
- it('should not configure redaction when redact is undefined', async () => {
160
- const { createLogger } = await import('../pino');
161
-
162
- createLogger({});
158
+ describe('Redaction', () => {
159
+ it('should not configure redaction when redact is undefined', async () => {
160
+ const { createLogger } = await import('../pino');
161
+
162
+ createLogger({});
163
163
 
164
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
165
- expect(callArgs.redact).toBeUndefined();
166
- });
164
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
165
+ expect(callArgs.redact).toBeUndefined();
166
+ });
167
167
 
168
- it('should not configure redaction when redact is false', async () => {
169
- const { createLogger } = await import('../pino');
170
-
171
- createLogger({ redact: false });
172
-
173
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
174
- expect(callArgs.redact).toBeUndefined();
175
- });
176
-
177
- it('should use default redact paths when redact is true', async () => {
178
- const { createLogger } = await import('../pino');
179
-
180
- createLogger({ redact: true });
181
-
182
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
183
- expect(callArgs.redact).toEqual(DEFAULT_REDACT_PATHS);
184
- });
185
-
186
- it('should merge custom paths with defaults when redact is an array', async () => {
187
- const { createLogger } = await import('../pino');
188
- const customPaths = ['custom.field', 'user.ssn'];
189
-
190
- createLogger({ redact: customPaths });
191
-
192
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
193
- // Should include both defaults and custom paths
194
- expect(callArgs.redact).toEqual([
195
- ...DEFAULT_REDACT_PATHS,
196
- ...customPaths,
197
- ]);
198
- });
199
-
200
- it('should merge object config paths with defaults by default', async () => {
201
- const { createLogger } = await import('../pino');
202
- const customPaths = ['custom.field'];
203
-
204
- createLogger({
205
- redact: {
206
- paths: customPaths,
207
- censor: '***HIDDEN***',
208
- },
209
- });
210
-
211
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
212
- expect(callArgs.redact.paths).toEqual([
213
- ...DEFAULT_REDACT_PATHS,
214
- ...customPaths,
215
- ]);
216
- expect(callArgs.redact.censor).toBe('***HIDDEN***');
217
- });
218
-
219
- it('should override defaults when resolution is override', async () => {
220
- const { createLogger } = await import('../pino');
221
- const customPaths = ['only.this.path'];
222
-
223
- createLogger({
224
- redact: {
225
- paths: customPaths,
226
- resolution: 'override',
227
- },
228
- });
229
-
230
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
231
- expect(callArgs.redact.paths).toEqual(customPaths);
232
- // Should not include defaults
233
- expect(callArgs.redact.paths).not.toContain('password');
234
- });
235
-
236
- it('should merge with defaults when resolution is merge', async () => {
237
- const { createLogger } = await import('../pino');
238
- const customPaths = ['extra.secret'];
239
-
240
- createLogger({
241
- redact: {
242
- paths: customPaths,
243
- resolution: 'merge',
244
- },
245
- });
246
-
247
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
248
- expect(callArgs.redact.paths).toEqual([
249
- ...DEFAULT_REDACT_PATHS,
250
- ...customPaths,
251
- ]);
252
- });
253
-
254
- it('should support remove option in redact config', async () => {
255
- const { createLogger } = await import('../pino');
256
-
257
- createLogger({
258
- redact: {
259
- paths: ['temp.data'],
260
- remove: true,
261
- },
262
- });
263
-
264
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
265
- expect(callArgs.redact.remove).toBe(true);
266
- });
267
-
268
- it('should support censor function in redact config', async () => {
269
- const { createLogger } = await import('../pino');
270
- const censorFn = () => '***';
271
-
272
- createLogger({
273
- redact: {
274
- paths: ['secret'],
275
- censor: censorFn,
276
- },
277
- });
278
-
279
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
280
- expect(callArgs.redact.censor).toBe(censorFn);
281
- });
282
-
283
- it('should not include resolution field in pino config', async () => {
284
- const { createLogger } = await import('../pino');
285
-
286
- createLogger({
287
- redact: {
288
- paths: ['secret'],
289
- resolution: 'override',
290
- },
291
- });
292
-
293
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
294
- expect(callArgs.redact).not.toHaveProperty('resolution');
295
- });
296
-
297
- it('should combine redact with other options', async () => {
298
- const { createLogger } = await import('../pino');
299
-
300
- createLogger({
301
- level: LogLevel.Debug,
302
- redact: true,
303
- });
304
-
305
- const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
306
- expect(callArgs.level).toBe(LogLevel.Debug);
307
- expect(callArgs.redact).toEqual(DEFAULT_REDACT_PATHS);
308
- });
309
- });
310
-
311
- describe('DEFAULT_REDACT_PATHS', () => {
312
- it('should include common password fields', () => {
313
- expect(DEFAULT_REDACT_PATHS).toContain('password');
314
- expect(DEFAULT_REDACT_PATHS).toContain('pass');
315
- expect(DEFAULT_REDACT_PATHS).toContain('passwd');
316
- });
317
-
318
- it('should include token fields', () => {
319
- expect(DEFAULT_REDACT_PATHS).toContain('token');
320
- expect(DEFAULT_REDACT_PATHS).toContain('accessToken');
321
- expect(DEFAULT_REDACT_PATHS).toContain('refreshToken');
322
- expect(DEFAULT_REDACT_PATHS).toContain('idToken');
323
- });
324
-
325
- it('should include API key variations', () => {
326
- expect(DEFAULT_REDACT_PATHS).toContain('apiKey');
327
- expect(DEFAULT_REDACT_PATHS).toContain('api_key');
328
- expect(DEFAULT_REDACT_PATHS).toContain('apikey');
329
- });
330
-
331
- it('should include authorization headers', () => {
332
- expect(DEFAULT_REDACT_PATHS).toContain('headers.authorization');
333
- expect(DEFAULT_REDACT_PATHS).toContain('headers.Authorization');
334
- expect(DEFAULT_REDACT_PATHS).toContain('headers.cookie');
335
- });
336
-
337
- it('should include wildcard patterns for nested fields', () => {
338
- expect(DEFAULT_REDACT_PATHS).toContain('*.password');
339
- expect(DEFAULT_REDACT_PATHS).toContain('*.secret');
340
- expect(DEFAULT_REDACT_PATHS).toContain('*.token');
341
- });
342
-
343
- it('should include sensitive personal data fields', () => {
344
- expect(DEFAULT_REDACT_PATHS).toContain('ssn');
345
- expect(DEFAULT_REDACT_PATHS).toContain('creditCard');
346
- expect(DEFAULT_REDACT_PATHS).toContain('cardNumber');
347
- expect(DEFAULT_REDACT_PATHS).toContain('cvv');
348
- });
349
-
350
- it('should include database connection strings', () => {
351
- expect(DEFAULT_REDACT_PATHS).toContain('connectionString');
352
- expect(DEFAULT_REDACT_PATHS).toContain('databaseUrl');
353
- });
354
- });
168
+ it('should not configure redaction when redact is false', async () => {
169
+ const { createLogger } = await import('../pino');
170
+
171
+ createLogger({ redact: false });
172
+
173
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
174
+ expect(callArgs.redact).toBeUndefined();
175
+ });
176
+
177
+ it('should use default redact paths when redact is true', async () => {
178
+ const { createLogger } = await import('../pino');
179
+
180
+ createLogger({ redact: true });
181
+
182
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
183
+ expect(callArgs.redact).toEqual(DEFAULT_REDACT_PATHS);
184
+ });
185
+
186
+ it('should merge custom paths with defaults when redact is an array', async () => {
187
+ const { createLogger } = await import('../pino');
188
+ const customPaths = ['custom.field', 'user.ssn'];
189
+
190
+ createLogger({ redact: customPaths });
191
+
192
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
193
+ // Should include both defaults and custom paths
194
+ expect(callArgs.redact).toEqual([
195
+ ...DEFAULT_REDACT_PATHS,
196
+ ...customPaths,
197
+ ]);
198
+ });
199
+
200
+ it('should merge object config paths with defaults by default', async () => {
201
+ const { createLogger } = await import('../pino');
202
+ const customPaths = ['custom.field'];
203
+
204
+ createLogger({
205
+ redact: {
206
+ paths: customPaths,
207
+ censor: '***HIDDEN***',
208
+ },
209
+ });
210
+
211
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
212
+ expect(callArgs.redact.paths).toEqual([
213
+ ...DEFAULT_REDACT_PATHS,
214
+ ...customPaths,
215
+ ]);
216
+ expect(callArgs.redact.censor).toBe('***HIDDEN***');
217
+ });
218
+
219
+ it('should override defaults when resolution is override', async () => {
220
+ const { createLogger } = await import('../pino');
221
+ const customPaths = ['only.this.path'];
222
+
223
+ createLogger({
224
+ redact: {
225
+ paths: customPaths,
226
+ resolution: 'override',
227
+ },
228
+ });
229
+
230
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
231
+ expect(callArgs.redact.paths).toEqual(customPaths);
232
+ // Should not include defaults
233
+ expect(callArgs.redact.paths).not.toContain('password');
234
+ });
235
+
236
+ it('should merge with defaults when resolution is merge', async () => {
237
+ const { createLogger } = await import('../pino');
238
+ const customPaths = ['extra.secret'];
239
+
240
+ createLogger({
241
+ redact: {
242
+ paths: customPaths,
243
+ resolution: 'merge',
244
+ },
245
+ });
246
+
247
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
248
+ expect(callArgs.redact.paths).toEqual([
249
+ ...DEFAULT_REDACT_PATHS,
250
+ ...customPaths,
251
+ ]);
252
+ });
253
+
254
+ it('should support remove option in redact config', async () => {
255
+ const { createLogger } = await import('../pino');
256
+
257
+ createLogger({
258
+ redact: {
259
+ paths: ['temp.data'],
260
+ remove: true,
261
+ },
262
+ });
263
+
264
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
265
+ expect(callArgs.redact.remove).toBe(true);
266
+ });
267
+
268
+ it('should support censor function in redact config', async () => {
269
+ const { createLogger } = await import('../pino');
270
+ const censorFn = () => '***';
271
+
272
+ createLogger({
273
+ redact: {
274
+ paths: ['secret'],
275
+ censor: censorFn,
276
+ },
277
+ });
278
+
279
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
280
+ expect(callArgs.redact.censor).toBe(censorFn);
281
+ });
282
+
283
+ it('should not include resolution field in pino config', async () => {
284
+ const { createLogger } = await import('../pino');
285
+
286
+ createLogger({
287
+ redact: {
288
+ paths: ['secret'],
289
+ resolution: 'override',
290
+ },
291
+ });
292
+
293
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
294
+ expect(callArgs.redact).not.toHaveProperty('resolution');
295
+ });
296
+
297
+ it('should combine redact with other options', async () => {
298
+ const { createLogger } = await import('../pino');
299
+
300
+ createLogger({
301
+ level: LogLevel.Debug,
302
+ redact: true,
303
+ });
304
+
305
+ const callArgs = pinoMock.mock.calls[pinoMock.mock.calls.length - 1][0];
306
+ expect(callArgs.level).toBe(LogLevel.Debug);
307
+ expect(callArgs.redact).toEqual(DEFAULT_REDACT_PATHS);
308
+ });
309
+ });
310
+
311
+ describe('DEFAULT_REDACT_PATHS', () => {
312
+ it('should include common password fields', () => {
313
+ expect(DEFAULT_REDACT_PATHS).toContain('password');
314
+ expect(DEFAULT_REDACT_PATHS).toContain('pass');
315
+ expect(DEFAULT_REDACT_PATHS).toContain('passwd');
316
+ });
317
+
318
+ it('should include token fields', () => {
319
+ expect(DEFAULT_REDACT_PATHS).toContain('token');
320
+ expect(DEFAULT_REDACT_PATHS).toContain('accessToken');
321
+ expect(DEFAULT_REDACT_PATHS).toContain('refreshToken');
322
+ expect(DEFAULT_REDACT_PATHS).toContain('idToken');
323
+ });
324
+
325
+ it('should include API key variations', () => {
326
+ expect(DEFAULT_REDACT_PATHS).toContain('apiKey');
327
+ expect(DEFAULT_REDACT_PATHS).toContain('api_key');
328
+ expect(DEFAULT_REDACT_PATHS).toContain('apikey');
329
+ });
330
+
331
+ it('should include authorization headers', () => {
332
+ expect(DEFAULT_REDACT_PATHS).toContain('headers.authorization');
333
+ expect(DEFAULT_REDACT_PATHS).toContain('headers.Authorization');
334
+ expect(DEFAULT_REDACT_PATHS).toContain('headers.cookie');
335
+ });
336
+
337
+ it('should include wildcard patterns for nested fields', () => {
338
+ expect(DEFAULT_REDACT_PATHS).toContain('*.password');
339
+ expect(DEFAULT_REDACT_PATHS).toContain('*.secret');
340
+ expect(DEFAULT_REDACT_PATHS).toContain('*.token');
341
+ });
342
+
343
+ it('should include sensitive personal data fields', () => {
344
+ expect(DEFAULT_REDACT_PATHS).toContain('ssn');
345
+ expect(DEFAULT_REDACT_PATHS).toContain('creditCard');
346
+ expect(DEFAULT_REDACT_PATHS).toContain('cardNumber');
347
+ expect(DEFAULT_REDACT_PATHS).toContain('cvv');
348
+ });
349
+
350
+ it('should include database connection strings', () => {
351
+ expect(DEFAULT_REDACT_PATHS).toContain('connectionString');
352
+ expect(DEFAULT_REDACT_PATHS).toContain('databaseUrl');
353
+ });
354
+ });
355
355
  });