/** * The fix handlers behind `val validate --fix`, and the editor quick fixes that * must agree with it. * * ## Why this lives in `@valbuild/server` * * A fix is two layers. `createFixPatch` is the second: given a validation * error it produces a patch. The first is everything that has to happen before a * patch can be produced — read the file, check it is actually on disk, extract * metadata, pick a remote bucket, upload the bytes, download them back. That * layer used to live in `packages/cli/src/runValidation.ts`, which made it * reachable only from the CLI: `@valbuild/cli` exports nothing but `./cli`, and * `@valbuild/language-server` cannot depend on it anyway without creating a * cycle (the CLI depends on the language server). * * The consequence was that an editor could offer the fixes needing no * precondition and had to reimplement or skip the rest. Both happened: the * VS Code extension grew its own remote-upload client, and the language server * declined to offer remote fixes at all. * * So the layer sits here, next to `createFixPatch`, and the CLI and the language * server are both callers. {@link FixHandlerContext} is deliberately made of * things any caller already has — a `Service`, an `IValFSHost`, a project root — * rather than of CLI concepts. * * What stays in the CLI is the driver: the `runValidation` async generator that * walks every module, decides what to report, and renders it to a terminal. */ import { ModuleFilePath, SourcePath, ValidationFix } from "@valbuild/core"; import type { Service } from "./Service.js"; import type { IValFSHost } from "./ValFSHost.js"; export type { IValFSHost }; export type IValRemote = { remoteHost: string; getSettings(projectName: string, options: { pat: string; }): Promise<{ success: true; data: { publicProjectId: string; remoteFileBuckets: { bucket: string; }[]; }; } | { success: false; message: string; }>; uploadFile(project: string, bucket: string, fileHash: string, fileExt: string | undefined, fileBuffer: Buffer, options: { pat: string; }): Promise<{ success: true; } | { success: false; error: string; }>; }; export type ValModule = Awaited>; export type ValidationError = { message: string; value?: unknown; fixes?: ValidationFix[]; keyError?: boolean; }; export type FixHandlerContext = { sourcePath: SourcePath; validationError: ValidationError; valModule: ValModule; projectRoot: string; fix: boolean; service: Service; valFiles: string[]; moduleFilePath: ModuleFilePath; file: string; fs: IValFSHost; remoteFiles: Record; }>; publicProjectId?: string; remoteFileBuckets?: string[]; remoteFilesCounter: number; remote: IValRemote; project: string | undefined; }; export type FixHandlerResult = { success: boolean; errorMessage?: string; shouldApplyPatch?: boolean; appliedFix?: boolean; fixableErrorMessage?: string; publicProjectId?: string; remoteFileBuckets?: string[]; remoteFilesCounter?: number; events?: ValidationEvent[]; }; export type FixHandler = (ctx: FixHandlerContext) => Promise; export type ValidationEvent = { type: "file-valid"; file: string; durationMs: number; } | { type: "file-error-count"; file: string; errorCount: number; durationMs: number; } | { type: "validation-error"; sourcePath: string; message: string; keyError?: boolean; } | { type: "validation-fixable-error"; sourcePath: string; message: string; fixable: boolean; keyError?: boolean; } | { type: "unknown-fix"; sourcePath: string; fixes: string[]; keyError?: boolean; } /** * A file that default exports a Val module, but that val.modules does not * register — so Val will never serve it. Files that do NOT default export a * module are not reported at all, and one whose default export is not a * module is a `fatal-error`; see `reportUnregistered` in the CLI. */ | { type: "unregistered-module"; file: string; } | { type: "fix-applied"; file: string; sourcePath: string; } | { type: "fatal-error"; file: string; message: string; } | { type: "remote-uploading"; ref: string; } | { type: "remote-uploaded"; ref: string; } | { type: "remote-already-uploaded"; filePath: string; } | { type: "remote-downloading"; sourcePath: string; } | { type: "summary-errors"; count: number; } | { type: "summary-success"; }; export declare function handleFileMetadata(ctx: FixHandlerContext): Promise; export declare function handleRemoteFileUpload(ctx: FixHandlerContext): Promise; export declare function handleRemoteGalleryFileUpload(ctx: FixHandlerContext): Promise; export declare function handleRemoteFileDownload(ctx: FixHandlerContext): Promise; export declare function handleRemoteFileCheck(): Promise; export declare function handleUniqueFolderCheck(ctx: FixHandlerContext): Promise; /** * What is out of step between a gallery's entries and its directory. * * Two questions, and they treat a remote entry differently — which is the whole * reason this is separate from the handler around it: * * - **Missing**: an entry with no bytes at its local path. Asked of LOCAL * entries only. A remote entry's bytes live on the content host, and nothing * puts a copy in the working tree: `saveOrUploadFiles` uploads the remote * descriptors and copies only the local ones into the tree, so a remote entry * added through the Studio (or over MCP) has no local file by design, and * demanding one would mean committing remote bytes to git — which is what * remote storage exists to avoid. Whether those bytes really are on the host * is a different check, `image:check-remote`, which already runs for exactly * these entries. * - **Untracked**: a file in the directory that no entry claims. Asked of every * entry, remote included, and that is why they are normalised to their local * path: `val validate --fix` promotes a local file to a remote ref and leaves * the file where it was, so a remote entry can perfectly well have one. */ export declare function checkGalleryFiles(input: { entryKeys: string[]; dir: string; projectRoot: string; fs: Pick; }): { missingTrackedFiles: string[]; untrackedFiles: string[]; }; export declare function handleCheckAllFiles(ctx: FixHandlerContext): Promise; export declare function handleJsonValuesExtractEntry(ctx: FixHandlerContext): Promise; /** * `external:upload` under `val validate --fix`: refuse, and say what to run. * * Every other fix in this registry rewrites something inside the repository — * reversible, visible in a diff, wrong by at most one commit. This one would * write entries into a live external store: not in a diff, not undone by * `git revert`, and against production indistinguishable from an editor's * publish. So a blanket `--fix` must never apply it. * * `fixableErrorMessage` rather than a plain error, because the error IS fixable * — just not by this command. */ /** * A view pointer that names a module its schema does not. * * Nothing to look up and nothing to ask: the schema names the module, so the * one correct value is already known. `createFixPatch` writes it — this handler * exists to send it there, and to say what `--fix` would do when it is off. */ export declare function handleViewCheckModule(ctx: FixHandlerContext): Promise; export declare function handleExternalUpload(): Promise; export declare const currentFixHandlers: Record, FixHandler>; export declare const fixHandlers: Record; export declare function createDefaultValFSHost(): IValFSHost;