/** * Fail-atomic user-file writes for the write/edit/LSP writethrough path. * * The guarantee is visibility, not crash durability: a failed write never * publishes partial or truncated bytes, and the destination is left byte- * identical. It is deliberately NOT crash-atomic -- the parent directory is * never fsynced, so a rename can be lost across a system crash. * * Publication is last-writer-wins, not conditional. Identity is revalidated * immediately before the rename, which rejects a destination that was replaced * or retargeted while staging, but `rename(2)` commits against the pathname: * a writer that publishes a successor inside the window between that check and * the rename is overwritten. Closing that window needs an OS conditional- * replace primitive; the exchange-based one available here was removed because * it validated only after committing and leaked protocol debris into user * directories on every successful write. `write` is a last-writer-wins tool by * contract, so this is the documented behavior rather than a silent race. * * `Bun.write` truncates the destination then copies bytes. A permission or IO * failure after that truncate leaves a 0-byte target even though the tool * reported an error. Stage to a sibling temp, then rename over the destination * so a failed attempt never publishes a truncated file. Directory fsync is * intentionally omitted: Windows reports `EPERM` for it (#4457) and user-file * publication does not need that durability barrier. * * Destination symlinks are followed: the referent is replaced, the link stays. * The referent is re-resolved immediately before publication so a retargeted * link cannot silently repoint the write, and when the lexical destination * sits inside a session-scoped `gjc-local` root the resolved referent and its * parent must remain inside that root (a link there must not redirect the * write out of the trust boundary). * * Staging uses exclusive create (`wx`) so a colliding leftover temp is never * truncated or unlinked; only the temp this call created is cleaned on failure. * Existing-file mode and ownership are re-applied after staging so a process * umask or replacement inode never changes the target's identity. Hard-linked * targets are rejected because replacement would split their link group, and * target identity is revalidated before the final same-directory rename. The * staged bytes are synced before publication; directory fsync is intentionally not * promised because Windows reports EPERM for it (#4457). */ export type FileWritePublicationState = "not_published" | "published" | "unknown"; export declare class FileWriteNotPublishedError extends Error { readonly dest: string; readonly destUnchanged: boolean; readonly publicationState: FileWritePublicationState; readonly cause: unknown; constructor(dest: string, cause: unknown, options?: { destUnchanged?: boolean; publicationState?: FileWritePublicationState; }); } export declare function isFileWritePermissionError(error: unknown): boolean; export declare function formatFileWriteError(error: unknown, dest: string, options?: { destUnchanged?: boolean; }): string; export interface WriteFileAtomicallyOptions { /** * Trusted root that a resolved symlink referent and its parent must not * leave. When omitted, the helper still enforces the session-scoped * `gjc-local` boundary implied by a lexical destination under * `/gjc-local/`. */ trustBoundary?: string; /** Platform override used by deterministic retry tests. */ platform?: NodeJS.Platform; /** Sleep seam used by deterministic retry tests. */ sleep?: (delayMs: number) => Promise; /** Test seam invoked after the fallback handle is opened and before mutation. */ beforeInPlaceMutation?: () => Promise; } export declare function writeFileAtomically(dest: string, content: string | Uint8Array, options?: WriteFileAtomicallyOptions): Promise;