/** * API breaking-change detection over route reflection. * * `snapshotRoutes` turns an app (or reflected descriptors) into a plain-JSON snapshot - the * `standard` validators are dropped, so a snapshot survives `JSON.stringify` and can be committed * as a CI baseline. `diffRouteSnapshots` compares two snapshots and classifies every change by * wire compatibility, direction-aware: * * request direction (body/query - what clients SEND): a new required field, a removed field, or * a narrowed type breaks existing callers; widening (new optional field, enum superset, * required→optional) is compatible. * response direction (response/sse/errors - what clients RECEIVE): a removed field, a field made * optional, or a widened type breaks existing readers; narrowing (new field, optional→required, * enum subset) is compatible. * * The classifier FAILS CLOSED: a schema change it cannot prove compatible is reported as breaking. * Schemas without JSON Schema metadata (validation-only Standard Schemas) cannot be compared and * yield `info` - never a silent pass presented as proof. */ import { type JsonSchema, type ReflectedSchemaField } from "./reflection.js"; /** One schema position in a snapshot: JSON Schema metadata only, no validator. */ export interface SchemaSnapshot { readonly jsonSchema?: JsonSchema; readonly fields?: readonly ReflectedSchemaField[]; } export interface RouteSnapshotSchema { readonly headers?: SchemaSnapshot; readonly body?: SchemaSnapshot; readonly query?: SchemaSnapshot; readonly response?: SchemaSnapshot; readonly sse?: SchemaSnapshot; readonly errors?: Readonly>; } /** One route in a snapshot - plain JSON, safe to persist as a CI baseline. */ export interface RouteSnapshot { readonly method: string; readonly path: string; readonly schema?: RouteSnapshotSchema; } export type DiffSeverity = "breaking" | "compatible" | "info"; export interface RouteChange { readonly severity: DiffSeverity; readonly method: string; readonly path: string; /** Which part of the contract changed; "route" for add/remove of the whole route. */ readonly section: "route" | "headers" | "body" | "query" | "response" | "sse" | "errors"; /** The top-level field (or error status) the change is about, when field-granular. */ readonly field?: string; readonly message: string; } export interface RoutesDiff { readonly changes: readonly RouteChange[]; /** True when any change is `breaking` - the CI-gate signal. */ readonly hasBreaking: boolean; } /** * Snapshot an app's routes (anything `reflectRoutes` accepts) as plain JSON. Validators are * dropped; only introspectable JSON Schema metadata is kept, so the result round-trips through * `JSON.stringify` unchanged. */ export declare function snapshotRoutes(source: unknown): readonly RouteSnapshot[]; /** * Diff two route snapshots (`snapshotRoutes` output, possibly restored from JSON). Every change is * classified breaking/compatible/info; `hasBreaking` is the CI-gate bit. */ export declare function diffRouteSnapshots(before: readonly RouteSnapshot[], after: readonly RouteSnapshot[]): RoutesDiff; //# sourceMappingURL=diff.d.ts.map