54 lines
2.7 KiB
TypeScript
54 lines
2.7 KiB
TypeScript
/**
|
|
* @module utils/callerLocation
|
|
* @description Stack-frame extraction utility used by error/warning messages
|
|
* across Brainy. The first non-Brainy frame on the stack is the consumer's
|
|
* actual call site — the one that's useful to point at in diagnostics.
|
|
*
|
|
* Used by:
|
|
* - Subtype enforcement errors in `brainy.ts` (7.30.1+) — guides consumers
|
|
* from `requireSubtype()` rejections back to the offending call site.
|
|
* - Query-limit enforcement in `paramValidation.ts` (7.30.2+) — pairs with
|
|
* the recalibrated limit cap so consumers see which `find({ limit })`
|
|
* call triggered the warn-or-throw.
|
|
*
|
|
* No runtime dependencies. Pure stack walking + string matching.
|
|
*/
|
|
|
|
/**
|
|
* Extract the first non-Brainy frame from the current stack so error and
|
|
* warning messages can point at the consumer's call site instead of Brainy
|
|
* internals.
|
|
*
|
|
* The function walks `new Error().stack`, skips any frame inside Brainy's own
|
|
* source or compiled output, and returns the first remaining frame stripped of
|
|
* the leading `at ` token so the caller can compose the rendered line however
|
|
* it likes.
|
|
*
|
|
* @param extraSkipPatterns Optional list of substrings — frames matching ANY
|
|
* of these are also skipped. Use for in-module helpers that show up between
|
|
* the public API and the consumer (e.g. the formatter that builds the
|
|
* error message itself).
|
|
* @returns The caller's location string (e.g. `"OrderService.create (/app/src/orders/service.ts:42:23)"`)
|
|
* or `null` when the stack isn't available or only contains Brainy frames.
|
|
*/
|
|
export function findCallerLocation(extraSkipPatterns: string[] = []): string | null {
|
|
const stack = new Error().stack
|
|
if (!stack) return null
|
|
const lines = stack.split('\n').slice(1) // drop the `Error` line
|
|
for (const raw of lines) {
|
|
const line = raw.trim()
|
|
// Always skip Brainy's own source + compiled output. Consumers need their
|
|
// own call site, not a line inside `brainy.ts` or `dist/brainy.js`.
|
|
if (line.includes('/src/brainy.ts') || line.includes('/dist/brainy.js')) continue
|
|
// Skip the validation + diagnostic helpers regardless of which file they
|
|
// live in — they're plumbing between the public API and the consumer.
|
|
if (line.includes('findCallerLocation')) continue
|
|
if (line.includes('paramValidation') && line.includes('validate')) continue
|
|
if (line.includes('enforceSubtypeOn') || line.includes('formatSubtypeError')) continue
|
|
// Caller-supplied patterns (e.g. specific formatter names)
|
|
if (extraSkipPatterns.some(p => line.includes(p))) continue
|
|
// Strip leading `at ` if present so the caller can format consistently.
|
|
return line.replace(/^at /, '')
|
|
}
|
|
return null
|
|
}
|