UNPKG

@graphql-hive/logger

Version:
1,285 lines (1,209 loc) 36.8 kB
'use strict'; var json = require('./json-CSWNs2TF.cjs'); function createDeferredPromise() { if (Promise.withResolvers) { return Promise.withResolvers(); } let resolveFn; let rejectFn; const promise = new Promise(function deferredPromiseExecutor(resolve, reject) { resolveFn = resolve; rejectFn = reject; }); return { promise, get resolve() { return resolveFn; }, get reject() { return rejectFn; }, }; } const DisposableSymbols = { get asyncDispose() { return Symbol.asyncDispose || Symbol.for('asyncDispose'); }, }; var validator_1 = validator$1; function validator$1 (opts = {}) { const { ERR_PATHS_MUST_BE_STRINGS = () => 'fast-redact - Paths must be (non-empty) strings', ERR_INVALID_PATH = (s) => `fast-redact – Invalid path (${s})` } = opts; return function validate ({ paths }) { paths.forEach((s) => { if (typeof s !== 'string') { throw Error(ERR_PATHS_MUST_BE_STRINGS()) } try { if (/〇/.test(s)) throw Error() const expr = (s[0] === '[' ? '' : '.') + s.replace(/^\*/, '〇').replace(/\.\*/g, '.〇').replace(/\[\*\]/g, '[〇]'); if (/\n|\r|;/.test(expr)) throw Error() if (/\/\*/.test(expr)) throw Error() /* eslint-disable-next-line */ Function(` 'use strict' const o = new Proxy({}, { get: () => o, set: () => { throw Error() } }); const 〇 = null; o${expr} if ([o${expr}].length !== 1) throw Error()`)(); } catch (e) { throw Error(ERR_INVALID_PATH(s)) } }); } } var rx$3 = /[^.[\]]+|\[((?:.)*?)\]/g; const rx$2 = rx$3; var parse_1 = parse$1; function parse$1 ({ paths }) { const wildcards = []; var wcLen = 0; const secret = paths.reduce(function (o, strPath, ix) { var path = strPath.match(rx$2).map((p) => p.replace(/'|"|`/g, '')); const leadingBracket = strPath[0] === '['; path = path.map((p) => { if (p[0] === '[') return p.substr(1, p.length - 2) else return p }); const star = path.indexOf('*'); if (star > -1) { const before = path.slice(0, star); const beforeStr = before.join('.'); const after = path.slice(star + 1, path.length); const nested = after.length > 0; wcLen++; wildcards.push({ before, beforeStr, after, nested }); } else { o[strPath] = { path: path, val: undefined, precensored: false, circle: '', escPath: JSON.stringify(strPath), leadingBracket: leadingBracket }; } return o }, {}); return { wildcards, wcLen, secret } } const rx$1 = rx$3; var redactor_1 = redactor$1; function redactor$1 ({ secret, serialize, wcLen, strict, isCensorFct, censorFctTakesPath }, state) { /* eslint-disable-next-line */ const redact = Function('o', ` if (typeof o !== 'object' || o == null) { ${strictImpl(strict, serialize)} } const { censor, secret } = this const originalSecret = {} const secretKeys = Object.keys(secret) for (var i = 0; i < secretKeys.length; i++) { originalSecret[secretKeys[i]] = secret[secretKeys[i]] } ${redactTmpl(secret, isCensorFct, censorFctTakesPath)} this.compileRestore() ${dynamicRedactTmpl(wcLen > 0, isCensorFct, censorFctTakesPath)} this.secret = originalSecret ${resultTmpl(serialize)} `).bind(state); redact.state = state; if (serialize === false) { redact.restore = (o) => state.restore(o); } return redact } function redactTmpl (secret, isCensorFct, censorFctTakesPath) { return Object.keys(secret).map((path) => { const { escPath, leadingBracket, path: arrPath } = secret[path]; const skip = leadingBracket ? 1 : 0; const delim = leadingBracket ? '' : '.'; const hops = []; var match; while ((match = rx$1.exec(path)) !== null) { const [ , ix ] = match; const { index, input } = match; if (index > skip) hops.push(input.substring(0, index - (ix ? 0 : 1))); } var existence = hops.map((p) => `o${delim}${p}`).join(' && '); if (existence.length === 0) existence += `o${delim}${path} != null`; else existence += ` && o${delim}${path} != null`; const circularDetection = ` switch (true) { ${hops.reverse().map((p) => ` case o${delim}${p} === censor: secret[${escPath}].circle = ${JSON.stringify(p)} break `).join('\n')} } `; const censorArgs = censorFctTakesPath ? `val, ${JSON.stringify(arrPath)}` : `val`; return ` if (${existence}) { const val = o${delim}${path} if (val === censor) { secret[${escPath}].precensored = true } else { secret[${escPath}].val = val o${delim}${path} = ${isCensorFct ? `censor(${censorArgs})` : 'censor'} ${circularDetection} } } ` }).join('\n') } function dynamicRedactTmpl (hasWildcards, isCensorFct, censorFctTakesPath) { return hasWildcards === true ? ` { const { wildcards, wcLen, groupRedact, nestedRedact } = this for (var i = 0; i < wcLen; i++) { const { before, beforeStr, after, nested } = wildcards[i] if (nested === true) { secret[beforeStr] = secret[beforeStr] || [] nestedRedact(secret[beforeStr], o, before, after, censor, ${isCensorFct}, ${censorFctTakesPath}) } else secret[beforeStr] = groupRedact(o, before, censor, ${isCensorFct}, ${censorFctTakesPath}) } } ` : '' } function resultTmpl (serialize) { return serialize === false ? `return o` : ` var s = this.serialize(o) this.restore(o) return s ` } function strictImpl (strict, serialize) { return strict === true ? `throw Error('fast-redact: primitives cannot be redacted')` : serialize === false ? `return o` : `return this.serialize(o)` } var modifiers = { groupRedact: groupRedact$1, groupRestore: groupRestore$1, nestedRedact: nestedRedact$1, nestedRestore: nestedRestore$1 }; function groupRestore$1 ({ 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$1 (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$1 (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$1 (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 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]; 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) ; 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)) ; else { const rv = restoreInstr(redactPathCurrent, ov, o[originalKey]); store.push(rv); n[k] = nv; } n = n[k]; } if (typeof n !== 'object') break } } 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) ; else if ((nv === undefined && censor !== undefined) || (has(wcov, k) && nv === ov)) ; 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 } } const { groupRestore, nestedRestore } = modifiers; var restorer_1 = restorer$1; function restorer$1 () { return function compileRestore () { if (this.restore) { this.restore.state.secret = this.secret; return } const { secret, wcLen } = this; const paths = Object.keys(secret); const resetters = resetTmpl(secret, paths); const hasWildcards = wcLen > 0; const state = hasWildcards ? { secret, groupRestore, nestedRestore } : { secret }; /* eslint-disable-next-line */ this.restore = Function( 'o', restoreTmpl(resetters, paths, hasWildcards) ).bind(state); this.restore.state = state; } } /** * Mutates the original object to be censored by restoring its original values * prior to censoring. * * @param {object} secret Compiled object describing which target fields should * be censored and the field states. * @param {string[]} paths The list of paths to censor as provided at * initialization time. * * @returns {string} String of JavaScript to be used by `Function()`. The * string compiles to the function that does the work in the description. */ function resetTmpl (secret, paths) { return paths.map((path) => { const { circle, escPath, leadingBracket } = secret[path]; const delim = leadingBracket ? '' : '.'; const reset = circle ? `o.${circle} = secret[${escPath}].val` : `o${delim}${path} = secret[${escPath}].val`; const clear = `secret[${escPath}].val = undefined`; return ` if (secret[${escPath}].val !== undefined) { try { ${reset} } catch (e) {} ${clear} } ` }).join('') } /** * Creates the body of the restore function * * Restoration of the redacted object happens * backwards, in reverse order of redactions, * so that repeated redactions on the same object * property can be eventually rolled back to the * original value. * * This way dynamic redactions are restored first, * starting from the last one working backwards and * followed by the static ones. * * @returns {string} the body of the restore function */ function restoreTmpl (resetters, paths, hasWildcards) { const dynamicReset = hasWildcards === true ? ` const keys = Object.keys(secret) const len = keys.length for (var i = len - 1; i >= ${paths.length}; i--) { const k = keys[i] const o = secret[k] if (o) { if (o.flat === true) this.groupRestore(o) else this.nestedRestore(o) secret[k] = null } } ` : ''; return ` const secret = this.secret ${dynamicReset} ${resetters} return o ` } var state_1 = state$1; function state$1 (o) { const { secret, censor, compileRestore, serialize, groupRedact, nestedRedact, wildcards, wcLen } = o; const builder = [{ secret, censor, compileRestore }]; if (serialize !== false) builder.push({ serialize }); if (wcLen > 0) builder.push({ groupRedact, nestedRedact, wildcards, wcLen }); return Object.assign(...builder) } const validator = validator_1; const parse = parse_1; const redactor = redactor_1; const restorer = restorer_1; const { groupRedact, nestedRedact } = modifiers; const state = state_1; const rx = rx$3; const validate = validator(); const noop = (o) => o; noop.restore = noop; const DEFAULT_CENSOR = '[REDACTED]'; fastRedact$1.rx = rx; fastRedact$1.validator = validator; var fastRedact_1 = fastRedact$1; function fastRedact$1 (opts = {}) { const paths = Array.from(new Set(opts.paths || [])); const serialize = 'serialize' in opts ? ( opts.serialize === false ? opts.serialize : (typeof opts.serialize === 'function' ? opts.serialize : JSON.stringify) ) : JSON.stringify; const remove = opts.remove; if (remove === true && serialize !== JSON.stringify) { throw Error('fast-redact – remove option may only be set when serializer is JSON.stringify') } const censor = remove === true ? undefined : 'censor' in opts ? opts.censor : DEFAULT_CENSOR; const isCensorFct = typeof censor === 'function'; const censorFctTakesPath = isCensorFct && censor.length > 1; if (paths.length === 0) return serialize || noop validate({ paths, serialize, censor }); const { wildcards, wcLen, secret } = parse({ paths}); const compileRestore = restorer(); const strict = 'strict' in opts ? opts.strict : true; return redactor({ secret, wcLen, serialize, strict, isCensorFct, censorFctTakesPath }, state({ secret, censor, compileRestore, serialize, groupRedact, nestedRedact, wildcards, wcLen })) } var fastRedact = /*@__PURE__*/json.getDefaultExportFromCjs(fastRedact_1); function tryStringify (o) { try { return JSON.stringify(o) } catch(e) { return '"[Circular]"' } } var quickFormatUnescaped = format$1; function format$1(f, args, opts) { var ss = (opts && opts.stringify) || tryStringify; var offset = 1; if (typeof f === 'object' && f !== null) { var len = args.length + offset; if (len === 1) return f var objects = new Array(len); objects[0] = ss(f); for (var index = 1; index < len; index++) { objects[index] = ss(args[index]); } return objects.join(' ') } if (typeof f !== 'string') { return f } var argLen = args.length; if (argLen === 0) return f var str = ''; var a = 1 - offset; var lastPos = -1; var flen = (f && f.length) || 0; for (var i = 0; i < flen;) { if (f.charCodeAt(i) === 37 && i + 1 < flen) { lastPos = lastPos > -1 ? lastPos : 0; switch (f.charCodeAt(i + 1)) { case 100: // 'd' case 102: // 'f' if (a >= argLen) break if (args[a] == null) break if (lastPos < i) str += f.slice(lastPos, i); str += Number(args[a]); lastPos = i + 2; i++; break case 105: // 'i' if (a >= argLen) break if (args[a] == null) break if (lastPos < i) str += f.slice(lastPos, i); str += Math.floor(Number(args[a])); lastPos = i + 2; i++; break case 79: // 'O' case 111: // 'o' case 106: // 'j' if (a >= argLen) break if (args[a] === undefined) break if (lastPos < i) str += f.slice(lastPos, i); var type = typeof args[a]; if (type === 'string') { str += '\'' + args[a] + '\''; lastPos = i + 2; i++; break } if (type === 'function') { str += args[a].name || '<anonymous>'; lastPos = i + 2; i++; break } str += ss(args[a]); lastPos = i + 2; i++; break case 115: // 's' if (a >= argLen) break if (lastPos < i) str += f.slice(lastPos, i); str += String(args[a]); lastPos = i + 2; i++; break case 37: // '%' if (lastPos < i) str += f.slice(lastPos, i); str += '%'; lastPos = i + 2; i++; a--; break } ++a; } ++i; } if (lastPos === -1) return f else if (lastPos < flen) { str += f.slice(lastPos); } return str } var format = /*@__PURE__*/json.getDefaultExportFromCjs(quickFormatUnescaped); const logLevel = { trace: 0, debug: 1, info: 2, warn: 3, error: 4 }; function shouldLog(setLevel, loggingLevel) { setLevel = typeof setLevel === "function" ? setLevel() : setLevel; return setLevel !== false && // logging is not disabled logLevel[setLevel] <= logLevel[loggingLevel]; } function logLevelToString(level) { switch (level) { case "trace": return "TRC"; case "debug": return "DBG"; case "info": return "INF"; case "warn": return "WRN"; case "error": return "ERR"; default: throw new Error(`Unknown log level "${level}"`); } } function isPromise(val) { const obj = Object(val); return typeof obj.then === "function" && typeof obj.catch === "function" && typeof obj.finally === "function"; } function parseAttrs(attrs, functionUnwrapDepth = 0) { if (functionUnwrapDepth > 3) { throw new Error("Too much recursion while unwrapping function attributes"); } if (!attrs) { return void 0; } if (typeof attrs === "function") { return parseAttrs(attrs(), functionUnwrapDepth + 1); } if (Array.isArray(attrs)) { return attrs.map((val) => unwrapAttrVal(val)); } if (isPlainObject(attrs)) { const unwrapped = {}; for (const key of Object.keys(attrs)) { const val = attrs[key]; unwrapped[key] = unwrapAttrVal(val); } return unwrapped; } return objectifyClass(attrs); } function unwrapAttrVal(attr, visited = /* @__PURE__ */ new WeakSet()) { if (!attr) { return attr; } if (isPrimitive(attr)) { return attr; } if (typeof attr === "function") { return `[Function: ${attr.name || "(anonymous)"}]`; } if (visited.has(attr)) { return "[Circular]"; } visited.add(attr); if (Array.isArray(attr)) { return attr.map((val) => unwrapAttrVal(val)); } if (isPlainObject(attr)) { const unwrapped = {}; for (const key of Object.keys(attr)) { const val = attr[key]; unwrapped[key] = unwrapAttrVal(val, visited); } return unwrapped; } return objectifyClass(attr, visited); } function isPrimitive(val) { return val !== Object(val); } const nodejsCustomInspectSy = Symbol.for("nodejs.util.inspect.custom"); function objectifyClass(val, visited = /* @__PURE__ */ new WeakSet()) { if ( // simply empty !val || // Object.create(null) Object(val).__proto__ == null ) { return {}; } if (typeof val === "object" && "toJSON" in val && typeof val.toJSON === "function") { return val.toJSON(); } if (typeof val === "object" && nodejsCustomInspectSy in val && typeof val[nodejsCustomInspectSy] === "function") { return { [nodejsCustomInspectSy.toString()]: unwrapAttrVal( val[nodejsCustomInspectSy](Infinity, {}), visited ), class: val.constructor.name }; } const props = {}; for (const propName of Object.getOwnPropertyNames(val)) { props[propName] = unwrapAttrVal(val[propName], visited); } for (const protoPropName of Object.getOwnPropertyNames( Object.getPrototypeOf(val) )) { const propVal = val[protoPropName]; if (typeof propVal === "function") { continue; } props[protoPropName] = unwrapAttrVal(propVal, visited); } return { ...props, class: val.constructor.name }; } function shallowMergeAttributes(target, source) { switch (true) { case (Array.isArray(source) && Array.isArray(target)): return [...target, ...source]; case Array.isArray(source): return target ? [target, ...source] : source; case Array.isArray(target): return source ? [...target, source] : target; case !!(target || source): return { ...target, ...source }; default: return void 0; } } function isPlainObject(val) { return Object(val).constructor === Object && Object.getPrototypeOf(val) === Object.prototype; } const asciMap = { timestamp: "\x1B[90m", // bright black trace: "\x1B[36m", // cyan debug: "\x1B[90m", // bright black info: "\x1B[32m", // green warn: "\x1B[33m", // yellow error: "\x1B[41;39m", // red; white message: "\x1B[1m", // bold key: "\x1B[35m", // magenta reset: "\x1B[0m" // reset }; class ConsoleLogWriter { #console; #noColor; #noTimestamp; #async; constructor(opts = {}) { const { console = globalThis.console, // no color if we're running in browser-like (edge) environments noColor = typeof process === "undefined" || // or no color if https://no-color.org/ json.getEnvBool("NO_COLOR"), noTimestamp = false, async = false } = opts; this.#console = console; this.#noColor = noColor; this.#noTimestamp = noTimestamp; this.#async = async; } color(style, text) { if (!text) { return text; } if (this.#noColor) { return text; } return asciMap[style] + text + asciMap.reset; } #writeToConsole(level, attrs, msg) { this.#console[level === "trace" ? "debug" : level]( [ !this.#noTimestamp && this.color("timestamp", (/* @__PURE__ */ new Date()).toISOString()), this.color(level, logLevelToString(level)), this.color("message", msg), attrs && this.stringifyAttrs(attrs) ].filter(Boolean).join(" ") ); } write(level, attrs, msg) { if (this.#async) { const { promise, resolve } = createDeferredPromise(); setTimeout(() => { this.#writeToConsole(level, attrs, msg); resolve(); }, 0); return promise; } this.#writeToConsole(level, attrs, msg); } stringifyAttrs(attrs) { let log = "\n"; for (const line of json.jsonStringify(attrs, true).split("\n")) { if (line === "{" || line === "}" || line === "[" || line === "]") { continue; } let formattedLine = line; formattedLine = formattedLine.replace( /"([^"]+)":/, this.color("key", "$1:") ); let indentationSize = line.match(/^\s*/)?.[0]?.length || 0; if (indentationSize) indentationSize++; formattedLine = formattedLine.replaceAll( /\\n/g, "\n" + [...Array(indentationSize)].join(" ") ); formattedLine = formattedLine.replace(/,$/, ""); formattedLine = formattedLine.replace( /(\[|\{|\]|\})$/, this.color("key", "$1") ); log += formattedLine + "\n"; } log = log.slice(0, -1); return log; } } class MemoryLogWriter { logs = []; write(level, attrs, msg) { this.logs.push({ level, ...msg ? { msg } : {}, ...attrs ? { attrs } : {} }); } } class Logger { #level; #prefix; #attrs; #writers; #pendingWrites; #redact; #redactOption; constructor(opts = {}) { let logLevelEnv = json.getEnvStr("LOG_LEVEL"); if (logLevelEnv && !(logLevelEnv in logLevel)) { throw new Error( `Invalid LOG_LEVEL environment variable "${logLevelEnv}". Must be one of: ${[...Object.keys(logLevel), "false"].join(", ")}` ); } this.#level = opts.level ?? logLevelEnv ?? (json.getEnvBool("DEBUG") ? "debug" : "info"); this.#prefix = opts.prefix; this.#attrs = opts.attrs; this.#writers = opts.writers ?? (json.getEnvBool("LOG_JSON") ? [new json.JSONLogWriter()] : [new ConsoleLogWriter()]); if (opts.redact) { this.#redactOption = opts.redact; const paths = Array.isArray(opts.redact) ? opts.redact : opts.redact.paths; const censor = Array.isArray(opts.redact) ? void 0 : opts.redact.censor; const remove = Array.isArray(opts.redact) ? void 0 : opts.redact.remove; if (paths.length > 0) { this.#redact = fastRedact({ paths, censor: remove ? void 0 : censor ?? "[Redacted]", serialize: false, strict: false }); } } } /** The prefix that's prepended to each log message. */ get prefix() { return this.#prefix; } /** * The attributes that are added to each log. If the log itself contains * attributes with keys existing in {@link attrs}, the log's attributes will * override. */ get attrs() { return this.#attrs; } /** The current {@link LogLevel} of the logger. You can change the level using the {@link setLevel} method. */ get level() { return typeof this.#level === "function" ? this.#level() : this.#level; } /** * Sets the new {@link LogLevel} of the logger. All subsequent logs, and {@link child child loggers} whose * level did not change, will respect the new level. */ setLevel(level) { this.#level = level; } write(level, attrs, msg) { for (const w of this.#writers) { const write$ = w.write(level, attrs, msg); if (isPromise(write$)) { this.#pendingWrites ??= /* @__PURE__ */ new Set(); this.#pendingWrites.add(write$); write$.then(() => { this.#pendingWrites.delete(write$); }).catch((e) => { console.error("Failed to write async log", e); }); } } } flush() { const writerFlushes = this.#writers.map((w) => w.flush).filter((f) => !!f); if (this.#pendingWrites?.size || writerFlushes.length) { const errs = []; return Promise.allSettled([ ...Array.from(this.#pendingWrites || []).map( (w) => w.catch((err) => errs.push(err)) ), ...Array.from(writerFlushes || []).map(async (f) => { try { await f(); } catch (err) { errs.push(err); } }) ]).then(() => { this.#pendingWrites?.clear(); if (errs.length === 1) { throw new Error("Failed to flush", { cause: errs[0] }); } else if (errs.length) { throw new AggregateError( errs, `Failed to flush with ${errs.length} errors` ); } }); } return; } async [DisposableSymbols.asyncDispose]() { return this.flush(); } child(prefixOrAttrs, prefix) { if (typeof prefixOrAttrs === "string") { return new Logger({ level: () => this.level, // inherits the parent level (yet can be changed on child only when using setLevel) prefix: (this.#prefix || "") + prefixOrAttrs, attrs: this.#attrs, writers: this.#writers, redact: this.#redactOption }); } return new Logger({ level: () => this.level, // inherits the parent level (yet can be changed on child only when using setLevel) prefix: (this.#prefix || "") + (prefix || "") || void 0, attrs: shallowMergeAttributes(this.#attrs, prefixOrAttrs), writers: this.#writers, redact: this.#redactOption }); } log(level, maybeAttrsOrMsg, ...rest) { if (!shouldLog(this.#level, level)) { return; } let msg; let attrs; if (typeof maybeAttrsOrMsg === "string") { msg = maybeAttrsOrMsg; } else if (maybeAttrsOrMsg) { attrs = maybeAttrsOrMsg; if (typeof rest[0] === "string") { msg = rest.shift(); } } if (this.#prefix) { msg = `${this.#prefix}${msg || ""}`.trim(); } attrs = shallowMergeAttributes(parseAttrs(this.#attrs), parseAttrs(attrs)); if (this.#redact && attrs) { this.#redact(attrs); } msg = msg && rest.length ? format(msg, rest, { stringify: json.fastSafeStringify }) : msg; this.write(level, attrs, msg); if (json.getEnvBool("LOG_TRACE_LOGS")) { console.trace("\u{1F446}"); } } trace(...args) { this.log( "trace", ...args ); } debug(...args) { this.log( "debug", ...args ); } info(...args) { this.log( "info", ...args ); } warn(...args) { this.log( "warn", ...args ); } error(...args) { this.log( "error", ...args ); } } class LegacyLogger { #logger; constructor(logger) { this.#logger = logger; } static from(logger) { return new LegacyLogger(logger); } #log(level, ...[maybeMsgOrArg, ...restArgs]) { if (typeof maybeMsgOrArg === "string") { if (restArgs.length) { this.#logger.log(level, restArgs, maybeMsgOrArg); } else { this.#logger.log(level, maybeMsgOrArg); } } else { if (restArgs.length) { this.#logger.log(level, [maybeMsgOrArg, ...restArgs]); } else { this.#logger.log(level, maybeMsgOrArg); } } } log(...args) { this.#log("info", ...args); } warn(...args) { this.#log("warn", ...args); } info(...args) { this.#log("info", ...args); } error(...args) { this.#log("error", ...args); } debug(...lazyArgs) { if (!shouldLog(this.#logger.level, "debug")) { return; } this.#log("debug", ...handleLazyMessage(lazyArgs)); } child(name) { name = stringifyName(name) + // append space if object is strigified to space out the prefix (typeof name === "object" ? " " : ""); if (this.#logger.prefix === name) { return this; } return LegacyLogger.from(this.#logger.child(name)); } addPrefix(prefix) { prefix = stringifyName(prefix); if (this.#logger.prefix?.includes(prefix)) { return this; } return LegacyLogger.from(this.#logger.child(prefix)); } } function stringifyName(name) { if (typeof name === "string" || typeof name === "number") { return `${name}`; } const names = []; for (const [key, value] of Object.entries(name)) { names.push(`${key}=${value}`); } return `${names.join(", ")}`; } function handleLazyMessage(lazyArgs) { return lazyArgs.flat(Infinity).flatMap((arg) => { if (typeof arg === "function") { return arg(); } return arg; }); } exports.JSONLogWriter = json.JSONLogWriter; exports.jsonStringify = json.jsonStringify; exports.ConsoleLogWriter = ConsoleLogWriter; exports.LegacyLogger = LegacyLogger; exports.Logger = Logger; exports.MemoryLogWriter = MemoryLogWriter;