/** * Contract change rules and parity (change: add-service-contract-certification, OpenAPI phase). * * Pure functions over the normalized element model of `openapi.ts`: * * - {@link diffContracts}: a closed rule table, one verdict per changed element. A change the table * cannot prove compatible is `potentially-breaking`, never silently safe. * - {@link contractParity}: operations with no indexed route handler, and indexed handlers on a path * a specification covers but does not declare. Matching is literal: method plus normalized path * template. Nothing is matched by similarity. */ import type { ContractDocument, ContractOperation } from '../../analyzer/contract/openapi.js'; export type ContractChangeClass = 'breaking' | 'dangerous' | 'non-breaking' | 'potentially-breaking'; /** The closed rule table: each rule and its class. */ export declare const CONTRACT_RULES: { readonly 'operation-removed': "breaking"; readonly 'operation-added': "non-breaking"; readonly 'operation-deprecated': "dangerous"; readonly 'required-param-added': "breaking"; readonly 'optional-param-added': "non-breaking"; readonly 'param-removed': "non-breaking"; readonly 'param-made-required': "breaking"; readonly 'param-type-changed': "breaking"; readonly 'body-added-required': "breaking"; readonly 'body-made-required': "breaking"; readonly 'required-body-property-added': "breaking"; readonly 'optional-body-property-added': "non-breaking"; readonly 'body-property-made-required': "breaking"; readonly 'body-property-type-changed': "breaking"; readonly 'body-property-widened': "non-breaking"; readonly 'response-property-removed': "breaking"; readonly 'response-property-made-optional': "breaking"; readonly 'response-property-added': "non-breaking"; readonly 'response-property-type-changed': "breaking"; readonly 'response-property-widened': "dangerous"; readonly 'schema-unassessed': "potentially-breaking"; readonly 'rpc-removed': "breaking"; readonly 'rpc-added': "non-breaking"; readonly 'rpc-deprecated': "dangerous"; readonly 'rpc-type-changed': "breaking"; readonly 'rpc-streaming-changed': "breaking"; readonly 'type-removed': "breaking"; readonly 'type-added': "non-breaking"; readonly 'field-removed': "breaking"; readonly 'field-removed-reserved': "dangerous"; readonly 'field-added': "non-breaking"; readonly 'field-number-changed': "breaking"; readonly 'field-type-changed': "breaking"; readonly 'field-label-changed': "breaking"; readonly 'field-presence-changed': "dangerous"; readonly 'field-oneof-changed': "breaking"; readonly 'field-renamed': "dangerous"; readonly 'field-deprecated': "dangerous"; readonly 'reserved-number-reused': "breaking"; readonly 'reserved-removed': "dangerous"; readonly 'enum-value-removed': "breaking"; readonly 'enum-value-removed-reserved': "dangerous"; readonly 'enum-value-added': "non-breaking"; readonly 'enum-value-number-changed': "breaking"; readonly 'enum-value-renamed': "dangerous"; readonly 'arg-removed': "breaking"; readonly 'arg-default-changed': "dangerous"; readonly 'output-field-made-non-null': "non-breaking"; readonly 'input-made-optional': "non-breaking"; readonly 'required-input-field-added': "breaking"; readonly 'graphql-enum-value-added': "dangerous"; readonly 'union-member-added': "dangerous"; readonly 'union-member-removed': "breaking"; readonly 'interface-added': "dangerous"; readonly 'directive-added': "non-breaking"; readonly 'directive-removed': "breaking"; readonly 'directive-location-removed': "breaking"; readonly 'directive-repeatable-removed': "breaking"; readonly 'interface-removed': "breaking"; readonly 'enum-value-deprecated': "dangerous"; }; export type ContractRule = keyof typeof CONTRACT_RULES; export interface ContractChange { file: string; address: string; rule: ContractRule; class: ContractChangeClass; /** The element within the operation, when the rule is about one (`query:limit`, `email`). */ element?: string; detail: string; line: number; } /** * Compare the operations of two revisions of the specifications. Operations are matched by method * and path template with parameter names erased (renaming `{id}` is not a wire change), across all * files (an operation moved between files is not removed). An operation declared in more than one * file is compared as a set: any difference is `schema-unassessed`. */ export declare function diffContracts(base: readonly ContractDocument[], head: readonly ContractDocument[]): ContractChange[]; /** A server route as the route inventory records it. */ export interface IndexedRoute { method: string; path: string; file: string; handler: string; } export interface ParityResult { declaredUnimplemented: Array<{ file: string; address: string; line: number; }>; implementedUndeclared: Array<{ method: string; path: string; file: string; handler: string; }>; matched: number; /** Routes with a dynamic path segment (`*`): a boundary, never a finding. */ dynamic: number; } /** The indexed routes that serve an operation: same method, path with or without the base path. */ export declare function routesServing(op: Pick, routes: readonly IndexedRoute[]): IndexedRoute[]; /** * Two-way parity between the specifications and the indexed route handlers. A handler is * "covered" when its parent path equals a declared operation's path or that path's parent — so * with `/v1/orders/{id}` declared, `/v1/orders/export` and `/v1/orders/{id}/refund` are covered * while `/v1/ordersX` and `/internal/health` are not. A route is compared both with and without * the specification's base path (servers may mount the router under it). */ export declare function contractParity(docs: readonly ContractDocument[], routes: readonly IndexedRoute[]): ParityResult; /** The version bump a set of changes calls for; `null` when compatibility is not proven. */ export declare function contractBump(changes: readonly ContractChange[]): 'major' | 'minor' | 'patch' | null; //# sourceMappingURL=contract-diff.d.ts.map