UNPKG

@dudousxd/nestjs-telescope

Version:

Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.

66 lines 3.88 kB
import type { ExceptionContent } from '../entry/content.js'; import { type RecordInput } from '../entry/entry.js'; import type { ExceptionsOptions } from './telescope.options.js'; /** * Where a throw happened, for the doors that know something the stack doesn't. * * The Nest interceptor passes nothing (the request entry in the same batch * already says which route it was). A queue or schedule watcher DOES have * context worth keeping — which queue, which job id, which cron task — because * off the request path there is no sibling `request` entry to read it from. */ export interface ExceptionCaptureDetails { /** Merged into the entry's `content.context` (queue + job name, task name, …). */ context?: Record<string, unknown>; /** Extra tags appended to the exception entry. */ tags?: string[]; } /** * Decides whether a thrown error is expected 4xx control flow that should NOT * become an exception entry. True only for a NestJS `HttpException` whose * `getStatus()` is a 4xx (>= 400 and < 500), and only while the * `captureHttp4xx` escape hatch is off (the default). * * WHY the default-skip: expected 4xx control flow is NOT an incident. A * `ForbiddenException` (403), `NotFoundException` (404) or a validation 400 is * the framework doing its job — permission denied, resource missing, bad input. * Recording those as exception entries means every permission denial in * production opens a NEW exception family (the family hash keys on * name+message+top-frame, so each call site is its own family), which fires the * `new-exception` Slack alert and, in AI auto-mode, spends model tokens on a * "diagnosis" of intended behaviour. We hit exactly this: Telescope's own * client-errors `authorize` gate threw a 403, it was captured as a brand-new * family, paged Slack, and burned an AI diagnosis. * * This applies to EVERY door, not just HTTP. An `HttpException` thrown inside a * job body is the same expected control flow (hosts routinely reuse * `NotFoundException` in services that both a controller and a worker call), so * a queue retry storm must not be able to page on-call through the back door * that the front door was hardened against. * * Detected via `instanceof HttpException` from `@nestjs/common` (a peer dep), * which also covers all the built-in subclasses (`ForbiddenException`, * `NotFoundException`, `BadRequestException`, the validation-pipe 400, …). */ export declare function isExpectedHttpControlFlow(error: unknown, options: ExceptionsOptions | undefined): boolean; /** * Build the `exception` entry for a thrown value. A non-`Error` throw (a string, * a rejected object) is normalised into an `Error` so `class`/`message` are * always populated and the family hash is always computable. */ export declare function toExceptionRecordInput(error: unknown, details?: ExceptionCaptureDetails): RecordInput<ExceptionContent>; /** * Apply the 4xx policy and, when the error survives it, hand the exception * entry to `record`. Returns whether an entry was recorded (the watchers ignore * it; it exists so tests and future doors can assert the decision). * * This function NEVER throws. Every caller is on the host's own failure path — * an rxjs `catchError`, a `catch` block that is about to re-throw a job's error * — and a throw from here would REPLACE the host's original error with a * Telescope error, turning an observability bug into a data-loss bug. Building * the entry (`String(error)` on a hostile `toString`, a getter on `err.stack`) * runs on the host's thread, so the guard has to be here and not only inside * the Recorder. */ export declare function captureException(record: (input: RecordInput) => void, error: unknown, options: ExceptionsOptions | undefined, details?: ExceptionCaptureDetails): boolean; //# sourceMappingURL=exception-capture.d.ts.map