fix(logging): preserve shared diagnostics

This commit is contained in:
4gray committed 2026-07-13 22:25:39 +02:00
1 parent 135dae2b15
commit e25c8fc35f
3 files changed
+64 -35

No files matched your search

@@ -1,3 +1,5 @@
import { redactSensitiveData } from '@iptvnator/shared/logging';
const TRACE_ENV_TRUE_VALUES = new Set(['1', 'true', 'yes', 'on']);
const TRACE_PREFIX = '[IPTVnator Trace]';
const MAX_TRACE_ARRAY_ITEMS = 5;
@@ -166,8 +168,7 @@ export function trace(scope: string, message: string, payload?: unknown): void {
console.log(
`${TRACE_PREFIX}[${scope}] ${message} ${safeStringifyForTrace(
summarizeForTrace(redactSensitiveData(payload))
summarizeForTrace(payload)
)}`
);
}
import { redactSensitiveData } from '@iptvnator/shared/logging';
@@ -121,4 +121,24 @@ describe('redactSensitiveData', () => {
expect(serialized(result)).not.toContain(TEST_SECRETS[2]);
expect(serialized(result)).toContain('[Truncated');
});
it('preserves repeated non-circular references while still redacting them', () => {
const shared = {
operation: 'get_profile',
password: TEST_SECRETS[2],
};
const result = redactSensitiveData({ first: shared, second: shared });
expect(result).toEqual({
first: {
operation: 'get_profile',
password: REDACTED_VALUE,
},
second: {
operation: 'get_profile',
password: REDACTED_VALUE,
},
});
});
});
@@ -277,43 +277,51 @@ export function redactSensitiveData(
}
seen.add(object);
if (input instanceof URL) {
return redactUrl(input, (entry) => visitString(entry, depth + 1));
}
if (input instanceof URLSearchParams) {
return redactSearchParams(input, (entry) =>
visitString(entry, depth + 1)
).toString();
}
if (input instanceof Error) {
return visitError(input, depth);
}
if (input instanceof Date) {
return input.toISOString();
}
if (Array.isArray(input)) {
const output = input
.slice(0, resolved.maxArrayItems)
.map((entry) => visit(entry, depth + 1));
if (input.length > resolved.maxArrayItems) {
output.push(
`[Truncated ${input.length - resolved.maxArrayItems} items]`
try {
if (input instanceof URL) {
return redactUrl(input, (entry) =>
visitString(entry, depth + 1)
);
}
return output;
}
if (input instanceof Map) {
const entries: Record<string, unknown> = {};
for (const [key, entry] of input) {
entries[String(key)] = entry;
if (input instanceof URLSearchParams) {
return redactSearchParams(input, (entry) =>
visitString(entry, depth + 1)
).toString();
}
if (input instanceof Error) {
return visitError(input, depth);
}
if (input instanceof Date) {
return input.toISOString();
}
if (Array.isArray(input)) {
const output = input
.slice(0, resolved.maxArrayItems)
.map((entry) => visit(entry, depth + 1));
if (input.length > resolved.maxArrayItems) {
output.push(
`[Truncated ${
input.length - resolved.maxArrayItems
} items]`
);
}
return output;
}
if (input instanceof Map) {
const entries: Record<string, unknown> = {};
for (const [key, entry] of input) {
entries[String(key)] = entry;
}
return visitObject(entries, depth);
}
if (input instanceof Set) {
return visit(Array.from(input), depth);
}
return visitObject(entries, depth);
}
if (input instanceof Set) {
return visit(Array.from(input), depth);
}
return visitObject(input as Record<string, unknown>, depth);
return visitObject(input as Record<string, unknown>, depth);
} finally {
seen.delete(object);
}
};
return visit(value, 0);