import { type AstDocument, type Range } from "@telorun/analyzer"; import { type IncompatibilityReason, type VersionCompatibilityCheck } from "./version-compatibility.js"; /** One version of a module, as the hub reports it. * * `integrity` is the import pin for exactly this version (`sha256-` * over whatever the owning transport verifies its own reads against). It is * absent for a version the hub tracked before it recorded pins, and for a ref * no transport can hash — never an error, just no pin to write. */ export interface ModuleVersion { version: string; integrity?: string; } /** Version enumeration for one version-independent base ref, newest first. * * Narrower than the full `IdeEnvironmentAdapter` on purpose. It is the only * environment capability an upgrade check needs, and a host that caches or * throttles hub traffic wraps just this — a CodeLens re-resolves far more * often than a completion popup opens, so the refresh cadence is the host's * policy, not this module's. * * Deliberately NOT `adapter.listVersionsForRef`, which answers `string[]`: * completion offers names, an upgrade writes a pin, so the two want different * things from one route. A host backed by the hub fetches * `GET /module/versions` and passes the body through * {@link parseModuleVersions}. */ export type ModuleVersionLookup = (baseRef: string) => Promise; /** Everything an upgrade needs from its host: what versions exist, and whether * this runtime can host one. * * `isCompatible` is required rather than optional so no host can quietly skip * the check and walk an author onto a version their telo cannot load — the gap * this package existed with. A host that genuinely cannot fetch candidate * manifests passes {@link uncheckedVersionCompatibility}, which says so * instead of pretending. */ export interface ImportUpgradeEnvironment { listVersions: ModuleVersionLookup; isCompatible: VersionCompatibilityCheck; /** Offer prereleases as automatic upgrade targets. Off by default, matching * `telo upgrade`: an upgrade nobody asked for must not walk a caller onto an * `-rc` build. */ includePrerelease?: boolean; } /** A source edit a host applies verbatim to upgrade an import. Ranges never * overlap, within an upgrade or across a batch, so a host may apply the whole * set in one pass without ordering them. */ export interface ImportUpgradeEdit { range: Range; newText: string; } /** One import that has a newer version available. */ export interface ImportUpgrade { alias: string; /** The source as written, with any object-form `integrity:` folded in. */ source: string; currentVersion: string; latestVersion: string; /** What the import resolves to after the edits, in the same folded form as * `source` — re-pointed at `latestVersion`, carrying the new pin as a * `#sha256-…` fragment when one was available. Folded rather than literal * because where the pin physically lands depends on the shape the author * wrote (fragment vs `integrity:` sibling), and a host showing this as a * preview wants the resolved import, not one of two spellings of it. */ newSource: string; /** True when the replaced import carried a pin. */ wasPinned: boolean; /** True when the edits leave the import pinned to `latestVersion`. False * means no pin was available for the target version — a host should say so * when `wasPinned`, since the rewrite silently drops a hash the author had. */ repinned: boolean; /** A newer version that exists but this runtime cannot host, when * `latestVersion` is not the newest published. Surfacing it is not optional: * an upgrade that quietly stops short of the newest version, with no reason * given, reads as a bug in the tooling. */ heldBack?: { version: string; reason: IncompatibilityReason; }; /** Span of the alias key — where a per-entry affordance anchors. */ keyRange: Range; /** Apply all of these to upgrade this one import. */ edits: ImportUpgradeEdit[]; } /** One import that is already at the newest version but carries no integrity * pin, and for which a pin is available. Mirrors what `telo upgrade` does with * `ensurePinned`: a rarely-released module whose version never moves would * otherwise stay unpinned forever, because nothing ever offers to rewrite it. */ export interface ImportPin { alias: string; source: string; version: string; /** The source that replaces it: unchanged but for the appended pin. */ newSource: string; /** Span of the alias key — where a per-entry affordance anchors. */ keyRange: Range; edits: ImportUpgradeEdit[]; } /** An import that IS behind but that this module declines to rewrite. Carries * the same anchor and versions an {@link ImportUpgrade} does, so a host can * render it in place of the upgrade affordance rather than leaving the author * wondering why a stale import shows nothing at all. */ export interface ImportUpgradeSkip { alias: string; currentVersion: string; latestVersion: string; /** Span of the alias key — where a per-entry affordance anchors. */ keyRange: Range; /** Which decision this was, for a host that styles or filters them. * `incompatible` — every newer version declares a telo this runtime is not; * `stale-inline-pin` — the rewrite could not carry or drop the author's pin. */ code: "incompatible" | "stale-inline-pin"; /** Which rejection produced an `incompatible` skip, so a host can phrase its * own affordance without re-deriving one. Carried BESIDE `code` because the * code says what was not done while this says why, and the remedies differ: * updating telo cannot fix a requirement the module failed to state. Absent * for `stale-inline-pin`, which is not a compatibility decision at all. */ reason?: IncompatibilityReason; /** Author-facing sentence: what was not done, and what to do instead. */ message: string; } export interface ImportUpgradeSet { /** Span of the `imports:` key — where a summary affordance anchors. */ importsKeyRange: Range; upgrades: ImportUpgrade[]; pins: ImportPin[]; skipped: ImportUpgradeSkip[]; /** Base refs whose version lookup failed. Never thrown: one unreachable ref * must not blank the affordances for every other import in the file. The * host decides whether to log or surface these. */ failures: Array<{ baseRef: string; message: string; }>; } /** * Find every `imports:` entry of a module document that names a version older * than the newest one `listVersions` reports, and produce the source edits that * re-point it — plus every entry already at the newest version that carries no * integrity pin, and the edits that pin it. * * The target is the newest version this runtime can HOST, which is not always * the newest one published: each candidate's declared `requires.telo` is checked * newest-first, and a newer version that was held back is reported on the * upgrade rather than dropped. An import whose every newer version needs a newer * telo produces a `skipped` entry instead, since the alternative — offering it * anyway — is a manifest that fails at the load gate. * * Skips what carries no upgradeable version: local path imports, bare URLs, * untagged refs, and pins that are not SemVer (an OCI digest, a moving tag like * `latest`) — `parseVersionedRef` and `isNewerModuleVersion` both decline to * guess, so those simply produce no upgrade. * * Pure apart from `listVersions`: no filesystem, no direct network, no host * API. Returns `undefined` when the file declares no module document or the * module declares no `imports:`. */ export declare function buildImportUpgrades(text: string, env: ImportUpgradeEnvironment, docs?: AstDocument[]): Promise; /** How a held-back version reads in a sentence, with the version itself as the * implied subject. Names the cause the check actually established. * * The parameter is REQUIRED, so a caller cannot reach a sentence for a cause * nothing established — defaulting an absent reason to "requires a newer telo" * is precisely the invented verdict the four-way split exists to prevent. */ export declare function describeReason(reason: IncompatibilityReason): string; /** What the author can do about it. Split from {@link describeReason} because * the two rejections have DIFFERENT remedies: an unreadable requirement is not * a version skew, so telling the author to update telo would send them after a * fix that cannot work — only the module's own author can repair it. */ export declare function describeRemedy(reason: IncompatibilityReason): string; //# sourceMappingURL=build-import-upgrades.d.ts.map