* Creates a circular-safe replacer function for JSON.stringify * @param {string[]} keysToSkip - Keys to skip during serialization to break circular references * @returns {Function} Replacer function for JSON.stringify
(keysToSkip = [])
| 624 | * @returns {Function} Replacer function for JSON.stringify |
| 625 | */ |
| 626 | function createCircularSafeReplacer(keysToSkip = []) { |
| 627 | const seen = new WeakSet() |
| 628 | const defaultSkipKeys = ['parent', 'tests', 'suite', 'root', 'runner', 'ctx'] |
| 629 | const skipKeys = new Set([...defaultSkipKeys, ...keysToSkip]) |
| 630 | |
| 631 | return function (key, value) { |
| 632 | // Skip specific keys that commonly cause circular references |
| 633 | if (key && skipKeys.has(key)) { |
| 634 | return undefined |
| 635 | } |
| 636 | |
| 637 | // Coerce types that JSON.stringify can't handle natively |
| 638 | if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]` |
| 639 | if (typeof value === 'bigint') return `${value.toString()}n` |
| 640 | if (typeof value === 'symbol') return value.toString() |
| 641 | if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack } |
| 642 | |
| 643 | if (value === null || typeof value !== 'object') { |
| 644 | return value |
| 645 | } |
| 646 | |
| 647 | // Handle circular references |
| 648 | if (seen.has(value)) { |
| 649 | return `[Circular Reference to ${value.constructor?.name || 'Object'}]` |
| 650 | } |
| 651 | |
| 652 | seen.add(value) |
| 653 | return value |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | /** |
| 658 | * Safely stringify an object, handling circular references |
no test coverage detected