/**
 * Per-variable initialization dependency graphs.
 *
 * Walks every top-level declaration + bare statement in a set of
 * pre-collected agency programs (the entry's full import closure) and
 * produces two independent dep graphs:
 *
 *   - `staticGraph` — one node per top-level `static const`, edges from
 *     each node to every other init-var node its initializer references.
 *     Sorted by `topSortInitGraph` to drive Phase A (once per process).
 *   - `globalGraph` — one node per non-static `const` / `let` /
 *     unscoped assignment, plus one node per bare top-level statement
 *     (function call etc., keyed by a synthetic `__bareStmt_…` name).
 *     Drives Phase B (every run).
 *
 * Edges are derived only from *direct* free-variable references in the
 * initializer expression. Function references (`def foo`, `node foo`)
 * are NOT edges — the dep graph orders *values*, not callable code.
 * The runtime read-before-init trap (PR 1) catches the residual case
 * where an initializer indirectly reads an unset static through a
 * function call.
 *
 * Cross-module references are resolved through the shared
 * {@link ImportAliasResolver}, which walks `export { x } from "y"`
 * chains to the ultimate source. The same resolver is reused by the
 * codegen wrap site (Task 4) to thread the source `moduleId` into the
 * PR-1 trap message — there is exactly one re-export resolver in the
 * codebase.
 *
 * **Closed interface:** consumers see only `nodes + edges` per graph;
 * file-import depth and source line are baked into each node's
 * `sequenceHint` at build time so the topsort has one ordering rule.
 *
 * **Closure walking is NOT done here.** The caller (Task 3's
 * `compileClosure`) is responsible for parsing every reachable module
 * once and passing the full `programs` map in. Keeps this module pure
 * and testable.
 *
 * Phase coupling rules enforced by this builder:
 *   - A static initializer that references a global → throws
 *     `StaticReferencesGlobalError` (the global doesn't exist yet at
 *     Phase A).
 *   - A global initializer that references a static → allowed,
 *     no edge (statics are already initialized at Phase B time).
 */
import type { AgencyProgram, AgencyNode, Expression } from "../types.js";
import type { SourceLocation } from "../types/base.js";
import type { FunctionDefinition } from "../types/function.js";
import type { SymbolTable } from "../symbolTable.js";
export type InitVarKind = "static" | "global";
/**
 * One node in an init dep graph — corresponds to a single top-level
 * declaration (or, for the global graph only, a single bare top-level
 * statement).
 *
 * `moduleId` is the absolute path of the source file the var was
 * declared in (NOT the file that imports it). For re-exports, the
 * canonical node lives in the originating source module — re-exporters
 * do not get their own node.
 *
 * `sequenceHint` packs `(fileImportDepth, sourceLine)` into one number
 * so the topsort can break ties deterministically with a single key.
 * Lower wins (initializes earlier).
 */
export type InitVarNode = {
    moduleId: string;
    varName: string;
    kind: InitVarKind;
    /** For bare statements (global graph only) this is the wrapping
     * statement node; for assignments it's the right-hand side. */
    initExpr: Expression | AgencyNode;
    loc?: SourceLocation;
    exported: boolean;
    sequenceHint: number;
    /** Set when the source wrapped the decl/statement in `with approve`.
     * `handle { ... }` blocks are NOT legal at module top level, so this
     * single optional flag covers the full top-level handler surface. */
    withApprove?: boolean;
};
/** Composite key for indexing init-var nodes: `${moduleId}::${varName}`. */
export type InitVarKey = string;
export declare function makeKey(moduleId: string, varName: string): InitVarKey;
/**
 * Edges go from a node to every other node its initializer references.
 * `topSortInitGraph` uses these to compute initialization order; cycles
 * produce a `CycleError`.
 */
