/** * Projection: OpenAPI operations → the existing graph primitives (change: add-contract-node-projection). * * The single place where a declared interface becomes an ordinary graph node. Because it reuses * FunctionNode/CallEdge, orient/search_code/get_subgraph/analyze_impact and the SCIP + federation * exports see contract elements with no change of their own. * * Two edges are produced, both LITERAL — no name similarity, no embeddings: * `implements_endpoint` handler function → the operation it serves * `consumes_endpoint` the function enclosing a client call → the operation it calls * * An edge carries this substrate's authority, so every rule below refuses rather than guesses: a * method + path that names more than ONE declared operation binds nothing (two services in one * repository are not one service), and a call written to an absolute URL binds only when the host * is one the document itself declares. * * Everything the projection could not bind is disclosed, never dropped: operations no route * serves, routes no operation declares, handlers and callers that matched but could not be pinned * to one node, client calls whose method or path is not literal, ambiguous addresses, a * specification that failed to parse, inputs that were not assessed at all, and every cap. Each * list is bounded and carries an exact total, so a disclosure can never itself become the thing * that blows up an artifact. */ import type { CallEdge, FunctionNode } from '../call-graph-types.js'; import type { HttpCall, RouteDefinition } from '../http-route-parser.js'; import { type UnresolvedRegistration } from './project-protobuf.js'; import { type SchemaProvenance } from './types.js'; import { type UnresolvedResolver } from './project-graphql.js'; import type { ResolverMap } from './graphql-resolvers.js'; import type { GrpcRegistration, ImportTarget } from './grpc-registration.js'; /** Specification files read per build. */ export declare const MAX_CONTRACT_FILES = 50; /** Operation nodes projected per build. */ export declare const MAX_CONTRACT_OPERATIONS = 2000; /** Items listed per disclosure (every list carries its exact total beside it). */ export declare const MAX_DISCLOSED = 20; /** The edge kinds this projection produces — neither is a call, and neither ranks code. */ export declare const CONTRACT_EDGE_KINDS: readonly ["implements_endpoint", "consumes_endpoint", "references_schema"]; /** Whether an edge kind is one the contract projection produces. */ export declare function isContractEdgeKind(kind: string | undefined): boolean; /** A bounded list with its exact total, so a cap never reads as the whole truth. */ export interface Disclosed { items: T[]; total: number; } /** Distinct names carried in {@link UnboundHandlerNames}, per list. */ export declare const MAX_UNBOUND_HANDLER_NAMES = 2000; /** * What every REFUSED binding names, over the COMPLETE refusal list rather than the * {@link MAX_DISCLOSED} display slice (change: cap-unbound-contract-handler-confidence). * * `find_dead_code` reads this to keep a handler whose wiring was refused out of its * high-confidence dead verdicts. It cannot read `unresolvedRegistrations.items` for that: those * are bounded for a READER, and a reader's cap must never decide a correctness question — the * canonical gRPC repository discloses 43 refusals, so an items-only rule would have to give up * the whole high-confidence tier there. * * DEDUPLICATED NAMES ONLY: no file, line or reason, so this grows with the number of DISTINCT * names, not with the number of refusals, and its own cap is disclosed in `truncated` (never * silently applied). */ export interface UnboundHandlerNames { /** Function names a refusal named literally. */ symbols: string[]; /** * Implementation classes a refusal named. `dir` is set only where the projection's own scope * rule PROVES the class can live nowhere else (a Go registration naming an unqualified type can * only mean its own package), so a consumer narrows exactly as far as is sound and no further. */ classes: Array<{ name: string; dir?: string; }>; /** * Files holding a refusal that names NO function at all — the registration or resolver map * handed over a variable, so the handler names are not in it. The file that declares it is then * the most precise honest granularity. */ files: string[]; /** Distinct names dropped at {@link MAX_UNBOUND_HANDLER_NAMES}; `0` when nothing was dropped. */ truncated: number; } /** A refusal row, as much of one as the name buckets need; a real row carries more. */ export interface NamedRefusal { file?: string; reason?: string; line?: number; symbol?: string; symbols?: string[]; className?: string; classDir?: string; } /** * Sort every refusal into the three buckets {@link UnboundHandlerNames} describes. The ONE place * the bucket rule lives, so the producer and `find_dead_code` cannot drift apart. * * A refusal that names no function AND is not a `not-literal` implementation argument buckets * NOTHING: an inline handler has no graph node to look dead, and an unknown field or service with * an all-inline map left no named symbol unrooted. Capping on those would print a reason that is * false about the repository. */ export declare function unboundHandlerNames(...lists: ReadonlyArray): UnboundHandlerNames; /** A specification file that declared itself one and could not be read. */ export interface UnreadSpecification { file: string; reason: string; } /** A route registration no declared operation matched. */ export interface UnmatchedRoute { file: string; method: string; path: string; } /** A route or call that matched an operation but could not be pinned to one code node. */ export interface UnresolvedBinding { file: string; line: number; method: string; path: string; operation: string; } /** A client call site the projection refused to bind, with why. */ export interface UnboundClientCall { file: string; line: number; url: string; reason: 'method-not-literal' | 'path-not-literal' | 'foreign-host' | 'ambiguous-operation' | 'no-operation'; } /** A method + path that names more than one declared operation, so nothing binds to it. */ export interface AmbiguousAddress { address: string; operations: string[]; } /** * `synthesizedBy` on an edge bound by `operationId` rather than by a literal path. * * Such an edge has no literal call site by construction, so a consumer that re-scans for one * must read its absence as expected, not as a stale index. */ export declare const GENERATED_CLIENT_BINDING = "operationId"; /** An `operationId` a generated-client module declared but that could not be bound. */ export interface GeneratedClientBinding { operation: string; operationId: string; spec: string; reason: 'duplicate-operation-id' | 'ambiguous-function'; /** For `ambiguous-function`: the modules holding the same-named functions. */ files?: string[]; } /** * What the projection could not bind, carried on the build result so a consumer can say * "unmatched" rather than imply absence. No node, no edge — disclosure only. */ /** One candidate schema's provenance, as disclosed by the projection. */ export interface SchemaProvenanceFact extends SchemaProvenance { /** Path relative to the analyzed root. */ file: string; } export interface ContractDisclosure { /** Operations no indexed route serves and no indexed client call consumes. */ unmatchedOperations: Disclosed; /** Route registrations no declared operation matched. */ unmatchedRoutes: Disclosed; /** Routes and calls that matched an operation but whose code node could not be resolved. */ unresolvedBindings: Disclosed; /** Specifications that declared themselves one and could not be read. */ unreadSpecifications: Disclosed; /** Client call sites left unbound. */ unboundClientCalls: Disclosed; /** Addresses that name more than one declared operation; nothing binds to them. */ ambiguousAddresses: Disclosed; /** `operationId`s a generated-client module declared that could not be bound. */ unresolvedGeneratedBindings: Disclosed; /** gRPC server registrations that name a service but could not be bound to its RPCs. */ unresolvedRegistrations: Disclosed; /** * What the COMPLETE refusal list names, for a consumer that must not rest a conclusion on the * reader-facing `items` slice. Absent when nothing was refused, and absent from an artifact * written before this field existed — a consumer must then fall back to the bounded items and * say so. */ unboundHandlerNames?: UnboundHandlerNames; /** * Files that looked like a specification but are not one — a YAML or JSON file with an * `openapi` or `swagger` key that is not at the top level — each with why. Listed so the * discovery scope is visible: a file the reader passed over is never silently absent. */ skippedCandidates: Disclosed; /** * Candidate schemas that look GENERATED or VENDORED — a codegen output under `__generated__/`, * a `# @generated`-headed SDL, a copy under `vendor/` — each with the rule that said so * (change: disclose-schema-provenance). * * An ANNOTATION, never a drop: the schema is projected and graded exactly as before. A * `path-pattern` verdict is a guess at the weaker certainty and is never reported as `declared`. */ schemaProvenance?: Disclosed; /** * RPCs no scanned registration binds. Registrations are read in Go, Python, and Node (grpc-js) * only, so an RPC served from another language is listed here too: this is "not bound", never * "not implemented". */ unboundRpcs: Disclosed; /** GraphQL resolver-map entries that name an operation but could not be bound to it. */ unresolvedResolvers: Disclosed; /** * GraphQL operations (root fields) no resolver map binds. Maps are read in JS/TS and Python * (Ariadne) only, and code-first servers have none, so this is "not bound", never "not served". */ unboundGraphqlOperations: Disclosed; /** * Set when a conclusion is WITHHELD because an input was not assessed — the route extraction * failed or degraded, a specification could not be read, or the operation cap stopped the * index. Then `unmatchedOperations` and `unmatchedRoutes` are empty because they cannot be * computed, NOT because nothing is unmatched. */ notAssessed?: string[]; /** * What was said after the fact and withholds nothing: references that drew no edge, elements * outside the model. Kept out of `notAssessed`, which consumers read as "a binding may be * missing" — an unresolved `google.protobuf.Timestamp` leaves every handler bound. */ notes?: string[]; /** Every cap the projection stopped at (both can fire in one build). */ truncations?: Array<{ reason: 'file-cap' | 'proto-file-cap' | 'graphql-file-cap' | 'operation-cap' | 'schema-cap'; cap: number; seen: number; }>; } export interface ProjectedContracts extends ContractDisclosure { nodes: FunctionNode[]; edges: CallEdge[]; } /** An empty projection, for a caller that must disclose a failure instead of a result. */ export declare function emptyContractProjection(): ProjectedContracts; /** The disclosure half of a projection, or undefined when it says nothing at all. */ export declare function contractDisclosure(p: ProjectedContracts): ContractDisclosure | undefined; export interface ContractProjectionInput { /** Candidate specification files, path + content. */ files: readonly { path: string; content: string; }[]; /** Candidates the caller did not read, so the cap it hit is disclosed rather than invisible. */ filesOmitted?: number; /** `.proto` files the caller did not retain: they withhold protobuf conclusions only. */ protoFilesOmitted?: number; /** GraphQL schema files the caller did not retain: they withhold GraphQL conclusions only. */ graphqlFilesOmitted?: number; /** Schema files the caller deliberately did not pass (fixtures, a Relay copy), each with why. */ skippedFiles?: ReadonlyArray<{ file: string; reason: string; }>; /** Server route registrations from the HTTP route parser. */ routes: readonly RouteDefinition[]; /** Client call sites from the HTTP projection. */ calls: readonly HttpCall[]; /** * Why the route/call extraction cannot be treated as complete (it threw, or a file hit a parse * budget). Present means no "nothing serves this operation" conclusion is drawn at all. */ extractionNotAssessed?: readonly string[]; /** The code node that implements a route, or undefined when it cannot be resolved uniquely. */ resolveHandler: (route: RouteDefinition) => FunctionNode | undefined; /** The code node enclosing a client call, or undefined. */ resolveCaller: (call: HttpCall) => FunctionNode | undefined; /** * The functions of each module whose header names this specification as its source, indexed by * function name (change: add-contract-graph-projection). * * A generated client builds its URL from a base variable — * `fetch(\`${BASE}/orders/${id}\`)` — so the HTTP projection sees no literal path and binds * nothing: the operation it calls reads as consumed by no one. Its method name, however, IS the * `operationId`, which is an exact identifier rather than a guess. * * Supplied by the caller because the projector holds specifications, not source: the header * scan needs file content the builder already has. */ generatedClientFunctions?: (specPath: string) => ReadonlyMap; /** gRPC server registrations in the indexed source, for binding protobuf RPCs to their implementation. */ grpcRegistrations?: readonly GrpcRegistration[]; /** Where an import names a function, one barrel re-export followed (gRPC Node, GraphQL). */ importTargets?: (fromFile: string, specifier: string, name: string, runtime: 'node' | 'python') => readonly ImportTarget[]; /** The resolver maps in the indexed source for the given root type names (GraphQL). */ graphqlResolverMaps?: (roots: readonly string[]) => readonly ResolverMap[]; /** The code functions of a name a resolver entry could mean. */ resolveGraphqlImplementation?: (map: ResolverMap, name: string) => readonly FunctionNode[]; /** The code functions a registration could mean by a name (see `ProtobufProjectionInput`). */ resolveGrpcImplementation?: (reg: GrpcRegistration, name: string) => readonly FunctionNode[]; } /** * Project the operations declared by `files` and bind them to the code that serves and calls them. * * Matching is by HTTP method plus the normalized path template: the operation's FULL path (the * servers base path included), or — because a router is often mounted under that base — the path * as declared under `paths`. Either way the match must name exactly ONE declared operation across * every specification; a base-less match additionally needs a literal segment. A match that is * not unique is no match, and the address is disclosed as ambiguous. */ export declare function projectContracts(input: ContractProjectionInput): ProjectedContracts; //# sourceMappingURL=project.d.ts.map