/** * Scope-resolution type definitions — RFC §2 data model (authoritative source). * * See: https://www.notion.so/346dc50b6ed281cfaacbe480bf231d50 * * Anti-drift rule: every type, interface, and enum defined here is the single * source of truth. Later code that references these names must import them * from `gitnexus-shared`; it must not re-define them locally. * * Lifecycle contract (RFC §2.8): scopes are **constructed during extraction, * linked during finalize, immutable after finalize**. All fields are * `readonly` at the type level; `Object.freeze` is applied at runtime in dev * builds. `ReferenceIndex` is the sole structure populated after freeze — by * resolution, before emission. */ import type { NodeLabel } from '../graph/types.js'; import type { SymbolDefinition } from './symbol-definition.js'; /** Stable per-(file, range, kind) scope identifier; interned for identity-fast equality. */ export type ScopeId = string; /** Stable symbol-definition identifier (graph nodeId). */ export type DefId = string; /** Kinds of lexical scope a `Scope` node can represent. */ export type ScopeKind = 'Module' | 'Namespace' | 'Class' | 'Function' | 'Block' | 'Expression'; /** Source-text range. 1-based `startLine`/`endLine`; 0-based `startCol`/`endCol`. */ export interface Range { readonly startLine: number; readonly startCol: number; readonly endLine: number; readonly endCol: number; } /** * Tagged capture emitted by a LanguageProvider's `emitScopeCaptures` hook. * * Parser-agnostic: tree-sitter queries and COBOL's regex tagger both produce * `Capture[]`. The central `ScopeExtractor` consumes captures without * knowing which parser produced them. */ export interface Capture { /** Capture name, including leading `@` (e.g., `'@scope.module'`, `'@declaration.class'`). */ readonly name: string; readonly range: Range; /** The captured source text. */ readonly text: string; } /** * A grouping of `Capture`s that came from a single query match (e.g., one * `@import.statement` match carries `@import.source`, `@import.name`, * `@import.alias?` as child captures). Keyed by capture name for O(1) * child access. */ export type CaptureMatch = Readonly>; /** * Provider-interpreted raw import, consumed by finalize (Phase 2) to produce * linked `ImportEdge[]`. The provider's `interpretImport` hook turns a * `CaptureMatch` for an `@import.statement` into one of these; the central * finalize algorithm resolves `targetRaw` to a concrete file via * `resolveImportTarget` and materializes the final `ImportEdge`. * * Discriminated union — each variant carries only the fields that make sense * for its kind. Invalid shapes (e.g., a `namespace` import with an alias-like * `importedName` mismatch) are compile errors, not latent bugs. `'wildcard- * expanded'` is deliberately NOT a variant: that kind is finalize output only, * produced when `expandsWildcardTo` materializes a wildcard against target * exports — a provider must never emit it at parse time. */ export type ParsedImport = /** * Per-name import without rename. * * Examples: * - Python `from foo import X` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: 'foo' }` * - TS `import { X } from './foo'` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: './foo' }` * - Java `import foo.bar.X` → `{ kind: 'named', localName: 'X', importedName: 'X', targetRaw: 'foo.bar' }` */ { readonly kind: 'named'; readonly localName: string; readonly importedName: string; readonly targetRaw: string; } /** * Per-name import with rename. * * Examples: * - Python `from foo import X as Y` → `{ kind: 'alias', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: 'foo' }` * - TS `import { X as Y } from './foo'` → `{ kind: 'alias', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: './foo' }` */ | { readonly kind: 'alias'; readonly localName: string; readonly importedName: string; readonly alias: string; readonly targetRaw: string; } /** * Qualified module handle, with or without rename. `importedName` is the * module being aliased; `localName` is the scope-visible handle (often the * same unless renamed). * * Examples: * - Python `import numpy` → `{ kind: 'namespace', localName: 'numpy', importedName: 'numpy', targetRaw: 'numpy' }` * - Python `import numpy as np` → `{ kind: 'namespace', localName: 'np', importedName: 'numpy', targetRaw: 'numpy' }` * - TS `import * as np from 'numpy'` → `{ kind: 'namespace', localName: 'np', importedName: 'numpy', targetRaw: 'numpy' }` * - Go `import foo "pkg/bar"` → `{ kind: 'namespace', localName: 'foo', importedName: 'bar', targetRaw: 'pkg/bar' }` */ | { readonly kind: 'namespace'; /** Scope-visible handle (e.g. `np` in `import numpy as np`; `numpy` when unaliased). */ readonly localName: string; /** Module being aliased (e.g. `numpy` in `import numpy as np`). */ readonly importedName: string; readonly targetRaw: string; } /** * Syntactically-detectable parse-time re-export. Finalize may still produce * `ImportEdge { kind: 'reexport', transitiveVia }` when flattening chains; * this variant preserves the *parse-time* signal so finalize doesn't have * to re-derive it from scratch. * * Examples: * - TS `export { X } from './y'` → `{ kind: 'reexport', localName: 'X', importedName: 'X', targetRaw: './y' }` * - TS `export { X as Y } from './y'` → `{ kind: 'reexport', localName: 'Y', importedName: 'X', alias: 'Y', targetRaw: './y' }` * - Rust `pub use foo::bar` → `{ kind: 'reexport', localName: 'bar', importedName: 'bar', targetRaw: 'foo' }` */ | { readonly kind: 'reexport'; /** Name as re-exported in the current module. */ readonly localName: string; /** Name in the source module. */ readonly importedName: string; readonly targetRaw: string; /** Set when the re-export renames the symbol (e.g. `export { X as Y } from './y'`). */ readonly alias?: string; } /** * Wildcard import — brings every exported name from the target module into * the importing scope. The finalize algorithm expands this into one * `BindingRef` per exported name via the provider's `expandsWildcardTo` * hook, producing the finalize-only `ImportEdge` kind `'wildcard-expanded'`. * * Examples: * - Python `from foo import *` → `{ kind: 'wildcard', targetRaw: 'foo' }` * - JS `export * from './foo'` → `{ kind: 'wildcard', targetRaw: './foo' }` * - Rust `pub use foo::*` → `{ kind: 'wildcard', targetRaw: 'foo' }` */ | { readonly kind: 'wildcard'; readonly targetRaw: string; } /** * Runtime-computed target — the import path is not a static literal at * parse time. Providers SHOULD emit the unresolvable expression's source * text as `targetRaw` to aid diagnostics; `null` only when no string form * exists. * * Examples: * - JS `await import(expr)` → `{ kind: 'dynamic-unresolved', localName: '', targetRaw: 'expr' }` * - Python `importlib.import_module(f'pkg.{name}')` → `{ kind: 'dynamic-unresolved', localName: '', targetRaw: "f'pkg.{name}'" }` */ | { readonly kind: 'dynamic-unresolved'; readonly localName: string; /** Source text of the unresolved expression when available; `null` otherwise. */ readonly targetRaw: string | null; }; /** * Provider-interpreted type binding. The provider's `interpretTypeBinding` * hook turns a `CaptureMatch` (e.g., `@type-binding.parameter`) into one of * these; the central extractor attaches the resulting `TypeRef` to the * appropriate scope's `typeBindings` map. */ export interface ParsedTypeBinding { /** The name being bound (parameter name, `self`, assignment LHS, …). */ readonly boundName: string; /** The raw type name as written in source (`'User'`, `'models.User'`, …). */ readonly rawTypeName: string; readonly source: TypeRef['source']; } /** * Cross-file workspace index consumed by finalize-phase hooks * (`resolveImportTarget`, `expandsWildcardTo`). Opaque placeholder in Ring 1; * concretely typed in Ring 2 SHARED (#915). */ export type WorkspaceIndex = unknown; /** * Minimal scope-lookup contract: map a `ScopeId` back to its `Scope` record. * * Lives in the data-model layer so both `ScopeTree` (§3.1) and * `resolveTypeRef` / `Registry.lookup` (§4) can depend on it without * inverting each other. `ScopeTree` is the canonical implementation; * tests and future alternative containers may supply their own. */ export interface ScopeLookup { getScope(id: ScopeId): Scope | undefined; } /** Call-site description passed to `arityCompatibility`. */ export interface Callsite { /** Number of arguments at the call site. */ readonly arity: number; } /** * A cross-file import edge attached to a module/namespace scope. * * Raw (unlinked) edges are emitted during parse (Phase 1); `targetModuleScope` * and `targetDefId` are filled in during finalize (Phase 2) via SCC-aware * bounded-fixpoint linking (RFC §3.2). */ export interface ImportEdge { /** How this scope sees the imported name (after alias). */ readonly localName: string; /** Exporting file; `null` only when `kind === 'dynamic-unresolved'`. */ readonly targetFile: string | null; /** The name under which the target exports this symbol. */ readonly targetExportedName: string; /** Pre-resolved at finalize: the module scope of the exporting file. */ readonly targetModuleScope?: ScopeId; /** Pre-resolved at finalize: the exported symbol's `DefId`. */ readonly targetDefId?: DefId; readonly kind: 'named' | 'alias' | 'namespace' | 'wildcard-expanded' | 'reexport' | 'dynamic-unresolved'; /** Re-export chain, for provenance (e.g., `['./y']` when re-exported via `./y`). */ readonly transitiveVia?: readonly string[]; /** Set to `'unresolved'` when the SCC fixpoint could not link this edge. */ readonly linkStatus?: 'unresolved'; } /** * A name binding visible at a scope, with provenance. * * Provenance stays at the visibility layer — a name being visible because it * is local vs imported vs wildcard-expanded vs re-exported is a property of * the binding itself. This keeps evidence emission and `import-use` reference * stamping first-class instead of reconstructing provenance from a side table. */ export interface BindingRef { readonly def: SymbolDefinition; readonly origin: 'local' | 'import' | 'namespace' | 'wildcard' | 'reexport'; /** Non-null for non-local origins; carries the `ImportEdge` that brought the name into this scope. */ readonly via?: ImportEdge; } /** * A reference to a named type, anchored at its declaration site. * * Design choice: raw name + declaration-site scope, resolved at lookup time. * Pre-resolution would invert the extraction/resolution wall. Deferred thunks * add no capability. Structured type systems are months of work per language. * This shape keeps V1 tractable while preserving correctness for aliases, * re-exports, and nested modules. Generics deferred to V2 via `typeArgs`. */ export interface TypeRef { /** The name as written in source (e.g., `'User'`, `'models.User'`, `'List'`). */ readonly rawName: string; /** Anchor for resolving `rawName` — the scope where the annotation/inference was written. */ readonly declaredAtScope: ScopeId; readonly source: 'annotation' | 'parameter-annotation' | 'return-annotation' | 'self' | 'assignment-inferred' | 'constructor-inferred' | 'receiver-propagated'; /** Reserved for V2+: generic type arguments (`List` → `[TypeRef('User')]`). V1 ignores. */ readonly typeArgs?: readonly TypeRef[]; } /** * The canonical lexical-scope node. Forms the spine of the SemanticModel. * * ScopeId shape (RFC §2.2): `scope:{filePath}#{startLine}:{startCol}-{endLine}:{endCol}:{kind}` * — deterministic, stable across reparses of the same source, interned. */ export interface Scope { readonly id: ScopeId; readonly parent: ScopeId | null; readonly kind: ScopeKind; readonly range: Range; readonly filePath: string; /** Names visible from this scope. Provenance preserved via `BindingRef.origin`. */ readonly bindings: ReadonlyMap; /** Defs structurally owned by this scope (e.g., methods owned by a class body scope). */ readonly ownedDefs: readonly SymbolDefinition[]; /** Import edges attached to this scope. Mostly module/namespace scopes, but some * languages allow local imports (Python `def f(): from x import Y`, Rust * fn-local `use`, TS dynamic `import()`). */ readonly imports: readonly ImportEdge[]; /** Local type facts visible from this scope (parameter annotations, `self` binding, etc.). */ readonly typeBindings: ReadonlyMap; } /** * One piece of evidence for a `Resolution`. Multiple signals corroborate a * single match; their weights compose additively to produce `confidence`. * * Weights come from `EvidenceWeights` (see `./evidence-weights.ts`). */ export interface ResolutionEvidence { readonly kind: 'local' | 'scope-chain' | 'import' | 'type-binding' | 'owner-match' | 'kind-match' | 'arity-match' | 'global-name' | 'global-qualified' | 'dynamic-import-unresolved'; /** Signal weight, sourced from `EvidenceWeights`. Additive; sum capped at 1.0. */ readonly weight: number; /** Optional debug annotation (e.g., `'matched via self: User'`). */ readonly note?: string; } /** * A ranked resolution candidate returned by `ClassRegistry.lookup` / * `MethodRegistry.lookup` / `FieldRegistry.lookup`. Evidence composes * additively; callers read `[0]` for the one-shot answer or inspect the * evidence trace for debugging. */ export interface Resolution { readonly def: SymbolDefinition; /** Σ of `evidence[].weight`, capped at 1.0. */ readonly confidence: number; readonly evidence: readonly ResolutionEvidence[]; /** Optional debug trace: scopes walked to reach `def`. */ readonly path?: readonly ScopeId[]; } /** * A post-resolution usage fact: some code at `atRange` inside `fromScope` * references `toDef` with the given confidence/evidence. Materialized by the * resolution phase; emitted as graph edges (`CALLS`/`READS`/`WRITES`/etc.) * during the emit phase. */ export interface Reference { /** Innermost lexical scope containing `atRange`. */ readonly fromScope: ScopeId; readonly toDef: DefId; /** Location of the reference in source. */ readonly atRange: Range; readonly kind: 'call' | 'read' | 'write' | 'type-reference' | 'inherits' | 'import-use'; readonly confidence: number; readonly evidence: readonly ResolutionEvidence[]; } /** * Two-way index over `Reference` records, populated during the resolution * phase. Scopes stay immutable after finalize; references accumulate here. */ export interface ReferenceIndex { readonly bySourceScope: ReadonlyMap; readonly byTargetDef: ReadonlyMap; } /** * Opaque placeholder for the per-kind registry passed as the owner-scoped * contributor. Typed concretely in Ring 2 SHARED (#917); kept as `unknown` * here so Ring 1 can ship without pulling in the registry implementation. */ export type RegistryContributor = unknown; /** * Parameters accepted by `Registry.lookup`. Three registries (Class/Method/ * Field) run the same 7-step algorithm with different parameter tuples; see * RFC §4.4 for per-registry specializations. */ export interface LookupParams { readonly acceptedKinds: readonly NodeLabel[]; /** Class lookups: false. Method/Field lookups: true. */ readonly useReceiverTypeBinding: boolean; readonly ownerScopedContributor: RegistryContributor | null; /** Optional arity hint fed to `provider.arityCompatibility`. */ readonly arityHint?: number; /** Explicit receiver name (e.g., `'user'` in `user.save()`). When present, * the receiver's type binding at the callsite scope is used; otherwise * the enclosing method's implicit `self`/`this` is consulted. See §4.1. */ readonly explicitReceiver?: { readonly name: string; }; } //# sourceMappingURL=types.d.ts.map