import { type AppMount, type FilesAdapter, type IsoDateTime } from "@vendoai/core"; import type { Db } from "./db.js"; /** Build contract §3.3 — inline in `content` up to this size; past it the row carries a `blob_ref` into the files adapter instead. */ export { WORKSPACE_INLINE_MAX_BYTES } from "@vendoai/core"; /** Build contract §3.3 — retention per path, same as app history. */ export declare const WORKSPACE_HISTORY_LIMIT = 50; /** Build contract §9.7 — the mount type and the one derivation of an app's root path both live in core now: `@vendoai/apps` projects an app into a workspace and has to compute the same address this file moves rows between. */ export type { AppMount } from "@vendoai/core"; /** One file's metadata. Content is fetched separately (it may live in a blob). */ export interface WorkspaceFileMeta { path: string; owner: string; bytes: number; revision: number; updatedAt: IsoDateTime; } /** One superseded revision: which revision it was, and why it was replaced. The content itself stays behind the table — nothing reads it, and fetching it here would mean one blob read per entry. */ export interface WorkspaceHistoryEntry { revision: number; intent?: string; at: IsoDateTime; } interface StoredContent { content: string | undefined; blobRef: string | undefined; } interface Current { revision: number; bytes: number; updatedAt: IsoDateTime; stored: StoredContent; } /** * A write whose content is already placed (inline decided, blob uploaded) but * whose rows have not been touched yet — the unit `commit()` preflights with. * Everything that can deterministically fail (an over-cap file, an adapter * refusal) fails while producing one of these, so a commit either lands every * file or writes no row at all. */ export interface PreparedWrite { path: string; /** The content itself, kept so a lost CAS can re-aim at the new head without re-placing the blob (the bytes have not changed — only the base has). */ content: Uint8Array; bytes: number; revision: number; stored: StoredContent; prior: Current | undefined; } /** One path's mutation inside a batched commit: a prepared write to land, or a * tombstone. `expectedRevision` is the revision the turn checked out (`null` = * "did not exist then"), and `strict` is the mount's policy — the same pair * `land` takes, per entry. */ export type WorkspaceCommitEntry = { path: string; write: PreparedWrite; strict: boolean; expectedRevision: number | null; } | { path: string; delete: true; }; /** What one batched commit did: the paths that lost their compare-and-swap, the * rows that now hold new content (`changed` false when the head already held * exactly these bytes), and the tombstones that actually removed a row. */ export interface CommitAllResult { conflicts: string[]; landed: Array<{ path: string; revision: number; updatedAt: IsoDateTime; changed: boolean; }>; removed: string[]; } /** Row-level access to the workspace pair (build contract §3.3). Content lands * inline or in the files adapter; every overwrite appends the superseded * revision to history. */ export interface WorkspaceRows { /** The path index the façade builds at turn start (§3.2). */ index(owners: string[]): Promise; read(owner: string, path: string): Promise; /** Place the content and reserve a revision, touching no row. `unchanged` means the bytes are already stored: no revision bump, nothing to sync. */ prepare(owner: string, path: string, bytes: Uint8Array): Promise; /** Land a prepared write atomically: one statement compare-and-swaps the file row against the revision `prepare` read AND records the superseded revision, so two overlapping commits can never both claim the same revision number. A lost race re-aims at the new head and retries here — /user stays last-write-wins for the final content, and the loser's edit still lands as a history row. `landed: false` means the head already holds these exact bytes, so this commit wrote nothing. */ land(owner: string, prepared: PreparedWrite, intent?: string, /** Build contract §9.7 — `/orgs` commits are STRICT compare-and-swap: one attempt, no re-aim. A lost swap comes back `conflict: true` (and its blob is released) so the façade can answer the frozen conflict branch instead of overwriting a colleague's edit. */ options?: { strict?: boolean; /** The revision this turn CHECKED OUT (contract §3.5) — null when the file did not exist then. Strict mounts swap against this, not against whatever the head happens to be at commit time, or a commit would quietly overwrite an edit that landed mid-turn. */ expectedRevision?: number | null; }): Promise<{ landed: boolean; revision: number; updatedAt: IsoDateTime; conflict?: true; }>; /** Land a whole commit's worth of entries — the turn's removals and its prepared writes, in one pass. The SQL backend runs the same per-path statements `land`/`remove` do; a backend over the 42-op wire sends ONE `workspace.commit`, which is what takes a turn's commit from a round trip per file to a round trip. */ commitAll(owner: string, entries: ReadonlyArray, intent?: string): Promise; /** Drop a prepared write that will never land, so its blob is not orphaned. */ discard(prepared: PreparedWrite): Promise; /** Deleting records what it removed, because history is append-only (§3.3). Returns false if there was nothing there. */ remove(owner: string, path: string, intent?: string): Promise; /** Build contract §9.5 — promote's workspace half: move one app's documents between mounts (`/user/apps//**` owned by a person and `/orgs//apps//**` owned by the org). Owner AND path change together, because owner derivation is a pure function of the path (§9.7); history moves with the files, or the trail would point at unreachable rows. Both directions, so the umbrella's promote can put the documents back when the row flip that follows them fails. A destination that already holds these paths is refused BEFORE anything moves. Returns how many file rows moved. */ moveApp(appId: string, from: AppMount, to: AppMount): Promise; history(owner: string, path: string): Promise; } export declare function workspaceRows(db: Db, files: FilesAdapter): WorkspaceRows; //# sourceMappingURL=workspace-rows.d.ts.map