Spaces:
Runtime error
Runtime error
| module.exports = { | |
| groupRedact, | |
| groupRestore, | |
| nestedRedact, | |
| nestedRestore | |
| } | |
| function groupRestore ({ keys, values, target }) { | |
| if (target == null || typeof target === 'string') return | |
| const length = keys.length | |
| for (var i = 0; i < length; i++) { | |
| const k = keys[i] | |
| target[k] = values[i] | |
| } | |
| } | |
| function groupRedact (o, path, censor, isCensorFct, censorFctTakesPath) { | |
| const target = get(o, path) | |
| if (target == null || typeof target === 'string') return { keys: null, values: null, target, flat: true } | |
| const keys = Object.keys(target) | |
| const keysLength = keys.length | |
| const pathLength = path.length | |
| const pathWithKey = censorFctTakesPath ? [...path] : undefined | |
| const values = new Array(keysLength) | |
| for (var i = 0; i < keysLength; i++) { | |
| const key = keys[i] | |
| values[i] = target[key] | |
| if (censorFctTakesPath) { | |
| pathWithKey[pathLength] = key | |
| target[key] = censor(target[key], pathWithKey) | |
| } else if (isCensorFct) { | |
| target[key] = censor(target[key]) | |
| } else { | |
| target[key] = censor | |
| } | |
| } | |
| return { keys, values, target, flat: true } | |
| } | |
| /** | |
| * @param {RestoreInstruction[]} instructions a set of instructions for restoring values to objects | |
| */ | |
| function nestedRestore (instructions) { | |
| for (let i = 0; i < instructions.length; i++) { | |
| const { target, path, value } = instructions[i] | |
| let current = target | |
| for (let i = path.length - 1; i > 0; i--) { | |
| current = current[path[i]] | |
| } | |
| current[path[0]] = value | |
| } | |
| } | |
| function nestedRedact (store, o, path, ns, censor, isCensorFct, censorFctTakesPath) { | |
| const target = get(o, path) | |
| if (target == null) return | |
| const keys = Object.keys(target) | |
| const keysLength = keys.length | |
| for (var i = 0; i < keysLength; i++) { | |
| const key = keys[i] | |
| specialSet(store, target, key, path, ns, censor, isCensorFct, censorFctTakesPath) | |
| } | |
| return store | |
| } | |
| function has (obj, prop) { | |
| return obj !== undefined && obj !== null | |
| ? ('hasOwn' in Object ? Object.hasOwn(obj, prop) : Object.prototype.hasOwnProperty.call(obj, prop)) | |
| : false | |
| } | |
| function specialSet (store, o, k, path, afterPath, censor, isCensorFct, censorFctTakesPath) { | |
| const afterPathLen = afterPath.length | |
| const lastPathIndex = afterPathLen - 1 | |
| const originalKey = k | |
| var i = -1 | |
| var n | |
| var nv | |
| var ov | |
| var oov = null | |
| var wc = null | |
| var kIsWc | |
| var wcov | |
| var consecutive = false | |
| var level = 0 | |
| // need to track depth of the `redactPath` tree | |
| var depth = 0 | |
| var redactPathCurrent = tree() | |
| ov = n = o[k] | |
| if (typeof n !== 'object') return | |
| while (n != null && ++i < afterPathLen) { | |
| depth += 1 | |
| k = afterPath[i] | |
| oov = ov | |
| if (k !== '*' && !wc && !(typeof n === 'object' && k in n)) { | |
| break | |
| } | |
| if (k === '*') { | |
| if (wc === '*') { | |
| consecutive = true | |
| } | |
| wc = k | |
| if (i !== lastPathIndex) { | |
| continue | |
| } | |
| } | |
| if (wc) { | |
| const wcKeys = Object.keys(n) | |
| for (var j = 0; j < wcKeys.length; j++) { | |
| const wck = wcKeys[j] | |
| wcov = n[wck] | |
| kIsWc = k === '*' | |
| if (consecutive) { | |
| redactPathCurrent = node(redactPathCurrent, wck, depth) | |
| level = i | |
| ov = iterateNthLevel(wcov, level - 1, k, path, afterPath, censor, isCensorFct, censorFctTakesPath, originalKey, n, nv, ov, kIsWc, wck, i, lastPathIndex, redactPathCurrent, store, o[originalKey], depth + 1) | |
| } else { | |
| if (kIsWc || (typeof wcov === 'object' && wcov !== null && k in wcov)) { | |
| if (kIsWc) { | |
| ov = wcov | |
| } else { | |
| ov = wcov[k] | |
| } | |
| nv = (i !== lastPathIndex) | |
| ? ov | |
| : (isCensorFct | |
| ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) | |
| : censor) | |
| if (kIsWc) { | |
| const rv = restoreInstr(node(redactPathCurrent, wck, depth), ov, o[originalKey]) | |
| store.push(rv) | |
| n[wck] = nv | |
| } else { | |
| if (wcov[k] === nv) { | |
| // pass | |
| } else if ((nv === undefined && censor !== undefined) || (has(wcov, k) && nv === ov)) { | |
| redactPathCurrent = node(redactPathCurrent, wck, depth) | |
| } else { | |
| redactPathCurrent = node(redactPathCurrent, wck, depth) | |
| const rv = restoreInstr(node(redactPathCurrent, k, depth + 1), ov, o[originalKey]) | |
| store.push(rv) | |
| wcov[k] = nv | |
| } | |
| } | |
| } | |
| } | |
| } | |
| wc = null | |
| } else { | |
| ov = n[k] | |
| redactPathCurrent = node(redactPathCurrent, k, depth) | |
| nv = (i !== lastPathIndex) | |
| ? ov | |
| : (isCensorFct | |
| ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) | |
| : censor) | |
| if ((has(n, k) && nv === ov) || (nv === undefined && censor !== undefined)) { | |
| // pass | |
| } else { | |
| const rv = restoreInstr(redactPathCurrent, ov, o[originalKey]) | |
| store.push(rv) | |
| n[k] = nv | |
| } | |
| n = n[k] | |
| } | |
| if (typeof n !== 'object') break | |
| // prevent circular structure, see https://github.com/pinojs/pino/issues/1513 | |
| if (ov === oov || typeof ov === 'undefined') { | |
| // pass | |
| } | |
| } | |
| } | |
| function get (o, p) { | |
| var i = -1 | |
| var l = p.length | |
| var n = o | |
| while (n != null && ++i < l) { | |
| n = n[p[i]] | |
| } | |
| return n | |
| } | |
| function iterateNthLevel (wcov, level, k, path, afterPath, censor, isCensorFct, censorFctTakesPath, originalKey, n, nv, ov, kIsWc, wck, i, lastPathIndex, redactPathCurrent, store, parent, depth) { | |
| if (level === 0) { | |
| if (kIsWc || (typeof wcov === 'object' && wcov !== null && k in wcov)) { | |
| if (kIsWc) { | |
| ov = wcov | |
| } else { | |
| ov = wcov[k] | |
| } | |
| nv = (i !== lastPathIndex) | |
| ? ov | |
| : (isCensorFct | |
| ? (censorFctTakesPath ? censor(ov, [...path, originalKey, ...afterPath]) : censor(ov)) | |
| : censor) | |
| if (kIsWc) { | |
| const rv = restoreInstr(redactPathCurrent, ov, parent) | |
| store.push(rv) | |
| n[wck] = nv | |
| } else { | |
| if (wcov[k] === nv) { | |
| // pass | |
| } else if ((nv === undefined && censor !== undefined) || (has(wcov, k) && nv === ov)) { | |
| // pass | |
| } else { | |
| const rv = restoreInstr(node(redactPathCurrent, k, depth + 1), ov, parent) | |
| store.push(rv) | |
| wcov[k] = nv | |
| } | |
| } | |
| } | |
| } | |
| for (const key in wcov) { | |
| if (typeof wcov[key] === 'object') { | |
| redactPathCurrent = node(redactPathCurrent, key, depth) | |
| iterateNthLevel(wcov[key], level - 1, k, path, afterPath, censor, isCensorFct, censorFctTakesPath, originalKey, n, nv, ov, kIsWc, wck, i, lastPathIndex, redactPathCurrent, store, parent, depth + 1) | |
| } | |
| } | |
| } | |
| /** | |
| * @typedef {object} TreeNode | |
| * @prop {TreeNode} [parent] reference to the parent of this node in the tree, or `null` if there is no parent | |
| * @prop {string} key the key that this node represents (key here being part of the path being redacted | |
| * @prop {TreeNode[]} children the child nodes of this node | |
| * @prop {number} depth the depth of this node in the tree | |
| */ | |
| /** | |
| * instantiate a new, empty tree | |
| * @returns {TreeNode} | |
| */ | |
| function tree () { | |
| return { parent: null, key: null, children: [], depth: 0 } | |
| } | |
| /** | |
| * creates a new node in the tree, attaching it as a child of the provided parent node | |
| * if the specified depth matches the parent depth, adds the new node as a _sibling_ of the parent instead | |
| * @param {TreeNode} parent the parent node to add a new node to (if the parent depth matches the provided `depth` value, will instead add as a sibling of this | |
| * @param {string} key the key that the new node represents (key here being part of the path being redacted) | |
| * @param {number} depth the depth of the new node in the tree - used to determing whether to add the new node as a child or sibling of the provided `parent` node | |
| * @returns {TreeNode} a reference to the newly created node in the tree | |
| */ | |
| function node (parent, key, depth) { | |
| if (parent.depth === depth) { | |
| return node(parent.parent, key, depth) | |
| } | |
| var child = { | |
| parent, | |
| key, | |
| depth, | |
| children: [] | |
| } | |
| parent.children.push(child) | |
| return child | |
| } | |
| /** | |
| * @typedef {object} RestoreInstruction | |
| * @prop {string[]} path a reverse-order path that can be used to find the correct insertion point to restore a `value` for the given `parent` object | |
| * @prop {*} value the value to restore | |
| * @prop {object} target the object to restore the `value` in | |
| */ | |
| /** | |
| * create a restore instruction for the given redactPath node | |
| * generates a path in reverse order by walking up the redactPath tree | |
| * @param {TreeNode} node a tree node that should be at the bottom of the redact path (i.e. have no children) - this will be used to walk up the redact path tree to construct the path needed to restore | |
| * @param {*} value the value to restore | |
| * @param {object} target a reference to the parent object to apply the restore instruction to | |
| * @returns {RestoreInstruction} an instruction used to restore a nested value for a specific object | |
| */ | |
| function restoreInstr (node, value, target) { | |
| let current = node | |
| const path = [] | |
| do { | |
| path.push(current.key) | |
| current = current.parent | |
| } while (current.parent != null) | |
| return { path, value, target } | |
| } | |