@dudousxd/nestjs-telescope
Version:
Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.
100 lines • 4.4 kB
TypeScript
import { type RecordInput } from '../entry/entry.js';
import { type CpuProfilerResult } from './cpu-profiler.js';
import type { ResolvedProfilingConfig } from './profiling-config.js';
import type { CpuProfileContent } from './types.js';
/** The `type` of the entries this service records. */
export declare const CPU_PROFILE_ENTRY_TYPE: "cpu_profile";
/** Opaque handle for an in-flight capture, returned by {@link ProfilerService.begin}. */
export interface ProfileHandle {
readonly profiler: ProfilerLike;
readonly startedAt: number;
readonly reason: 'manual' | 'sampled';
/**
* The (fire-and-forget) start promise. {@link ProfilerService.end} awaits it
* before calling `stop()` so a capture that ends before `start()` settled (a
* very fast request, or a synchronous test) is still stopped correctly rather
* than seeing `isRunning === false` and silently dropping the profile.
*/
readonly started: Promise<void>;
}
/** Minimal profiler contract so the service can be tested with a fake. */
export interface ProfilerLike {
readonly isRunning: boolean;
start(): Promise<void>;
stop(): Promise<CpuProfilerResult | null>;
}
/** A pending manual capture budget: profile up to `count` more requests. */
interface ManualArm {
count: number;
/** When set, only requests whose label === this consume the budget. */
label?: string;
}
export interface ProfilerServiceDeps {
/** Records the finished profile as a `cpu_profile` entry (via the Recorder). */
record: (input: RecordInput<CpuProfileContent>) => void;
/** RNG for sampling — injectable for deterministic tests. */
random?: () => number;
/** Builds a profiler; injectable so tests avoid the real inspector. */
profilerFactory?: () => ProfilerLike;
}
export interface ProfilerStatus {
enabled: boolean;
sampleRate: number;
/** Currently in-flight captures. */
active: number;
maxConcurrent: number;
/** Remaining manual budget across all arms. */
pendingManual: number;
}
/**
* Decides whether to profile a request and drives the capture lifecycle around
* it. The integration calls {@link shouldProfile} (cheap gate), then
* {@link begin}/{@link end} around the request body.
*
* OVERHEAD: while disabled, {@link shouldProfile} returns false and {@link begin}
* returns null after a single boolean check — no profiler is ever constructed
* and `node:inspector` is never required. While enabled but a request isn't
* selected, the only added cost is the gate and (if sampling) one RNG call.
*/
export declare class ProfilerService {
private readonly config;
private readonly deps;
private active;
private readonly manual;
private readonly random;
private readonly profilerFactory;
constructor(config: ResolvedProfilingConfig, deps: ProfilerServiceDeps);
/**
* Arm a manual capture for the next `count` requests (optionally only those
* matching `label`). Returns the pending budget. No-op when disabled.
*/
arm(arm: ManualArm): {
pendingManual: number;
};
/**
* Whether this request should be profiled. Consults the manual arm budget
* first (targeted captures win), then sampling. Cheap and side-effect-free
* EXCEPT it does not yet consume the manual budget — that happens in
* {@link begin} so a request rejected by the concurrency cap doesn't waste it.
*/
shouldProfile(label: string | null): boolean;
/**
* Begin a capture for a request that {@link shouldProfile} accepted. Returns a
* handle to pass to {@link end}, or `null` if the request should not be
* profiled (disabled, concurrency cap reached, or not selected). The caller
* MUST treat a null handle as "no profiling" and proceed untouched.
*/
begin(label: string | null): ProfileHandle | null;
/**
* Stop a capture and record it as a `cpu_profile` entry. Safe to call with a
* null handle (no-op). Never throws into the host request path. Captures below
* the `minDurationMs` floor are discarded.
*/
end(handle: ProfileHandle | null, label: string | null): Promise<void>;
status(): ProfilerStatus;
private pendingManual;
/** First arm matching this label (labelled arms require an exact match). */
private matchingArm;
}
export {};
//# sourceMappingURL=profiler.service.d.ts.map