@graphql-hive/logger
Version:
1,285 lines (1,209 loc) • 36.8 kB
JavaScript
;
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;