import type { IntegrationProgressEvent, IntegrationProgressEventType, IntegrationRunManifest } from './types.js'; export interface EmitterOptions { /** Root directory containing per-run subdirs. Defaults to ./logs/integration-runs. */ rootDir?: string; /** Optional human-facing console mirror (default false — file is the primary record). */ consoleMirror?: boolean; /** * Max number of per-run subdirs to retain under rootDir. On each new run, older run dirs beyond * this count (by mtime) are pruned so the logs directory never grows unbounded across runs. * Defaults to MJ_INTEGRATION_MAX_RUN_DIRS env or 200. Set 0 to disable pruning. */ maxRunDirs?: number; } /** * Side-channel hook invoked for EVERY emitted event. The server layer registers this once at * startup to fan events onto a live GraphQL subscription topic (plan.md §11) WITHOUT this package * depending on the server (inversion of control). The durable JSONL artifact + the pollable * tail query remain the source of truth; this is the additive push path. Best-effort — a throwing * hook never breaks the sync or the durable write. */ export type IntegrationProgressPublishHook = (manifest: IntegrationRunManifest, event: IntegrationProgressEvent) => void; /** * Writes structured progress artifacts to `//`: * - manifest.json (written once at start) * - progress.jsonl (append-only event stream; checkpoint events carry resumableState) * - result.json (written once at terminate) * * Sequence numbers monotonic per emitter instance. Disk I/O is async and serialized * via an internal promise chain — callers don't await; the emitter guarantees ordered * writes. A flush() method awaits the queue when needed (test teardown, run completion). */ export declare class IntegrationProgressEmitter { private readonly manifest; private readonly runDir; private readonly progressPath; private readonly manifestPath; private readonly resultPath; private readonly consoleMirror; private writeChain; private seq; private terminated; private aggregateCounts; private errors; private warnings; private latestCheckpointSeq?; private readonly startMs; private static _publishHook?; /** * Register (or clear, with no arg) the global publish hook. The server registers this at startup * so every emitted event also fans out to the live GraphQL subscription, in addition to the * durable JSONL artifact + the pollable tail query. */ static SetPublishHook(hook?: IntegrationProgressPublishHook): void; private readonly root; private readonly maxRunDirs; constructor(manifest: IntegrationRunManifest, opts?: EmitterOptions); /** Emit a generic event. */ emit(eventType: IntegrationProgressEventType, partial?: Partial>): void; /** Convenience helpers — sugared `emit()` for the common cases. */ runStart(message?: string): void; stageStart(stage: string, message?: string): void; stageComplete(stage: string, counts?: IntegrationProgressEvent['counts']): void; stageError(stage: string, message: string, data?: Record): void; /** * Emit a non-fatal warning. Carries a structured {stage, code, message, data} * payload that the reader aggregates into the run result's `warnings[]` rollup. * Unlike `stageError`, a warning never fails the run. */ warning(stage: string, code: string, message: string, data?: Record): void; heartbeat(stage: string, message: string, counts?: IntegrationProgressEvent['counts']): void; /** * Write a resumable checkpoint. resumableState should carry enough subsystem- * specific data for the originating service to resume from this point. */ checkpoint(stage: string, resumableState: Record): void; externalCallStart(url: string, method: string, data?: Record): void; externalCallComplete(url: string, method: string, status: number, durationMs: number): void; objectAdded(objectName: string, source: 'Declared' | 'Discovered' | 'Custom'): void; fieldAdded(objectName: string, fieldName: string, source: 'Declared' | 'Discovered' | 'Custom'): void; pkClassifierInvoked(objectName: string): void; pkClassifierResult(objectName: string, verdict: Record): void; entityGenerated(objectName: string, mjEntityName: string): void; entitySkippedNoPK(objectName: string): void; /** Terminate the run as success. */ complete(message?: string): Promise; /** Terminate the run as failure. */ fail(message: string, code?: string): Promise; /** * Terminate the run as CANCELLED (a user/system abort stopped it mid-flight, not a failure and not a * clean completion). Emits exitReason='aborted' so a cancelled run is distinguishable from one that * finished — the persisted CompanyIntegrationRun has no 'Cancelled' status, so the artifact's * ExitReason is the GQL-visible signal that the run was stopped early (partial state is still durable). */ cancel(message?: string): Promise; /** Await all pending writes. */ flush(): Promise; private bootstrap; /** * Deletes the oldest per-run subdirs under the root, keeping the most recent `maxRunDirs` (by * mtime). Disabled when maxRunDirs <= 0. Never removes the current run dir (it's the newest). */ private pruneOldRuns; private writeTerminal; /** Reconstruct a {@link SyncWarning} from an emitted `'warning'` event. */ private warningFromEvent; private mirrorToConsole; static newRunID(prefix?: string): string; } //# sourceMappingURL=IntegrationProgressEmitter.d.ts.map