/** * Profiling support — optional and severable (see observability-hardening plan). * * Gate: `OPENSIP_PROFILING=1|true` explicitly enables local CPU artifacts. * OpenTelemetry export remains independently gated by its OTLP endpoint, so a * local profile never requires a collector and an endpoint alone never creates * an expensive profile artifact. * * Implementation uses Node's built-in `inspector` module for real CPU profiles * (no extra published dependency for the optional profiling path). This gives * actionable .cpuprofile files for short-lived CLI runs, with runId/command * labels embedded in the filename and a sidecar JSON. * * When a full OTel profiles signal / Pyroscope client is desired, the start/stop * hooks here are the single place to swap in that implementation while reusing * the same resource attributes, runId, and shutdown discipline. * * The seam is intentionally thin in core; all heavy lifting and any future * SDK bits stay in the CLI root. */ import { type RunScope } from '@opensip-cli/core'; import { type ProfileArtifactMetadata } from './profile-artifact-index.js'; /** * The slice of `node:inspector`'s {@link Session} this module actually drives: * `connect`/`disconnect` plus the two `post` overloads we issue * (`Profiler.enable`/`Profiler.start` and `Profiler.stop`). Narrowing to this * interface lets {@link __setInspectorSessionFactoryForTests} inject a fake * whose `post` invokes the callback SYNCHRONOUSLY — so the inner * `Profiler.start`/`Profiler.stop` callback arms (label-sidecar write, profile * write, and their error branches) are exercised deterministically, independent * of the real V8 profiler's async callback timing. That timing is non- * deterministic under the coverage lane (the `@vitest/coverage-v8` provider * holds its own inspector session), which previously made this file's branch * coverage flaky. Production keeps the real `new Session()` factory verbatim. */ export interface InspectorSession { connect(): void; disconnect(): void; post(method: 'Profiler.enable' | 'Profiler.start', callback?: (err: Error | null, params?: unknown) => void): void; post(method: 'Profiler.stop', callback?: (err: Error | null, params: { profile?: unknown; }) => void): void; } type InspectorSessionFactory = () => InspectorSession; export declare const PROFILING_INSPECTOR_TIMEOUT_MS = 5000; /** Returns true only when local CPU profiling was explicitly requested. */ export declare function isProfilingEnabled(): boolean; /** * Start CPU profiling for this invocation (if gate is open). * Must be called after RunScope is entered (so runId is available). * Safe to call multiple times (idempotent). */ export declare function startProfiling(scope?: RunScope, command?: string): Promise; /** * Stop profiling and flush the .cpuprofile + labels sidecar. * Safe and idempotent. */ export declare function stopProfiling(scope?: RunScope): Promise; /** Exposed for tests / shutdown. */ export declare function resetProfilingForTests(): void; /** * Test-only: swap the inspector-session factory so the start/stop callback * bodies can be driven deterministically (see {@link InspectorSession}). Pass * `undefined` to restore the real `new Session()` factory. Always restore in an * `afterEach` — {@link resetProfilingForTests} also restores it as a backstop. */ export declare function __setInspectorSessionFactoryForTests(factory: InspectorSessionFactory | undefined): void; export {}; //# sourceMappingURL=profiling.d.ts.map