export type InitDepGraph = {
    nodes: Record<InitVarKey, InitVarNode>;
    edges: Record<InitVarKey, InitVarKey[]>;
};
export type BuildInitDepGraphsResult = {
    staticGraph: InitDepGraph;
    globalGraph: InitDepGraph;
    /** Reused by codegen (Task 4) for PR-1 thread-through. */
    resolver: ImportAliasResolver;
    /** Look-up `(name | namespace.member) → top-level FunctionDefinition`,
     * scoped to the importing module. PR-2.5 uses it to drive depth-1
     * expansion of init-expression deps through direct function calls. */
    functionDefs: FunctionDefLookup;
};
/**
 * Resolves a name used inside one module to the top-level Agency
 * function definition that backs it. Returns the function's home
 * `moduleId` alongside the AST so callers can resolve free identifiers
 * *inside* the function body against the function's own import surface
 * — not the caller's.
 *
 * `find` handles bare-name calls (`getBar()`). `findNamespaceMember`
 * handles namespace-prefixed calls (`bar.getBar()`).
 *
 * Returns `null` for names that don't resolve to an Agency function we
 * have AST for — stdlib calls, function values stored in variables,
 * unknown names, etc. The depth-1 expansion treats those as "no
 * expansion", and the runtime read-before-init trap (PR 1) remains the
 * safety net for anything the static analysis can't see.
 *
 * Built once per `compileClosure` call; cached per importing module so
 * a lookup is O(1) after first use per module.
 */
export type FunctionDefLookup = {
    find(name: string, inModuleId: string): {
        moduleId: string;
        def: FunctionDefinition;
    } | null;
    findNamespaceMember(prefix: string, member: string, inModuleId: string): {
        moduleId: string;
        def: FunctionDefinition;
    } | null;
};
/**
 * Build a `FunctionDefLookup` over the entry's full import closure.
 *
 * Two layers of cache:
 *   - `localDefsByModule[moduleId]` — name → FunctionDefinition for all
 *     top-level `def` statements declared in that module. Populated
 *     lazily; same shape used as the destination of both bare-name and
 *     namespace-member lookups.
 *   - `cache[inModuleId]` — name → resolved `(moduleId, def)` for the
 *     importing module's view (local defs + named-import aliases of
 *     functions declared elsewhere). Populated lazily on first lookup.
 */
export declare function makeFunctionDefLookup(programs: Record<string, AgencyProgram>, resolver: ImportAliasResolver, symbolTable: SymbolTable | undefined): FunctionDefLookup;
/**
 * Compile-time error: a `static` initializer references a `global`. The
 * global doesn't exist yet at Phase A time, so this is unsatisfiable.
 *
 * Surface format prefers human-readable names: `static const x` is shown
 * as `static const 'x'`, while a `static <bare>` whose synthetic varName
 * starts with `__bareStmt_` is shown as `static <bare statement>`. Line
 * numbers fall back to `?` only when neither the static wrapper nor the
 * dep node has a usable `loc`.
 */
export declare class StaticReferencesGlobalError extends Error {
    readonly staticNode: InitVarNode;
    readonly globalNode: InitVarNode;
    constructor(staticNode: InitVarNode, globalNode: InitVarNode);
}
/**
 * Resolves a locally-bound name (used inside an initializer in some
 * module) to the `(moduleId, name)` pair that defines the value, walking
 * `export { x } from "y"` chains to the ultimate source.
 *
 * `resolveNamespace` covers the `import * as bar from "./bar.agency"`
 * shape: given the local prefix `bar`, returns the source module so
 * `bar.barStatic` can be resolved as `(bar.agency, barStatic)`. Returns
 * null when no namespace import bound that prefix in this module.
 *
 * Built once per `compileClosure` call; cached internally so a name
 * lookup is O(1) after first use per module.
 */
export type ImportAliasResolver = {
    resolve(localName: string, inModuleId: string): {
        sourceModuleId: string;
        sourceName: string;
    } | null;
    resolveNamespace(prefix: string, inModuleId: string): {
        sourceModuleId: string;
    } | null;
};
export declare function makeImportAliasResolver(programs: Record<string, AgencyProgram>, symbolTable: SymbolTable | undefined): ImportAliasResolver;
/**
 * Build the two phase-separated dep graphs from the entry's full
 * pre-parsed import closure.
 *
 * `programs` maps absolute module paths to their parsed AST and MUST
 * already contain every module reachable from `entryModuleId`. We do
 * not re-walk imports — the entry point (`compileClosure`) owns that.
 *
 * `symbolTable`, when provided, enables re-export chain resolution.
 * Without it, re-exporters appear as unresolved local names and produce
 * no edge — fine for the simplest unit tests but pulls the
 * `importedAlias` codegen path into the same starvation case the
 * PR-1 trap covers, so production callers should always pass one.
 */
