/** * A server declaration, and everything a spread composes into it. * * D115 P4 R4a. One module is all this analyzer sees, so a spread contributes * routes only when its value still reaches a server declared here — directly, * through an alias, or through the `velar/serve` combinators that carry paths * through. Anything else is let through unchecked and judged by D90 R19's * runtime referee when the table is finally assembled, because a false conflict * reported here would block a correct program. */ import { type Expression, type Program, type Span, type Statement, type ValueType } from "@velarscript/compiler/extension"; import { type ComposedFallback, type ComposedRoute, type ComposedServer, type ServeCombinator, type ServerAlias } from "../contracts.ts"; import { type NodeResponseDeclaration, type NodeServerDeclaration, type NodeServerSpread } from "../server-ast.ts"; import { type HandlerAnalysisHost } from "./handlers.ts"; /** * What server composition asks of the analyzer that hosts it. The four module * tables are filled by `analyze` before the walk begins and read while it runs; * `stableAliases` is the one this file writes, because the question it answers * — was this `let` ever reassigned — costs a walk of the whole program and is * asked once per name. */ export interface ServerCompositionHost extends HandlerAnalysisHost { readonly moduleProgram: Program | null; readonly moduleServeCombinators: ReadonlyMap; readonly moduleServerAliases: ReadonlyMap; readonly moduleServers: ReadonlyMap; readonly stableAliases: Map; declareBinding(name: string, mutable: boolean, type: ValueType, declarationSpan: Span): void; isPredeclared(statement: NodeServerDeclaration): boolean; isTopLevelScope(): boolean; lookup(name: string): { readonly span: Span; } | null; } export declare function analyzeServer(host: ServerCompositionHost, statement: NodeServerDeclaration): void; /** * Enters one route into this server's shape map and compares it against every route already * entered, whether that route was written here or composed in by a spread. Composition is why the * entries carry an origin: a conflicting route the author cannot see in his own file has to name * the server it came from. */ export declare function recordRoute(host: ServerCompositionHost, entry: ComposedRoute, routes: Map, operations: Map, span: Span): void; /** * The routes and fallback a spread composes into the server that writes it, or null when the * spread is not statically resolvable. This analyzer sees one module, so a spread contributes * only when its value reaches a server declared in this module: a plain identifier, an alias of * one, or a velar/serve combinator call around one — `use`, `bodyLimit`, `docs` and `lifecycle` * carry paths through unchanged, and `prefix` translates them by its literal path. An imported * server, a computed prefix path, or any other expression is let through unchecked, because a * false conflict here would block a correct program; D90 R19's runtime referee judges the final * table at assembly instead. Composition is followed transitively; the visited set bounds a * cycle and starts holding the composing server, so a cycle never folds a server's own routes * back into itself and reports each as conflicting with itself. */ export declare function composedItems(host: ServerCompositionHost, statement: NodeServerDeclaration, spread: NodeServerSpread): { readonly routes: readonly ComposedRoute[]; readonly notFound: ComposedFallback | null; readonly responsePolicy: NodeResponseDeclaration | null; } | null; /** * The server declaration a spread value names, or null when it is anything else. A * `const other = base` alias chain of this module's own servers resolves too, because the alias * holds exactly that ServeApp — and so does a `let` alias the whole module never reassigns, * because an unwritten `let` holds its initializer exactly as a `const` does. A reassigned or * ambiguous `let`, a member path, a conditional, an import, or a parameter contributes nothing. * A call resolves through the path-preserving velar/serve combinators when the callee still * reaches its velar/serve import: `prefix` with a literal path translates what its app argument * declares, and `use`/`bodyLimit`/`docs`/`lifecycle` pass it through untouched. A computed * prefix path contributes nothing — the assembly-time referee owns it. An alias's initializer * re-enters this resolver whole, so `const scoped = prefix("/api", routes)` resolves exactly as * the spelled-out spread does; the followed set bounds an alias cycle. */ export declare function resolveComposedServer(host: ServerCompositionHost, value: Expression, followed?: Set): ComposedServer | null; /** Whether a `let` alias has never been reassigned, asked once per name and program. */ export declare function aliasBindingIsStable(host: ServerCompositionHost, alias: ServerAlias): boolean; /** * Whether a name still reaches the declaration this module recorded for it. An import, a * shadowing binding, or a parameter of the same name reaches a different binding. */ export declare function resolvesTo(host: ServerCompositionHost, name: string, span: Span): boolean; /** * A module-level `const name = expression` or `let name = expression` binding, the indirect * spellings of a spread target that can still be exactly the value the initializer resolves to: a * bare name, or a velar/serve combinator call the resolver sees through. A `let` alias resolves * only after the stability predicate confirms the module never reassigns it — that check belongs * to the resolver, which is the point that knows the whole program. A pattern binding is excluded * because it never holds the whole value. */ export declare function moduleServerAlias(statement: Statement): ServerAlias | null; /** A composed route's address as the runtime's `prefix` will spell it: the literal prefix, then the path. */ export declare function prefixedRoutePath(prefix: string, path: string): string; //# sourceMappingURL=composition.d.ts.map