import { z } from 'zod'; import { T as TypeShape, M as MessageBusSemantics, D as DeployableUnit, S as Semantics, B as BoundaryBindingSchema, C as ConfidenceInfoSchema, a as ConfidenceLevelSchema, b as ConfidenceSourceSchema, c as CorroborationSchema, d as SourceLocationSchema, e as MessageBusTechnology } from './schemas-CIns0fqF.cjs'; export { f as DeployableUnitSchema, E as EVERY_FIELD, F as FunctionCallSemantics, g as FunctionCallSemanticsSchema, G as GraphqlOperationSemantics, h as GraphqlOperationSemanticsSchema, i as GraphqlResolverSemantics, j as GraphqlResolverSemanticsSchema, k as MessageBusSemanticsSchema, R as RestSemantics, l as RestSemanticsSchema, m as RuntimeConfigSemantics, n as RuntimeConfigSemanticsSchema, o as SemanticsSchema, p as StorageSemantics, q as StorageSemanticsSchema, r as TypeShapeSchema, s as ecsContainerInstanceName, t as methodsAgree, u as normalizePath, v as pathShape, w as routePathAdmits, x as routePathsMeet, y as storageContainerLabel, z as storageLabel, A as storageSystemLabel, H as typeDefinitionKey, I as withDefinitionsInlined } from './schemas-CIns0fqF.cjs'; /** * The boundary-name syntax, parsed and printed in one place. * * A name in a summary means one of three things, told apart by the * braces alone: a literal (`orders-v1`), a pattern with deploy-time * holes (`{stage}-orders-v1`), or a reference saying where to go and * ask (`{location.bucket}`, `{ORDER_TABLE}`). A second parser can * disagree about which is which, and a second printer can spell a * value this one cannot read back, so everything that touches the * braces calls this module: `parseBoundaryName` and * `boundaryNameString` are the two directions, and the helpers below * are views over them. The package README tells the longer story, * including why REST route paths and message-bus channels stay apart. */ /** One piece of a pattern: text the writer stated, or a hole. */ type NamePart = { type: "text"; text: string; } | { type: "hole"; label: string; }; /** * What a name string means. A `reference` keeps its raw dot-separated * path, and `referenceOf` is the view that checks the parts are all * present, so a malformed spelling still classifies as a reference and * still pairs with nothing. */ type BoundaryName = { type: "literal"; value: string; } | { type: "pattern"; parts: NamePart[]; } | { type: "reference"; path: string[]; }; /** What a name string means. The exact inverse of `boundaryNameString`. */ declare function parseBoundaryName(name: string): BoundaryName; /** The one serializer. Everything a summary spells goes through here. */ declare function boundaryNameString(name: BoundaryName): string; /** * How a hole is spelled. A reader that assembles a pattern a part at a * time, the way the adapter's name reader does, mints each hole here * so the spelling cannot drift from the parse above. */ declare function patternHole(label: string): string; /** * The name a CloudFormation `Fn::Sub` value states. The array form * takes its template from the first element, and the variable map only * says where a substitution comes from, which does not change the name. */ declare function namePatternFromSub(value: unknown): string | null; /** * Whether a name says only that somebody else knows it. A wrapper that * takes its bucket as an argument states `{location.bucket}` and * nothing more, which says which parameter to ask rather than which * bucket. A name like that agrees with nothing until something grounds * it, since otherwise it would agree with every name there is. */ declare function namesNothing(name: string): boolean; /** Whether a name has anything a source left for deploy time to fill. */ declare function hasNameHole(name: string): boolean; /** * The form two patterns are compared in: every hole reduced to the same * token, since the two sides pick their own name for the parameter. */ declare function namePatternKey(name: string): string; /** How much of a name the writer stated rather than left for deploy time. */ declare function fixedTextLength(name: string): number; /** * Whether two names are the same name. Two patterns agree when their * fixed parts line up, since a hole on one side meets a hole on the * other. A pattern and a concrete name agree when the fixed parts are * where the pattern says they are, which is what happens when one side * hardcoded what the other parameterized. A reference agrees with * nothing on either side. */ declare function namesAgree(a: string, b: string): boolean; /** Where a reference says to go and ask. */ interface Reference { /** * The value the code starts from: a parameter of the unit the * reference was written in, or a variable the deployment sets. Which * of those it is depends on the unit's inputs, so the reader that * grounds a reference decides, not the string. */ root: string; /** The fields to read inside it, outermost first. */ fields: string[]; } /** * How a reference is written. Null when a part of it is empty, since a * reference has to say what to ask about. */ declare function referenceName(reference: Reference): string | null; /** * The place a parsed reference says to ask, or null for a name that is * not a reference, or for a reference with a part missing, which says * nothing anybody could answer. */ declare function referenceOf(name: BoundaryName): Reference | null; /** * The reference a name states, or null when the name states a name * rather than where to go and ask. */ declare function referenceFromName(name: string): Reference | null; /** * What the deployment running a piece of code fills its variables in * with. * * A queue URL, a function name and a table name only exist once a stack * is deployed, so the source reaches them through a variable and the * template says what that variable is. Every protocol whose name has a * hole in it asks the same two questions here, and whoever knows which * deployment runs the code fills them in. * * The interface is declared next to the protocols because a protocol * has to ask without knowing what a summary is. `@suss/behavioral-ir` * works the values out from a set of summaries. */ interface Deployment { /** * Which variable a reference asks about, or null when what settles it * is an argument a caller passes rather than anything the deployment * says. */ variableFor(reference: Reference): string | null; /** * The string this deployment sets that variable to. Null when it sets * nothing, and null when two deployments of this code set it to * different strings, since picking one of two would be a guess. */ setTo(reference: Reference): string | null; /** * The declared resource this deployment wires that variable to, by * the name the resource is declared under. A template that writes * `!Ref ArchiveWorker` says which unit an invoke reaches without ever * writing the deployed function's name, and that logical id is what * the unit's own summary is keyed by. */ pointsAt(reference: Reference): string | null; } /** A deployment that settles nothing, for a run with no template in it. */ declare const NOTHING_DEPLOYED: Deployment; /** * Comparing two TypeShapes structurally. * * Both checkers do this. The behavioural checker compares a provider * body against a declared contract, and the intent checker compares a * code body against declared intent, and the two have to reach the same * verdict for the same pair. The comparison is here, next to the * TypeShape it works on, so neither checker owns it and the two cannot * drift apart. * * The comparison is asymmetric, and the answer has three values rather * than two. `unknown` is what keeps a shape nobody can see into from * being reported as agreement. */ /** * A comparison result with three values: * - "match": `actual` satisfies `declared` * - "nomatch": there is a concrete, verifiable incompatibility * - "unknown": uncertainty that would otherwise mask a mismatch * (spreads, refs, unknown shapes) */ type MatchResult = "match" | "nomatch" | "unknown"; /** * Compare an actual body against a declared body and say whether the * actual satisfies the declared. * * The two sides are not interchangeable. `actual` is the value that gets * produced (a response body, a return value), and `declared` is the * contract it has to conform to. `unknown` is a soft signal, and the * caller decides whether to show it. */ declare function bodyShapesMatch(actual: TypeShape, declared: TypeShape): MatchResult; /** * The contract a boundary protocol implements. * * A protocol (REST, message-bus, one side of GraphQL) is a single * module in this directory. Its schema says what the identity fields * are, its behavior says how those fields key and agree, and the two * travel together so neither can be added without the other. Nothing * outside a protocol's own module decides how its boundaries behave. * * These definitions ship with ir-core rather than with the packs, * because a published summary has to mean the same thing to a reader * who never installed the pack that wrote it. If a pack needs a * protocol nobody has defined yet, add a module here. * * A protocol also says which of its words come from OpenTelemetry's * semantic conventions, in `semconv`. The README says why that * matters and where it stops. */ /** One identity field written as an OpenTelemetry attribute. */ interface SemconvAttribute { /** The attribute name, as the semantic conventions spell it. */ name: string; /** * Values suss writes where the source named none. A span never says * them, so the projection leaves the attribute off rather than * emitting a string that could only ever mismatch. */ placeholderValues?: readonly string[]; } /** * Which identity fields the semantic conventions have an attribute * for. A field is absent when they have no attribute for it, or when * our value is our own string rather than the one a span gets. */ type SemconvMapping = { readonly [K in Exclude]?: SemconvAttribute; }; /** * How one semantics variant keys its boundaries and pairs them up. Each * variant fills this in inside its own module, and nothing else in the * tree gets a say in how a boundary keys or agrees. */ interface BoundaryBehavior { /** Null when the source never gave the boundary a name. */ identityKey(semantics: S): string | null; /** * The bucket the pairing pass groups by. Defaults to `identityKey`. * Define it where one side can know more than its counterpart, and put * only the shared part in the bucket: a `"*"` REST route has to meet * consumers that each specify one method, so REST buckets by path * alone. Whatever the bucket leaves out, `sidesAgree` has to compare. */ pairingKey?(semantics: S): string | null; /** * Settles whatever `pairingKey` left out of the bucket. A variant that * does not define it always agrees. */ sidesAgree?(a: S, b: S): boolean; /** * Whether this boundary's bucket can meet buckets with other keys. A * REST path with a hole that spans some number of segments has a key * of its own and serves what several other keys serve, so the pairing * pass compares it against every bucket on the other side with * `bucketsMeet`. A variant that does not define it meets only its own * key. */ spansBuckets?(semantics: S): boolean; /** Whether two buckets describe at least one boundary in common. */ bucketsMeet?(a: S, b: S): boolean; /** * How narrowly this bucket states what it serves, compared * lexicographically. When a consumer meets more than one bucket, the * one ranking highest is the one it reaches, and an even contest is * reported rather than paired. */ bucketRank?(semantics: S): readonly number[]; /** The line a reader sees for this boundary. Defaults to `identityKey`. */ displayLabel?(semantics: S): string | null; /** * The semantics with any filesystem path it states rewritten, for * the pass that makes a summary's paths project-relative. A * protocol that states no path declares nothing here. */ rewritePaths?(semantics: S, rewrite: (path: string) => string): S; /** * The first protocol whose `claims` returns true normalizes a * hand-written suppression boundary, and a string nobody claims is * compared byte for byte. A protocol whose keys are exact declares * nothing here and stays verbatim. */ ruleBoundary?: { claims(raw: string): boolean; normalize(raw: string): string; }; /** * A protocol whose boundaries have no URL leaves this undefined, which * is different from an "unknown" result: undefined means the question * does not apply, and unknown means this declaration cannot settle it. */ servesRequest?(semantics: S, method: string, path: string): MatchResult; /** * Whether every request this boundary takes falls inside a pattern * something else was registered for. Middleware registered for * `/v1/*` runs for `/v1/tenants/{id}` and not for `/health`, so * composing it into a unit asks this first. * * A protocol whose boundaries no pattern addresses leaves this * undefined, and nothing registered with a pattern reaches them. */ withinScope?(semantics: S, scope: string): boolean; /** * The same boundary with a name the deployment fills in put in. * * A call written as `fetch(`${process.env.API_BASE}/orders`)` gets * part of its boundary from the source and part from whatever runs * the code. Both parts have to be in before anybody can see that the * two sides describe one boundary. * * `deployment` is already scoped to the unit this boundary belongs * to. A protocol hands over the reference its name states and gets * back what fills it in, or null. Return null to leave it as it is, * which is right whenever nothing needs filling in or nothing can * fill it. * * A protocol whose names are settled in the source leaves this * undefined. */ groundName?(semantics: S, deployment: Deployment): S | null; /** * Where this boundary's name says to go and ask, when it says that * rather than a name. A caller that has to explain why two sides did * not meet reads it to say which input would settle them. * * A protocol that defines `groundName` defines this too, and the * same reference is what `groundName` hands over. */ nameReference?(semantics: S): Reference | null; /** * Whether a provider produces a status and a body that a consumer * reads back. Every protocol has to state this, so that one added * later says what it is instead of landing in the HTTP-style checks * because nobody remembered to exclude it. */ exchangesHttpResponses: boolean; /** * Whether crossing this boundary leaves the process. A report about * what a change means for somebody outside the code covers the ones * that do; a call from one function in a project to another is a * boundary as well, and a reader has the source diff for that. */ leavesTheProcess: boolean; /** * Whether this protocol's own pass already reports a boundary that * paired with nothing, so the generic unmatched list can leave it out. */ reportsUnpairedItself: boolean; /** * Whether a boundary with these semantics can pair with anything. * Defaults to a non-null pairing key. A protocol whose dedicated * pass pairs keyless boundaries overrides it, the way GraphQL * operations pair by document rather than by key. */ canPair?(semantics: S): boolean; } /** * One protocol in full: the schema for its identity fields, and the * behavior that keys them. The registry composes these into the * `Semantics` union and the runtime lookup. */ interface BoundarySemanticsDefinition> { name: z.infer["name"]; schema: Z; behavior: BoundaryBehavior>; /** * Every protocol declares this, empty included, so that adding one * means deciding where its words come from. */ semconv: SemconvMapping>; } declare const IdentityKeyBrand: unique symbol; /** `gql:Type.field`, the boundary key both GraphQL sides pair on. */ type GqlIdentityKey = `gql:${string}.${string}` & { readonly [IdentityKeyBrand]: "gql"; }; declare function gqlIdentityKey(typeName: string, fieldName: string): GqlIdentityKey; /** `fn:package::export.path`, the key a package-export boundary pairs on. */ type FnIdentityKey = `fn:${string}::${string}` & { readonly [IdentityKeyBrand]: "fn"; }; declare function fnIdentityKey(packageName: string, exportPath: readonly string[]): FnIdentityKey; /** `bus:technology subject`, with the technology closed off the schema. */ type BusIdentityKey = `bus:${MessageBusSemantics["messageBus"]} ${string}` & { readonly [IdentityKeyBrand]: "bus"; }; declare function busIdentityKey(messageBus: MessageBusSemantics["messageBus"], subject: string): BusIdentityKey; /** * `unit:platform name`, the key an invoker and the unit it invokes pair * on. The platform stays in because two clouds can each deploy * something called `Worker` and they are not the same thing. */ type UnitIdentityKey = `unit:${DeployableUnit["deploymentTarget"]} ${string}` & { readonly [IdentityKeyBrand]: "unit"; }; declare function unitIdentityKey(deploymentTarget: DeployableUnit["deploymentTarget"], instanceName: string): UnitIdentityKey; /** `metric:system type`, the key both sides of a metric pair on. */ type MetricIdentityKey = `metric:${string} ${string}` & { readonly [IdentityKeyBrand]: "metric"; }; declare function metricIdentityKey(metricSystem: string, metricType: string): MetricIdentityKey; declare const MetricSemanticsSchema: z.ZodObject<{ name: z.ZodLiteral<"metric">; metricSystem: z.ZodString; metricType: z.ZodNullable; }, z.core.$strip>; type MetricSemantics = z.infer; /** * `instanceName` is nullable here and required on the unit itself, * because only the provider always knows the name. A call that works * its callee out at run time still happened, and recording it with no * name says so without pairing it against everything. */ declare const UnitInvocationSemanticsSchema: z.ZodObject<{ deploymentTarget: z.ZodEnum<{ lambda: "lambda"; "ecs-task": "ecs-task"; container: "container"; "k8s-deployment": "k8s-deployment"; worker: "worker"; }>; name: z.ZodLiteral<"unit-invocation">; instanceName: z.ZodNullable; }, z.core.$strip>; type UnitInvocationSemantics = z.infer; /** * A boundary binding read as OpenTelemetry attributes. * * A summary says what a unit can reach and a span says what it did * reach. Joining the two means both sides spelling the boundary the * same way, so suss writes the value a span gets wherever the semantic * conventions have a word for it, and this projection puts those * values under the attributes a trace store already indexes. * * Which fields those are is each protocol's own declaration, in its * module under `semantics/`. Nothing here knows about any protocol. */ /** * The attributes a boundary states, by name. A field the source never * gave, a placeholder, and a field the conventions have no attribute * for are all left out, so every pair here compares byte for byte * against the same attribute on a span. */ declare function semconvAttributes(binding: BoundaryBinding): Record; /** * What a pack says about itself, for everything that has to know a pack * exists without reading its patterns. * * `suss init` suggests a pack when it sees the library in a project's * manifest, and the packages page lists every pack with a line about * what it reads. Both come from here, so nobody maintains a table. */ /** The manifest a dependency is declared in. */ type Ecosystem = "npm" | "pypi" | "rubygems"; /** * A value a pack cannot work out from the code, which the project has * to write down before the pack reads anything: which directory the * classes are under, which file the routes are in. */ interface PackConfiguration { /** Where to write it, relative to the project. */ file: string; /** A starting point, with this project's own values to fill in. */ example: Record; /** Whether the pack refuses to run without it. */ required: boolean; /** What the value is, in a sentence. */ why: string; } interface PackDeclaration { /** * What the pack contributes. A `framework` pack discovers units, a * `client` pack binds a call to the route it reaches, and an * `effects` pack recognises calls inside units another pack found, so * on its own it comes back empty. */ kind: "framework" | "client" | "effects"; /** The npm package that ships the pack, which a project installs. */ package: string; /** * What a project has to depend on for this pack to read anything. * Several entries mean any one of them, the way react-router ships * under two names. */ dependencies: Array<{ ecosystem: Ecosystem; name: string; }>; /** * Set for a library the language itself ships, which no manifest * lists: Ruby's Net::HTTP, the browser's fetch, Node's own surface. * Every project in that language is a candidate. */ shippedWith?: "typescript" | "python" | "ruby"; /** The line the packages page shows, one sentence, what it reads. */ reads: string; /** Set when the pack needs a value from the project to run at all. */ configuration?: PackConfiguration; } /** * Keys for a boundary binding, and whether two of them agree. Every * function here asks the binding's protocol and returns whatever it * says, so the rules themselves are in the protocol's own module under * `semantics/` and not in this file. * * The sharing is the point. The behavioural checker pairs code * summaries by these keys, and the intent checker pairs intent against * code by the same keys. If the two ever keyed differently, intent and * code would stop lining up, so the keying is here next to the binding * rather than in either checker. */ /** * Whether a binding is of one protocol, narrowed to that protocol's * own semantics when it is. A caller reaching through the binding to * compare the name by hand gets `string | undefined` and no narrowing, * so a name that no protocol uses compiles and quietly never matches. */ declare function bindingIs(binding: BoundaryBinding | null | undefined, name: N): binding is BoundaryBinding & { semantics: Extract; }; declare function boundaryKey(binding: BoundaryBinding): string | null; /** * The binding with any filesystem path its semantics state rewritten. * The CLI uses it to make a summary's paths project-relative, and each * protocol says for itself whether it states one. */ declare function withRewrittenPaths(binding: BoundaryBinding, rewrite: (path: string) => string): BoundaryBinding; /** * The bucket a boundary pairs in, with any name the deployment fills in * put in first. * * A consumer that gets its base URL from the runtime states a different * string from the provider it reaches, and the two are one boundary. * Every protocol that has such a name says how to fill it in, and one * whose names are settled in the source keys exactly as before. */ declare function groundedPairingKey(binding: BoundaryBinding, deployment: Deployment): string | null; /** * The same boundary, with whatever the deployment fills in put in. * * Everything that reads a boundary's name for a person to see goes * through here first: the pairing pass, the drafter that writes an * intent document, and the intent checker that reads one back. A step * only one of them took would have the drafter write a name the * checker then argued with. */ declare function groundBinding(binding: BoundaryBinding, deployment: Deployment): BoundaryBinding; /** * Where this boundary's name says to go and ask, or null when the * source stated a name outright. A report that has to say why two * sides did not meet reads it to say which input would settle them. */ declare function nameReference(binding: BoundaryBinding): Reference | null; declare function pairingKey(binding: BoundaryBinding): string | null; declare function semanticsAgree(a: Semantics, b: Semantics): boolean; /** Whether the binding's bucket can meet buckets with other keys. */ declare function spansBuckets(binding: BoundaryBinding): boolean; /** Whether two bindings' buckets describe a boundary in common. */ declare function bucketsMeet(a: BoundaryBinding, b: BoundaryBinding): boolean; /** How narrowly the binding's bucket states what it serves; see `compareRanks`. */ declare function bucketRank(binding: BoundaryBinding): readonly number[]; declare function boundaryLabel(binding: BoundaryBinding): string | null; declare function displayLabel(binding: BoundaryBinding): string; /** * A boundary string that no protocol claims keeps its case, because * message-bus keys are case-sensitive and uppercasing one would break * the rule without saying so. */ declare function normalizeRuleBoundary(raw: string): string; declare function exchangesHttpResponses(binding: BoundaryBinding): boolean; /** Whether crossing this boundary leaves the process. */ declare function leavesTheProcess(binding: BoundaryBinding): boolean; /** * Whether anything can pair with this binding: it has a pairing key, * or its protocol pairs keyless boundaries in a dedicated pass. */ declare function canPair(binding: BoundaryBinding): boolean; declare function reportsUnpairedItself(binding: BoundaryBinding): boolean; /** * Null means the protocol does not address its boundaries by method and * path at all, which a caller has to tell apart from an unknown answer: * unknown means it does, but this declaration cannot settle the question. */ declare function servesRequest(binding: BoundaryBinding, method: string, path: string): MatchResult | null; /** * Whether a wrapper registered for `scope` runs for this boundary. * False for a protocol whose boundaries no pattern addresses, so a * scoped registration reaches nothing it cannot be shown to cover. */ declare function withinScope(binding: BoundaryBinding, scope: string): boolean; /** * How somebody spells a boundary they want to talk about, and whether * what they wrote picks out a given one. * * The words get cut into tokens, the boundary does too, and a boundary * matches when it has every token somebody wrote. So * `aws.dynamodb:editions` matches the table and every index on it, and * adding `#by-publication` narrows it to the one index. * * `suss ask`, `suss check --at` and an intent document that says which * store a write reaches all read this, so the question and the * assertion are spelled and resolved the same way. */ /** * The name inside a fully-qualified cloud resource id, and anything * else unchanged. * * `arn:aws:lambda:us-east-1:1234:function:prod-worker` and * `projects/p/locations/l/functions/prod-worker` say the same thing as * `prod-worker` plus an account and a region. Keeping the whole string * makes one deployment's spelling of a resource disagree with * another's, so every reader that meets one reduces it here and the two * sides compare the part they can both know. A Lambda ARN's trailing * alias or version comes off with the rest: the function is the * boundary whichever published copy a call reaches. */ declare function resourceNameIn(spelling: string): string; /** * The words in a boundary spelling. Separators between parts of a name * are cut, and the characters inside one part are left alone, so * `by-publication` stays one word and `{id}` and `:id` both come out as * `id`. */ declare function spellingTokens(text: string): string[]; /** Every word this boundary can be asked about by. */ declare function bindingTokens(binding: BoundaryBinding): Set; /** * Whether what somebody typed is the whole of this boundary's name * rather than part of it. `POST /articles` is exactly the collection * route and only part of `POST /articles/{slug}/comments`. */ declare function namesBoundaryExactly(subject: string, binding: BoundaryBinding): boolean; /** Whether what somebody typed picks out this boundary. */ declare function namesBoundary(subject: string, binding: BoundaryBinding): boolean; /** * Deciding when two message-bus channel strings mean the same thing. * * A channel is a subject, optionally qualified by the bus that carries * it, written `${bus}#${subject}`. The two sides of a pairing rarely * know the same amount, so they pair on the subject and the buses have * to agree only when both sides know one. The package README works * through why, with examples. * * Everything that writes or reads that string goes through * `formatChannel` and `parseChannel`, so the wire format has a single * author. A template reader that hand-writes the `#` would be a second * author, and the two would drift the first time either one changed. */ /** A channel string, written `${bus}#${subject}`, split into its two parts. */ interface ParsedChannel { bus: string | null; subject: string; } declare function parseChannel(channel: string): ParsedChannel; declare const ChannelBrand: unique symbol; /** * `bus#subject`, or the bare subject when no source said the bus. The * unqualified form is legal, so fields keep the type string and the * mint is the whole win: a channel assembled by hand cannot claim the * brand, and the format has one spelling (#167). */ type Channel = string & { readonly [ChannelBrand]: "channel"; }; declare function formatChannel(bus: string | null, subject: string): Channel; declare function busesAgree(a: string | null, b: string | null): boolean; declare function channelsPair(a: string, b: string): boolean; /** * One convention for the source directory a deployable unit is built * from, one test for whether a file is inside it, and one reading of * the handler string that says which code the platform calls. * * A deploy template gives a directory per unit, and a summary stores it * as `metadata.codeScope.path`. Producers write that path and the * checker reads it back as a prefix test, so the two sides have to * agree on whether it ends in a slash. The test also has to stop at a * segment boundary, or `src/foo` would cover `src/foobar` and a handler * would pair with the wrong function. A CloudFormation template and a * Terraform configuration spell a handler the same way, so they read it * through the same function rather than through two of them. */ /** * The canonical form of a code-scope directory: no leading `./`, no * trailing slash, no surrounding whitespace. An empty result means the * scope is the project root. */ declare function codeScopePath(raw: string): string; /** * Whether a project-relative file is inside a code scope. Matching stops * at a segment boundary, so `src/foo` covers `src/foo/a.ts` and never * `src/foobar/a.ts`. A scope that is the project root covers every file. * * Both arguments go through `codeScopePath`, so a scope stored with a * trailing slash and one stored without it are read the same way. */ declare function fileInCodeScope(file: string, scope: string): boolean; interface ParsedHandler { modulePath: string; exportName: string; } /** * Split a handler string into its module path and exported symbol. * The final dot separates them: `"src/handlers/confirmToken.handler"` → * `{ modulePath: "src/handlers/confirmToken", exportName: "handler" }`. * Returns null when there's no dot, since there's no export to bind to. */ declare function parseHandler(handler: string): ParsedHandler | null; /** * The DispatchTable idiom: dispatch on a discriminated union through a * mapped-type Record, so a missing variant is a type error rather than a * runtime fall-through. It is decision #8 in design/docs-internal/style.md. * * It is here rather than in one package because the decision applies * repo-wide, and every package that walks an IR union needs it. Three * of them had written their own copy before this one existed. */ type DispatchTable = { [K in T["type"]]: (variant: Extract) => R; }; declare function dispatchByType(table: DispatchTable, value: T): R; /** * The variables a deployment medium puts in a process's environment * whatever its configuration says. * * Code reads them and nothing declares them, so a reader that left them * out would accuse every deployment of failing to supply `AWS_REGION`. * Two readers describe the same Lambda, a CloudFormation template and a * Terraform configuration, and a variable one of them knew about and * the other did not would be an error in one and not the other, so the * list is stated once here. A medium whose vendor documents a different * set per product, Cloud Run's `K_SERVICE` against a plain container's * nothing, states its own list in the pack that describes it. */ /** * Sourced from each platform's own documentation: * * Lambda: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html * "Reserved environment variables" * ECS: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_metadata.html * plus the AWS_DEFAULT_REGION the Fargate runtime sets. */ declare const PLATFORM_INJECTED_ENV_VARS: Record>; /** * What a unit does at a boundary, in the words `suss ask` asks with. * * `what writes postgresql:invoices` is the question and "this outcome * writes postgresql:invoices" is the assertion, so the report, the * question parser and an intent doc all have to spell the verbs the * same way. They are here rather than beside any one of them. * * `provides` is the unit's own boundary; `reads`, `writes` and * `invokes` are what a call site does at somebody else's. Calling a * deployed unit by name is one act, so it gets a verb of its own. */ declare const RelationSchema: z.ZodEnum<{ provides: "provides"; reads: "reads"; writes: "writes"; invokes: "invokes"; }>; type Relation = z.infer; /** * The verbs an effect can have. A boundary a unit serves is not an * effect of the unit, so `provides` is not one of them. */ declare const EffectRelationSchema: z.ZodEnum<{ reads: "reads"; writes: "writes"; invokes: "invokes"; }>; type EffectRelation = z.infer; /** * pathPattern.ts: whether two route paths describe a request in common. * * A normalized path is text with holes in it. `{id}` is exactly one * segment, `{tenant?}` is zero or one, `{rest+}` is one or more and * `{rest*}` is zero or more, the modifiers Express puts on `:name`. A * bare `*` segment is zero or more segments, Express 4's reading of a * star. A set piece is written `(v1|v2)` and matches any one of its * options, and an option can contain a slash. * * `pathsMeet` expands the sets into alternatives, splits each one into * segments, and walks the two segment lists with a reachability table * in which a hole absorbs as many segments as its range allows. */ /** How many segments a hole takes, in the words the value domain uses. */ type HoleRange = "one" | "optional" | "many" | "any"; /** How a hole taking `range` segments is spelled in a path. */ declare function rangedHole(name: string, range: HoleRange): string; /** * How a piece that is one of several texts is spelled in a path, or * null when an option would be read as something else, such as a set * boundary or the start of a query. */ declare function setPiece(options: readonly string[]): string | null; /** Negative when `a` ranks below `b`, positive above, zero when equal. */ declare function compareRanks(a: readonly number[], b: readonly number[]): number; /** * The suppression pipeline, over the smallest finding both checkers share. * * Behavioural `Finding` and intent `IntentFinding` both have `kind`, * `severity`, and an optional `suppressed`, which is all the pipeline * needs. The rule schema, first-match-wins matching, effect application, * and threshold counting are here so the two checkers share one * implementation without depending on each other. Only the matching of a * rule's discriminators differs, and a caller passes that in. * * The module owns rules and matching, not file I/O. The CLI reads a * .sussignore file, checks each kind against the published enums, and * hands the parsed rules over. */ declare const SuppressionRuleSchema: z.ZodObject<{ kind: z.ZodOptional; boundary: z.ZodOptional; consumer: z.ZodOptional; transitionId: z.ZodOptional; }, z.core.$strip>>; provider: z.ZodOptional; transitionId: z.ZodOptional; }, z.core.$strip>>; scope: z.ZodDefault>; reason: z.ZodString; effect: z.ZodDefault>; }, z.core.$strict>; type SuppressionRule = z.infer; declare const SuppressionFileSchema: z.ZodObject<{ version: z.ZodLiteral<1>; rules: z.ZodArray; boundary: z.ZodOptional; consumer: z.ZodOptional; transitionId: z.ZodOptional; }, z.core.$strip>>; provider: z.ZodOptional; transitionId: z.ZodOptional; }, z.core.$strip>>; scope: z.ZodDefault>; reason: z.ZodString; effect: z.ZodDefault>; }, z.core.$strict>>; }, z.core.$strict>; type SuppressionFile = z.infer; type SuppressableSeverity = "error" | "warning" | "info"; /** The `suppressed` annotation stamped onto a matched finding. */ interface FindingSuppression { reason: string; effect: "mark" | "downgrade" | "hide"; /** Original severity, present only when effect is "downgrade". */ originalSeverity?: SuppressableSeverity | undefined; } /** * The structural base that both finding types satisfy. Behavioural * `Finding` and intent `IntentFinding` each declare these fields in * their own schemas, kept structurally identical, and the pipeline needs * nothing more. The `| undefined` unions match what zod infers for * `.optional()` fields under exactOptionalPropertyTypes. */ interface SuppressibleFinding { kind: string; severity: SuppressableSeverity; suppressed?: FindingSuppression | undefined; } /** * Check that a narrow rule constrains something. A bare rule with only * a `reason` would suppress every finding in the codebase, which is * almost always a mistake. Broad-scope rules are allowed to match less * specifically, on purpose. */ declare function validateRule(rule: SuppressionRule): string | null; /** * Whether a rule's `boundary` discriminator matches a finding's boundary * key. It tries an exact match first, which covers "fn:..." and * "gql:...", and then the REST-normalized form. */ declare function ruleBoundaryMatchesKey(ruleBoundary: string, key: string | null): boolean; /** * Apply suppression rules to a list of findings. This works for either * finding type: `matches` decides whether a rule's discriminators past * `kind` (boundary, consumer, and so on) match a finding, and the kind * check itself is the same everywhere, so it happens here. * * Returns a new array. Findings with `effect: "hide"` are omitted from * the output entirely unless `keepHidden` is set. Findings with * `effect: "mark"` or `"downgrade"` are included with an added * `suppressed` field. First matching rule wins. */ declare function applySuppressionsToFindings(findings: T[], rules: SuppressionRule[], matches: (rule: SuppressionRule, finding: T) => boolean, opts?: { keepHidden?: boolean; }): T[]; /** * `hide` and `mark` findings are left out of the exit-code threshold, * and a `downgrade` finding counts at the severity it was downgraded * to. Callers use this to decide whether a finding contributes to * `hasErrors`-style gating. */ declare function countsForThreshold(finding: SuppressibleFinding): boolean; /** * urlPath.ts: the path part of a URL, however the URL was written. * * Two sides of one boundary spell it differently. A provider says * `/orders`, because a route is mounted at a path. A consumer says * `http://backend.internal/orders`, because a call has to say which * host. They are the same boundary, and pairing them means reading the * path out of both. * * The query and the fragment come off with the origin. Neither picks a * route: `/orders?page=2` reaches the same handler as `/orders`. */ /** * The path a URL states, with any origin, query, and fragment removed. * * Text that states no origin comes back with only the query and the * fragment removed, since a relative URL is already a path. A URL that * is nothing but an origin comes back as an empty string, which is what * a caller wanting a path should treat as "the root". */ declare function pathAfterOrigin(text: string): string; /** Whether the text opens with an origin rather than with a path. */ declare function statesAnOrigin(text: string): boolean; /** * @suss/ir-core: primitives shared across suss IRs. * * The schemas are the single source of truth, and the types come from * them. Each boundary protocol's schema and behavior are in one module * under `./semantics`, and everything else is in `./schemas`. The * boundary-binding constructors are here too, so that any package that * produces an IR object (pattern packs, contract readers, intent docs, * tests) can build a binding without depending on a specific IR. */ type ConfidenceSource = z.infer; type ConfidenceLevel = z.infer; type ConfidenceInfo = z.infer; type Corroboration = z.infer; type SourceLocation = z.infer; type BoundaryBinding = z.infer; /** * Build a REST-semantics binding. `method` and `path` are null when the * source does not specify them. `"*"` is the method wildcard, for a * handler that responds to every method. When pairing, null means * unspecified and matches nothing, and `"*"` matches every concrete method. */ declare function restBinding(opts: { transport: string; method: string | null; path: string | null; recognition: string; declaredResponses?: number[]; }): BoundaryBinding; /** * Build a function-call-semantics binding. Used by in-process packs * (React components, custom-hook boundaries, bare TS function exports). */ declare function functionCallBinding(opts: { transport: string; recognition: string; module?: string; exportName?: string; package?: string; exportPath?: string[]; }): BoundaryBinding; /** * Build a function-call binding that identifies a public package export, * which is the provider side of a library boundary. Transport defaults * to `"in-process"`, which is how a TypeScript library is usually used. */ declare function packageExportBinding(opts: { transport?: string; recognition: string; packageName: string; exportPath: string[]; }): BoundaryBinding; /** * Build a graphql-resolver-semantics binding. Transport varies by * deployment: `"http"` for Apollo Server, `"aws-https"` for AppSync. */ declare function graphqlResolverBinding(opts: { transport: string; recognition: string; /** Null when the source never says which type the resolver attaches to. */ typeName: string | null; fieldName: string; }): BoundaryBinding; /** * Build a graphql-operation-semantics binding, which is the consumer side * of a GraphQL boundary. Anonymous operations leave `operationName` unset. */ declare function graphqlOperationBinding(opts: { transport: string; recognition: string; operationType: "query" | "mutation" | "subscription"; operationName?: string; }): BoundaryBinding; /** * Whether a binding is the consumer side of a GraphQL boundary, * settled by the protocol's own schema so callers do not compare the * semantics tag themselves. */ declare function isGraphqlOperationBinding(binding: BoundaryBinding | null | undefined): boolean; /** * Build a runtime-config binding for a runtime configuration channel * (env vars on a Lambda, ECS task, container, or k8s pod). Transport is * `"os"` because the OS hands env vars to the process at startup no * matter what the deployment medium is. * * A provider says which deployment it is. A recognizer standing at a * read in the code leaves both off, since nothing in the code says * which deployment will run it. */ declare function runtimeConfigBinding(opts: { recognition: string; deploymentTarget?: DeployableUnit["deploymentTarget"]; instanceName?: string; }): BoundaryBinding; /** * Build a unit-invocation binding, the boundary between a deployed unit * and whatever calls it by name. Transport is `"invoke"` because the * call goes through the platform's own invoke API whichever cloud runs * the unit. */ declare function unitInvocationBinding(opts: { recognition: string; deploymentTarget: DeployableUnit["deploymentTarget"]; /** Null when this source does not say which unit it invokes. */ instanceName: string | null; }): BoundaryBinding; /** * Build a storage binding, the side of a store that both a schema * reader and a call site can spell. `transport` defaults to the store's * own name, which is right for a database whose product and wire * protocol are the same word; a store reached over an SDK passes its * wire instead, and one whose engine nobody settled has none to pass. */ declare function storageBinding(opts: { recognition: string; /** Null when the source states an engine this reader could not settle. */ storageSystem: string | null; transport?: string; scope: string; /** Null when the source gives a container this reader could not settle. */ container: string | null; /** A secondary index or alias, or null for the container's own primary key. */ accessPath?: string | null; }): BoundaryBinding; /** * Build a message-bus binding, the boundary between a producer that * sends discrete messages and the consumer(s) that receive them. */ declare function messageBusBinding(opts: { recognition: string; messageBus: MessageBusTechnology; /** Null when this source does not say which channel. */ channel: string | null; }): BoundaryBinding; /** * Build a metric binding, the boundary between whatever declares a * named series of measurements and whatever reads it back by that name. */ declare function metricBinding(opts: { recognition: string; metricSystem: string; /** Null when this source states a metric it could not name. */ metricType: string | null; }): BoundaryBinding; export { type BoundaryBehavior, type BoundaryBinding, BoundaryBindingSchema, type BoundaryName, type BoundarySemanticsDefinition, type BusIdentityKey, type Channel, type ConfidenceInfo, ConfidenceInfoSchema, type ConfidenceLevel, ConfidenceLevelSchema, type ConfidenceSource, ConfidenceSourceSchema, type Corroboration, CorroborationSchema, DeployableUnit, type Deployment, type DispatchTable, type Ecosystem, type EffectRelation, EffectRelationSchema, type FindingSuppression, type FnIdentityKey, type GqlIdentityKey, type HoleRange, type MatchResult, MessageBusSemantics, MessageBusTechnology, type MetricIdentityKey, type MetricSemantics, MetricSemanticsSchema, NOTHING_DEPLOYED, type NamePart, PLATFORM_INJECTED_ENV_VARS, type PackConfiguration, type PackDeclaration, type ParsedChannel, type ParsedHandler, type Reference, type Relation, RelationSchema, Semantics, type SemconvAttribute, type SemconvMapping, type SourceLocation, SourceLocationSchema, type SuppressableSeverity, type SuppressibleFinding, type SuppressionFile, SuppressionFileSchema, type SuppressionRule, SuppressionRuleSchema, TypeShape, type UnitIdentityKey, type UnitInvocationSemantics, UnitInvocationSemanticsSchema, applySuppressionsToFindings, bindingIs, bindingTokens, bodyShapesMatch, boundaryKey, boundaryLabel, boundaryNameString, bucketRank, bucketsMeet, busIdentityKey, busesAgree, canPair, channelsPair, codeScopePath, compareRanks, countsForThreshold, dispatchByType, displayLabel, exchangesHttpResponses, fileInCodeScope, fixedTextLength, fnIdentityKey, formatChannel, functionCallBinding, gqlIdentityKey, graphqlOperationBinding, graphqlResolverBinding, groundBinding, groundedPairingKey, hasNameHole, isGraphqlOperationBinding, leavesTheProcess, messageBusBinding, metricBinding, metricIdentityKey, namePatternFromSub, namePatternKey, nameReference, namesAgree, namesBoundary, namesBoundaryExactly, namesNothing, normalizeRuleBoundary, packageExportBinding, pairingKey, parseBoundaryName, parseChannel, parseHandler, pathAfterOrigin, patternHole, rangedHole, referenceFromName, referenceName, referenceOf, reportsUnpairedItself, resourceNameIn, restBinding, ruleBoundaryMatchesKey, runtimeConfigBinding, semanticsAgree, semconvAttributes, servesRequest, setPiece, spansBuckets, spellingTokens, statesAnOrigin, storageBinding, unitIdentityKey, unitInvocationBinding, validateRule, withRewrittenPaths, withinScope };