export declare function buildInitDepGraphs(programs: Record<string, AgencyProgram>, symbolTable: SymbolTable | undefined, entryModuleId: string): BuildInitDepGraphsResult;
/**
 * Walk an init expression for **direct call sites** — both bare-name
 * calls (`getBar()`) and namespace-method calls (`bar.getBar()`) — and
 * return the FunctionDefinitions they resolve to, paired with the
 * function's home module. Used by the depth-1 expansion to discover
 * which functions to look inside.
 *
 * Skips identifiers inside nested `function` / `graphNode` bodies via
 * the same ancestor check `collectFreeIdentifiers` uses, so when we
 * are already mid-walk of one function's body we don't accidentally
 * descend into another function defined alongside it.
 *
 * `collectFreeIdentifiers` cannot do this job because `walkNodes`'s
 * `functionCall` branch deliberately does not yield the function name
 * as a `variableName` (the call's identifier isn't a value
 * reference). Method-call chain elements (`obj.method()`) likewise
 * don't surface as `member` free refs — they're `methodCall` chain
 * entries, not `property` chain entries.
 */
export declare function collectDirectCalls(expr: Expression | AgencyNode, inModuleId: string, functionDefs: FunctionDefLookup): {
    moduleId: string;
    def: FunctionDefinition;
}[];
/**
 * Collect every free identifier in a function body, treating each top-
 * level statement of the body independently so the function's *own*
 * `function` node isn't on the ancestor stack (which would cause
 * `collectFreeIdentifiers` to skip everything by design). The result
 * still skips any further nested `function` / `graphNode` bodies — the
 * depth-1 boundary holds.
 *
 * **Parameter-shadow filtering.** Drops refs whose name (or `prefix`,
 * for member refs) matches one of the function's parameter names.
 * Otherwise a parameter that happens to share a name with a top-level
 * decl (e.g. `def readG(g: string) { return g }` when a top-level
 * `g` exists) would resolve through the import alias resolver to the
 * top-level binding and produce a spurious init edge or false
 * `StaticReferencesGlobalError`.
 */
export declare function collectFunctionBodyFreeRefs(def: FunctionDefinition): FreeRef[];
/**
 * A single free reference inside an initializer expression:
 *   - `name`: a bare identifier like `barStatic` (resolved via the
 *     `resolve` alias map).
 *   - `member`: a two-part reference of the form `prefix.member`
 *     where `prefix` is bound by a local declaration. The dep graph
 *     treats it as an edge target only when `prefix` was bound by a
 *     namespace import (`import * as bar from "./bar.agency"`), in
 *     which case it resolves to `(bar.agency, member)` — the same
 *     edge a named import would have produced. Local-variable
 *     member accesses (`person.name`) produce no edge.
 */
export type FreeRef = {
    kind: "name";
    name: string;
} | {
    kind: "member";
    prefix: string;
    member: string;
};
/**
 * Collect every free reference in `expr`, declaratively, via the
 * shared `walkNodes` walker. Skips identifiers that appear inside
 * nested name-binding constructs (`function`, `graphNode`) by checking
 * the ancestor stack — those bodies don't execute during the outer
 * initializer evaluation.
 *
 * Surfaces both bare identifiers (`barStatic`) and `prefix.member`
 * patterns (`bar.barStatic`) so the dep graph can resolve namespace
 * imports as cross-module edges. When a variableName is the base of
 * a `prefix.member`-shape valueAccess we skip its standalone yield;
 * the member form supersedes it.
 *
 * Exported so callers outside this module (e.g. `compileClosure`) can
 * reuse the same free-reference discipline when computing additional
 * cross-phase await dependencies that aren't representable as edges in
 * either single-phase graph.
 */
export declare function collectFreeIdentifiers(expr: Expression | AgencyNode): FreeRef[];
