Files
SnowLuma/.pnpm-store/v11/files/78/3be2796f67d2b8c0639438ebca726fadf2c8063963b5ebeaa4df3263438dbf4834b2bd1495ef56555d4e18ba5bfb67f0a7f04f7e78e355443eef62b2ea433d
T
TanTan 2a69b23b47
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:27:07 +08:00

91 lines
3.0 KiB
Plaintext

// Long-stack-trace support, after tlrobinson/long-stack-traces.
//
// extendTrace wraps a callback-mode method so that errors delivered to
// its callback carry the stack of the call site (filtered of this
// file's own frames). verbose() applies it to every method core.
import { fileURLToPath } from 'node:url';
import util from 'node:util';
const __filename = fileURLToPath(import.meta.url);
/**
* A callback-mode method that this module can wrap.
*
* @typedef {(this: unknown, ...args: unknown[]) => unknown} Traceable
*/
/**
* Wraps `object[property]` so that an error delivered to its callback
* carries the call site's stack, augmented with the wrapped call.
*
* The callback is found at `pos` (negative counts from the end, the
* default -1 meaning the last argument). Only callback-mode calls are
* affected: without a function in that position the wrapper is a
* pass-through, which is why verbose() traces the callback cores and
* then rebuilds the dual-mode wrappers around them.
*
* @param {Record<string, Traceable>} object the holder of the method.
* @param {string} property the method name on `object`.
* @param {number} [pos=-1] position of the callback argument.
* @returns {void}
*/
function extendTrace(object, property, pos = -1) {
const old = object[property];
/**
* @param {...unknown} args
* @this unknown
* @returns {unknown}
*/
object[property] = function (...args) {
/** @type {{ stack?: string }} */
const traceObj = {};
Error.captureStackTrace(traceObj, object[property]);
const cbPos = pos < 0 ? args.length + pos : pos;
const cb = args[cbPos];
if (typeof cb === 'function') {
/**
* @param {{ stack?: string, __augmented?: boolean }} err
* @param {...unknown} cbArgs
*/
args[cbPos] = function replacement(err, ...cbArgs) {
if (err?.stack && !err.__augmented) {
const name =
'Database#' +
property +
'(' +
args
.map((el) => util.inspect(el, false, 0))
.join(', ') +
')';
err.stack =
filter(err.stack).join('\n') +
'\n--> in ' +
name +
'\n' +
filter(traceObj.stack).slice(1).join('\n');
err.__augmented = true;
}
return cb.call(this, err, ...cbArgs);
};
}
return old.apply(this, args);
};
}
/**
* Drops this file's own frames from a stack string.
*
* @param {string | undefined} stackStr the stack to filter.
* @returns {string[]} the surviving frames.
*/
function filter(stackStr) {
if (!stackStr) return [];
return stackStr.split('\n').filter((line) => !line.includes(__filename));
}
export { extendTrace, filter };