import { createHash } from 'node:crypto';
import { channel, tracingChannel } from 'node:diagnostics_channel';
import { type EventLoopUtilization, performance } from 'node:perf_hooks';
function isObject(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null;
}
function hasBooleanOk(value: unknown): value is { ok: boolean } {
return isObject(value) && typeof value['ok'] === 'boolean';
}
function resolveDiagnosticsOk(result: unknown): boolean | undefined {
if (!isObject(result)) return undefined;
if (result['isError'] === true) return false;
if (hasBooleanOk(result)) return result.ok;
const structured = result['structuredContent'];
if (hasBooleanOk(structured)) return structured.ok;
return undefined;
}
function resolvePrimitiveDiagnosticsMessage(
error: unknown
): string | undefined {
if (typeof error === 'string') return error;
if (typeof error === 'number' || typeof error === 'boolean')
return String(error);
if (typeof error === 'bigint') return error.toString();
if (typeof error === 'symbol') return error.description ?? 'symbol';
return undefined;
}
function resolveObjectDiagnosticsMessage(error: unknown): string | undefined {
if (error instanceof Error) return error.message;
if (isObject(error) && typeof error['message'] === 'string') {
return error['message'];
}
try {
return JSON.stringify(error);
} catch {
return undefined;
}
}
function resolveDiagnosticsErrorMessage(error?: unknown): string | undefined {
if (error === undefined || error === null) return undefined;
return (
resolvePrimitiveDiagnosticsMessage(error) ??
resolveObjectDiagnosticsMessage(error)
);
}
function resolveResultErrorMessage(result: unknown): string | undefined {
if (!isObject(result)) return undefined;
const { structuredContent } = result;
if (!isObject(structuredContent)) return undefined;
const { error } = structuredContent;
if (!isObject(error)) return undefined;
const { message } = error;
return typeof message === 'string' ? message : undefined;
}
function logToolError(
tool: string,
durationMs: number,
message?: string
): void {
const rounded = durationMs.toFixed(1);
const suffix = message ? `: ${message}` : '';
console.error(`[ToolError] ${tool} failed in ${rounded}ms${suffix}`);
}
function captureEventLoopUtilization(): EventLoopUtilization {
return performance.eventLoopUtilization();
}
function diffEventLoopUtilization(
start: EventLoopUtilization
): EventLoopUtilization {
return performance.eventLoopUtilization(start);
}
function elapsedMs(startNs: bigint): number {
return Number(process.hrtime.bigint() - startNs) / 1_000_000;
}
type DiagnosticsDetail = 0 | 1 | 2;
interface ToolDiagnosticsEvent {
phase: 'start' | 'end';
tool: string;
durationMs?: number;
ok?: boolean;
error?: string;
path?: string;
}
interface PerfDiagnosticsEvent {
phase: 'end';
tool: string;
durationMs: number;
elu: {
idle: number;
active: number;
utilization: number;
};
}
export interface OpsTraceContext {
op: string;
engine?: string;
tool?: string;
path?: string;
[key: string]: unknown;
}
const TOOL_CHANNEL = channel('fs-context:tool');
const PERF_CHANNEL = channel('fs-context:perf');
const OPS_TRACE = tracingChannel<unknown, OpsTraceContext>('fs-context:ops');
function parseEnvBoolean(name: string): boolean {
const normalized = process.env[name]?.trim().toLowerCase();
return normalized === '1' || normalized === 'true' || normalized === 'yes';
}
function parseDiagnosticsEnabled(): boolean {
return parseEnvBoolean('FS_CONTEXT_DIAGNOSTICS');
}
function parseDiagnosticsDetail(): DiagnosticsDetail {
const normalized = process.env['FS_CONTEXT_DIAGNOSTICS_DETAIL']?.trim();
if (normalized === '2') return 2;
if (normalized === '1') return 1;
return 0;
}
function parseToolErrorLogging(): boolean {
return parseEnvBoolean('FS_CONTEXT_TOOL_LOG_ERRORS');
}
interface DiagnosticsConfig {
enabled: boolean;
detail: DiagnosticsDetail;
logToolErrors: boolean;
}
function readDiagnosticsConfig(): DiagnosticsConfig {
return {
enabled: parseDiagnosticsEnabled(),
detail: parseDiagnosticsDetail(),
logToolErrors: parseToolErrorLogging(),
};
}
function hashPath(value: string): string {
return createHash('sha256').update(value).digest('hex').slice(0, 16);
}
function normalizePathForDiagnostics(
pathValue: string,
detail: DiagnosticsDetail
): string | undefined {
if (detail === 0) return undefined;
if (detail === 2) return pathValue;
return hashPath(pathValue);
}
function normalizeOpsTraceContext(context: OpsTraceContext): OpsTraceContext {
if (!context.path) return context;
const detail = parseDiagnosticsDetail();
const normalizedPath = normalizePathForDiagnostics(context.path, detail);
if (!normalizedPath) {
const sanitized: OpsTraceContext = { ...context };
delete sanitized.path;
return sanitized;
}
return { ...context, path: normalizedPath };
}
function publishStartEvent(
tool: string,
options: { path?: string } | undefined,
detail: DiagnosticsDetail
): void {
const event: ToolDiagnosticsEvent = { phase: 'start', tool };
const normalizedPath = options?.path
? normalizePathForDiagnostics(options.path, detail)
: undefined;
if (normalizedPath !== undefined) {
event.path = normalizedPath;
}
TOOL_CHANNEL.publish(event);
}
function publishEndEvent(
tool: string,
ok: boolean,
durationMs: number,
error?: unknown
): void {
const event: ToolDiagnosticsEvent = {
phase: 'end',
tool,
ok,
durationMs,
};
const message = resolveDiagnosticsErrorMessage(error);
if (message !== undefined) {
event.error = message;
}
TOOL_CHANNEL.publish(event);
}
function publishPerfEndEvent(
tool: string,
durationMs: number,
elu: ReturnType<typeof diffEventLoopUtilization>
): void {
PERF_CHANNEL.publish({
phase: 'end',
tool,
durationMs,
elu: {
idle: elu.idle,
active: elu.active,
utilization: elu.utilization,
},
} satisfies PerfDiagnosticsEvent);
}
function startToolDiagnostics(
tool: string,
options: { path?: string } | undefined,
config: DiagnosticsConfig,
shouldPublishTool: boolean,
shouldPublishPerf: boolean
): {
startNs: bigint;
eluStart: ReturnType<typeof captureEventLoopUtilization> | undefined;
} {
const startNs = process.hrtime.bigint();
const eluStart = shouldPublishPerf
? captureEventLoopUtilization()
: undefined;
if (shouldPublishTool) {
publishStartEvent(tool, options, config.detail);
}
return { startNs, eluStart };
}
function finalizeToolDiagnostics(
tool: string,
startNs: bigint,
options: {
ok: boolean;
error?: unknown;
shouldPublishTool: boolean;
shouldPublishPerf: boolean;
eluStart: ReturnType<typeof captureEventLoopUtilization> | undefined;
}
): void {
const durationMs = elapsedMs(startNs);
if (options.shouldPublishPerf && options.eluStart) {
publishPerfEndEvent(
tool,
durationMs,
diffEventLoopUtilization(options.eluStart)
);
}
if (options.shouldPublishTool) {
publishEndEvent(tool, options.ok, durationMs, options.error);
}
}
async function runWithDiagnostics<T>(
tool: string,
run: () => Promise<T>,
options: { path?: string } | undefined,
config: DiagnosticsConfig,
shouldPublishTool: boolean,
shouldPublishPerf: boolean
): Promise<T> {
const { startNs, eluStart } = startToolDiagnostics(
tool,
options,
config,
shouldPublishTool,
shouldPublishPerf
);
const finalizeOptions = { shouldPublishTool, shouldPublishPerf, eluStart };
try {
const result = await run();
finalizeToolDiagnostics(tool, startNs, {
ok: resolveDiagnosticsOk(result) ?? true,
...finalizeOptions,
});
return result;
} catch (error: unknown) {
finalizeToolDiagnostics(tool, startNs, {
ok: false,
error,
...finalizeOptions,
});
throw error;
}
}
async function runWithErrorLogging<T>(
tool: string,
run: () => Promise<T>
): Promise<T> {
const startNs = process.hrtime.bigint();
try {
const result = await run();
const ok = resolveDiagnosticsOk(result);
if (ok === false) {
logToolError(tool, elapsedMs(startNs), resolveResultErrorMessage(result));
}
return result;
} catch (error: unknown) {
logToolError(
tool,
elapsedMs(startNs),
resolveDiagnosticsErrorMessage(error)
);
throw error;
}
}
export function shouldPublishOpsTrace(): boolean {
return parseDiagnosticsEnabled() && OPS_TRACE.hasSubscribers;
}
export function publishOpsTraceStart(context: OpsTraceContext): void {
OPS_TRACE.start.publish(normalizeOpsTraceContext(context));
}
export function publishOpsTraceEnd(context: OpsTraceContext): void {
OPS_TRACE.end.publish(normalizeOpsTraceContext(context));
}
export function publishOpsTraceError(
context: OpsTraceContext,
error: unknown
): void {
OPS_TRACE.error.publish({
...normalizeOpsTraceContext(context),
error,
});
}
export async function withToolDiagnostics<T>(
tool: string,
run: () => Promise<T>,
options?: { path?: string }
): Promise<T> {
const config = readDiagnosticsConfig();
if (!config.enabled) {
if (!config.logToolErrors) return await run();
return await runWithErrorLogging(tool, run);
}
const shouldPublishTool = TOOL_CHANNEL.hasSubscribers;
const shouldPublishPerf = PERF_CHANNEL.hasSubscribers;
if (!shouldPublishTool && !shouldPublishPerf) {
return await run();
}
return await runWithDiagnostics(
tool,
run,
options,
config,
shouldPublishTool,
shouldPublishPerf
);
}