import type { BuilderRegistries } from '../model/builder.js'; import type { MEMOConfig } from '../model/config.js'; import type { Diagnostic } from './diagnostic.js'; import type { MemoIr } from './protocol.js'; /** * What question a provider answers. * * validator "Is this valid SysML/KerML?" * lowering "What can MEMO ingest from this revision?" * package "How is this project packed and published?" * * One provider per role per revision. Two roles may be filled by the same tool. * Running two validators, or two lowerers, is never a thing MEMO does. */ export type ProviderRole = 'validator' | 'lowering' | 'package'; export declare const PROVIDER_ROLES: readonly ProviderRole[]; /** * What a provider can actually do. * * `check` and `parse-only` are deliberately distinct: a tool that accepts * `part w : NoSuchType;` is a syntax gate, not a validator, and it has to say * so rather than be registered on the strength of its README. */ export type Capability = 'check' | 'parse-only' | 'emit-ir' | 'format' | 'pack' | 'lower'; /** How a provider is reached. Callers branch on this, never on an ID. */ export type Transport = 'in-process' | 'process'; export interface ToolInvocation { command: string; args: string[]; provider: string; } /** Result of asking whether a provider can run here, without running it. */ export interface Availability { available: boolean; transport: Transport; /** Absolute path of the resolved executable, for `process` transports. */ executable?: string; /** Whatever the tool printed for `--version`, trimmed. */ version?: string; /** Why it is unavailable, when it is. */ detail?: string; } /** Everything a provider needs to build and run one invocation. */ export interface ProviderContext { config: MEMOConfig; projectDir: string; /** Resolved library roots offered to the tool as include paths. */ includeDirs?: string[]; /** * The exact sources to analyse, instead of the directory's own discovery. * * Not an optimisation. A provider discovers a *project*, and a conformance * corpus is not one: its Kernel libraries are `.kerml`, which no project * walker collects, and a run that quietly analysed none of them would * report a clean pass on files it never opened. Every adapter must honour * it or say what it did instead — silently widening the set back to the * directory is the failure this field exists to prevent. */ files?: readonly string[]; /** Destination artifact, for the `package` role. */ outputPath?: string; /** * Ontology registries the caller has already loaded. * * An offer, not an input: an in-process provider may use them instead of * loading the same closure a second time, and a process provider ignores * them and loads its own. Either way the value is the project's, so which * one ran is not observable in the result. */ registries?: BuilderRegistries; } export interface ProviderRunResult { provider: string; providerVersion?: string; transport: Transport; /** * True when the provider itself accepted the input. * * For an `in-process` provider this says only "the external step raised * nothing" — the caller still does the in-process work and reports on it. */ accepted: boolean; diagnostics: Diagnostic[]; /** Process exit status, when there was a process. */ exitCode?: number; } /** * What the lowering role returns beyond diagnostics: the IR. This is the one * role whose output is not just complaints. * * It used to be Langium documents, which is a shape only an in-process provider * can return — an AST does not cross a pipe. Making it IR is what turned the * lowering contract into something a separate process can honour, and therefore * into something a third party could implement. */ export interface LoweringRunResult extends ProviderRunResult { ir: MemoIr; } /** * A provider instance, created by its descriptor's factory. * * One verb — `run` — in every role. `R` differs only because lowering has to * hand back documents; validator and packaging report diagnostics and nothing * else. */ export interface Provider { readonly id: string; readonly role: ProviderRole; readonly transport: Transport; /** The command line this provider would run, or undefined when in-process. */ invocation(): ToolInvocation | undefined; run(): Promise; } export type ValidatorProvider = Provider; export type LoweringProvider = Provider; export type PackageProvider = Provider; /** * One configurable leaf under `toolchain.*`. * * Adapters contribute their own leaves, so `--toolchain.syside.executable` * exists because the syside adapter says so, not because the CLI knows about * syside. The CLI and `memo config effective` both read this. */ export interface ToolchainSchemaLeaf { /** Dotted path under `toolchain`, e.g. `validator` or `syside.executable`. */ path: string; type: 'string' | 'boolean' | 'enum'; /** Legal values for `enum`, or for a `string` leaf with a known roster. */ values?: readonly string[]; description: string; /** Provider that owns this leaf; absent for role-selection leaves. */ provider?: string; /** Present on leaves kept only for backwards compatibility. */ deprecated?: string; } export interface ProviderDescriptor { /** Plain string. Never widened into a union type anywhere. */ id: string; role: ProviderRole; capabilities: readonly Capability[]; /** * Selected for this role when the project configures nothing. * * At most one descriptor per role may claim it. This is how `internal` * stays the default without core code naming it. */ isDefault?: boolean; /** Config leaves this provider understands, relative to `toolchain`. */ settingsSchema?: readonly ToolchainSchemaLeaf[]; probe(context: ProviderContext): Availability; create(context: ProviderContext): Provider; } export type ValidatorDescriptor = ProviderDescriptor; export type LoweringDescriptor = ProviderDescriptor; export type PackageDescriptor = ProviderDescriptor; export declare class MissingToolError extends Error { readonly provider: string; readonly command: string; constructor(provider: string, command: string, detail: string); } export declare class UnknownProviderError extends Error { constructor(role: ProviderRole, id: string, registered: readonly string[]); } export declare class ProviderRegistry { private readonly byRole; register(descriptor: ProviderDescriptor): this; descriptors(role: ProviderRole): ProviderDescriptor[]; /** Every descriptor, in registration order, across all roles. */ all(): ProviderDescriptor[]; ids(role: ProviderRole): string[]; /** Distinct provider IDs across every role. */ allIds(): string[]; /** * The ID used when the project selects nothing for this role. * * Comes from whichever adapter declared `isDefault`, so core code never * writes a provider name. */ defaultId(role: ProviderRole): string | undefined; descriptor(role: ProviderRole, id: string): ProviderDescriptor; has(role: ProviderRole, id: string): boolean; resolveValidator(id: string, context: ProviderContext): ValidatorProvider; resolveLowering(id: string, context: ProviderContext): LoweringProvider; resolvePackager(id: string, context: ProviderContext): PackageProvider; /** Every configurable leaf, role selection first, then per-provider settings. */ schema(): ToolchainSchemaLeaf[]; } /** Settings key naming each role. Roles are MEMO's own vocabulary, not a tool's. */ export declare const ROLE_SETTING: Record; /** Kept readable by every command that reports the deprecation. */ export declare const DEPRECATED_COMPILER_SETTING = "compiler"; //# sourceMappingURL=registry.d.ts.map