1let _monorepoPrefix: string 2const getMonorepoPrefix = () => _monorepoPrefix ||= import.meta.url.replace(/pt0\/sharedF\/throwErrorF\/sharedErrF\.mts$/, '') 4export type DebugH = Record<string, unknown> 6export class PtErr extends Error { 7 uniqDebugH: DebugH | undefined 8 dumpDebugH: DebugH | undefined 9 throwLocation: string | undefined 11 constructor(message: string, uniqDebugH?: DebugH, dumpDebugH?: DebugH) { 13 // non-enumerable to avoid duplicate printing by Node's error inspector 14 Object.defineProperty(this, 'uniqDebugH', { value: uniqDebugH, enumerable: false, writable: true }) 15 Object.defineProperty(this, 'dumpDebugH', { value: { ...dumpDebugH }, enumerable: false, writable: true }) 20const origPrepareStackTrace = Error.prepareStackTrace 21Error.prepareStackTrace = (err, callSites) => { 22 if (!(err instanceof PtErr)) { 23 return origPrepareStackTrace ? origPrepareStackTrace(err, callSites) : `${err.name}: ${err.message}\n` + callSites.map(s => ` at ${s}`).join('\n') 25 const monorepoPrefix = getMonorepoPrefix() 26 const isInternalFrame = (shortPath: string) => shortPath.includes('/throwErrorF/') 27 const callerSite = callSites.find(site => { 28 const fileName = site.getFileName() || '' 29 if (!fileName.startsWith(monorepoPrefix) || fileName.includes('[eval')) return false 30 return !isInternalFrame(fileName.replace(monorepoPrefix, '')) 33 const shortPath = (callerSite.getFileName() || '').replace(monorepoPrefix, '') 34 const fnName = callerSite.getFunctionName() || '<anonymous>' 35 Object.defineProperty(err, 'throwLocation', { value: `${shortPath}:${fnName}`, enumerable: false, writable: true, configurable: true }) 37 const monorepoFrames = callSites 39 const fileName = site.getFileName() || '' 40 if (!fileName.startsWith(monorepoPrefix) || fileName.includes('[eval')) return null 41 const shortPath = fileName.replace(monorepoPrefix, '') 42 const fnName = site.getFunctionName() || '<anonymous>' 43 return ` at ${fnName} (${shortPath}:${site.getLineNumber()})` 45 .filter(Boolean) as string[] 46 const maxFrames = (err.uniqDebugH?._maxFrames as number | undefined) ?? Infinity 47 const shownFrames = monorepoFrames.slice(0, maxFrames) 48 const parts = [`PtErr: ${err.message}`, ...shownFrames] 49 const { _maxFrames, ...debugWithoutMaxFrames } = err.uniqDebugH || {} 50 if (Object.keys(debugWithoutMaxFrames).length > 0) parts.push(` uniqDebugH: ${JSON.stringify(debugWithoutMaxFrames)}`) 51 return parts.join('\n') 54export const throPtErr = (...props: [string, DebugH?, DebugH?]): never => { 55 throw new PtErr(...props)