UNPKG

@dudousxd/nestjs-telescope

Version:

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

123 lines 6.58 kB
export declare const DEFAULT_REDACT_KEYS: readonly string[]; export interface RedactOptions { /** Extra keys to mask (merged with DEFAULT_REDACT_KEYS, case-insensitive). */ keys?: string[]; /** * Exact dot-paths to mask regardless of key name, e.g. `'body.ssn'`. * Unlike `keys` (which match that key at any depth), a path must match the * full traversal location from the root of the value passed to `redact()`. */ paths?: string[]; /** Replacement string. Defaults to '[REDACTED]'. */ mask?: string; /** * Maximum object/array nesting depth to clone. Beyond it, the subtree is * replaced with `'[Truncated: depth]'`. ON BY DEFAULT (8). This bounds a deep * recursive graph (e.g. an ORM entity referencing its EntityManager → identity * map → entities) regardless of how the host shaped it. */ maxDepth?: number; /** * Maximum cloned string length. Longer strings are clipped to the first N * chars plus a `'…[truncated]'` suffix. ON BY DEFAULT (8_192). Defeats a single * mega-string field (a serialized blob, a base64 payload) ballooning an entry. */ maxStringLength?: number; /** * Maximum number of array items to clone. Longer arrays keep the first N items * and append a final `'[Truncated: N of M items]'` marker element. ON BY * DEFAULT (200). Bounds high-cardinality collections (a full result set, a * relation collection) without losing the head of the list. */ maxArrayLength?: number; /** * Per-call walked-node budget: every object, array, and leaf visited counts * against it. Once exhausted, remaining subtrees become `'[Truncated: size]'`. * ON BY DEFAULT (5_000). This is the overall bytes-ish cap that bounds a * mega-graph regardless of its SHAPE — wide, deep, or both — so a single fat * entry can never retain an unbounded clone. */ maxNodes?: number; /** * Approximate serialized-byte budget per cloned content: every string charges * its length, every visited node a small fixed overhead. Once exhausted, * remaining subtrees become `'[Truncated: size]'`. ON BY DEFAULT (16_384). * This is the DETERMINISTIC cap on `bytes_per_entry` — the third factor of the * OOM working-set formula (`prune.after × ingest rate × bytes_per_entry`) — * and the bound that bites on incident-class payloads made of MANY SMALL * strings (an ORM user graph) that slip under the node/string/array limits. */ maxContentBytes?: number; /** * Per-entry-type overrides of the numeric bounds above, keyed by the entry's * `type` (e.g. `'exception'`, `'client_exception'`). A listed type's bounds are * merged OVER the top-level bounds for that entry only; unlisted types use the * top-level bounds unchanged. The masking spec (`keys`/`paths`/`mask`) is never * per-type — it stays uniform (and is compiled once). * * Motivation: the content-byte budget is really an OOM guard on HIGH-VOLUME * entries (request/query/cache clone big live graphs). Rare, high-value entries * — exceptions and client exceptions, whose stacks/componentStacks are * legitimately many KB — can be given a bigger budget WITHOUT loosening the * guard on the noisy ones. */ perType?: Record<string, RedactBounds>; } /** * The numeric memory bounds of {@link RedactOptions} — the subset overridable * PER ENTRY TYPE via {@link RedactOptions.perType}. The masking spec * (`keys`/`paths`/`mask`) is deliberately excluded: masking is a security * invariant that stays uniform across every entry. */ export type RedactBounds = Pick<RedactOptions, 'maxDepth' | 'maxStringLength' | 'maxArrayLength' | 'maxNodes' | 'maxContentBytes'>; /** Result of a bounded redaction: the detached clone plus whether anything was clipped. */ export interface RedactBoundedResult { /** The detached, masked, bounded clone. */ value: unknown; /** True when any bound (depth/string/array/node) clipped some content. */ truncated: boolean; } /** * The masking decision derived from {@link RedactOptions}, compiled ONCE so the * per-entry hot path never rebuilds these Sets. `keySet` holds the lowercased * union of {@link DEFAULT_REDACT_KEYS} and `options.keys`; `paths` holds the * exact dot-paths. Build it via {@link compileRedactSpec} at boot (the Recorder * does this in its constructor) and feed it to {@link redactBoundedWith}. */ export interface CompiledRedactSpec { /** Lowercased union of default + configured keys, matched at any depth. */ keySet: ReadonlySet<string>; /** Exact dot-paths to mask regardless of key name. */ paths: ReadonlySet<string>; } /** * Precompiles the immutable key/path Sets from {@link RedactOptions}. Call once * (config is immutable after boot) and reuse the result across every entry — * this is the optimization that keeps the hottest redaction path allocation-free. */ export declare function compileRedactSpec(options: RedactOptions): CompiledRedactSpec; /** * Bounded, never-throwing, SYNCHRONOUS deep clone of `value` with sensitive * leaves masked. Same key/path masking semantics as {@link redact}, plus the * hard memory bounds in {@link RedactOptions} (all defaulted on). Returns the * clone AND whether truncation happened so the Recorder can surface a counter. * * Synchronicity is load-bearing: this is the detach that releases the host's * live object graph at `record()` time — never defer it (see spec §A.1). */ export declare function redactBounded(value: unknown, options: RedactOptions): RedactBoundedResult; /** * Bounded redaction using an ALREADY-COMPILED {@link CompiledRedactSpec}, so the * per-entry hot path never rebuilds the key/path Sets. Identical behaviour to * {@link redactBounded}; only the `keys`/`paths` of `options` are ignored in * favour of the prebuilt `spec` (the remaining bound options are still read). */ export declare function redactBoundedWith(value: unknown, options: RedactOptions, spec: CompiledRedactSpec): RedactBoundedResult; /** * Returns a deep clone of `value` with sensitive leaves replaced by the mask. * Never mutates the input. Memory-bounded by default (see {@link RedactOptions}); * delegates to {@link redactBounded} and discards the truncation flag, so every * existing caller keeps the original `(value) => clone` signature. */ export declare function redact(value: unknown, options: RedactOptions): unknown; //# sourceMappingURL=redact.d.ts.map