/** * Where a workspace's sources come from and where they go back to * (ADR 0100). * * Core is a pure function from sources to sources and never holds one of * these: a caller lists, reads, calls the pure function, and writes what it * returns. That is what lets the same engine serve a filesystem, an object * store and a database row without knowing which it is. */ export interface SourceStore { /** * Every path this store holds, for a manifest's patterns to match against. * Relative to the store's root and separated by `/` whatever the platform * writes, because a manifest's patterns are written that way. */ list(): readonly string[]; /** The bytes, and an opaque statement of which bytes they are. */ read(path: string): StoredSource | undefined; /** * All of them or none, each only if it still holds what was read. A write * with a `null` source removes the document instead of replacing it * (ADR 0103), under the same condition and in the same batch. */ writeAll(writes: readonly PendingWrite[]): WriteOutcome; } export interface StoredSource { readonly source: string; /** * Opaque outside the store that minted it. A filesystem store may use a * content hash, S3 an ETag, git a blob sha, D1 a rowversion. Nothing parses * one, orders two, or asks what it means; the only operation is equality, * performed by the store that issued it. */ readonly revision: string; } export interface PendingWrite { readonly path: string; /** * The bytes to leave behind, or `null` to remove the document (ADR 0103). * * Removal is a write like any other: it lands in the same all-or-none batch, * under the same compare-and-swap, so a view deleted beside a subject edit * either takes both or neither. A store with nothing to remove things from * is a store that cannot express a workspace shrinking, and the alternative - * a second call outside the batch - is exactly the unconditional write the * `expected` field exists to refuse. */ readonly source: string | null; /** * The revision this edit was made against, or `null` to require that the * document does not exist yet. There is no way to write unconditionally: * a caller with nothing to state is a caller that cannot detect a conflict. * * A removal must name a revision. `null` here would ask to remove something * on condition it is not there, which is not a thing to want. */ readonly expected: string | null; } export type WriteConflict = /** Someone else wrote it after this edit was made. */ { readonly path: string; readonly reason: 'changed'; } /** * Expected to be new, but something is already there - or a removal that * named no revision, which asks to remove a document on condition it does * not exist. Both are a caller stating something the store cannot satisfy. */ | { readonly path: string; readonly reason: 'exists'; } /** Expected to be there, and is not. */ | { readonly path: string; readonly reason: 'missing'; }; export type WriteOutcome = { readonly ok: true; readonly revisions: ReadonlyMap; } | { readonly ok: false; readonly conflicts: readonly WriteConflict[]; }; /** * A store over a directory, which is the manifest's own directory: a * workspace's patterns are confined beneath it already (`YM701`), so listing * everything under it enumerates the workspace and nothing else. * * Confinement is enforced here rather than in Core. `realpath` is a filesystem * concept, and a store with no symlinks must not be asked to pretend it has * them. */ export declare const createFileSystemStore: (root: string) => SourceStore;