import type { FileSystemEntry } from '../types'; import type { VaultConfig } from '../../vault/types'; import type { VaultSyncEngine } from '../../vault/engine'; import { LocalFilesystemProvider, type LocalFilesystemProviderOptions, } from './localFilesystemProvider'; export type SyncedVaultFilesystemProviderOptions = LocalFilesystemProviderOptions & { vault: VaultConfig; engine: VaultSyncEngine; }; /** * Filesystem provider for a synced vault. * * The sidebar tree and CRUD reuse the on-disk behaviour of * `LocalFilesystemProvider` (files live on disk after the first-mount clone); * each mutation then drives the push-only `VaultSyncEngine` to reconcile the * remote Habitat records. File *content* edits are not handled here — they * flow through the live Automerge session in `SyncedVaultDocumentsProvider`. */ export class SyncedVaultFilesystemProvider extends LocalFilesystemProvider { private vault: VaultConfig; private engine: VaultSyncEngine; constructor(options: SyncedVaultFilesystemProviderOptions) { super(options); this.vault = options.vault; this.engine = options.engine; } /** Absolute entry id → vault-relative path. */ private rel(entryId: string): string { const prefix = `${this.rootPath}/`; return entryId.startsWith(prefix) ? entryId.slice(prefix.length) : entryId; } async createFile( parentId?: string, name?: string, content?: string, ): Promise { // Write to disk first, then push — so the remote doc carries the initial // contents rather than being created empty and filled by a later sync. const entry = await super.createFile(parentId, name, content); // Creates the remote file doc + a snapshot entry with its uri. await this.engine.pushFile(this.vault, this.rootPath, this.rel(entry.id)); return entry; } // createDirectory: the remote directory doc is created lazily by the engine // when the first child file is pushed (updateParentDirectories), so the // disk-only behaviour inherited from LocalFilesystemProvider is sufficient. async renameFile(entryId: string, newName: string): Promise { const entry = await super.renameFile(entryId, newName); // Rename = delete old + add new on disk; sync()'s move detection // reconciles the remote doc in place. await this.engine.sync(this.vault, this.rootPath); return entry; } async moveFile( entryId: string, targetDirId: string, ): Promise { const entry = await super.moveFile(entryId, targetDirId); // Move = delete old + add new on disk; sync()'s move detection reconciles // the remote doc in place. await this.engine.sync(this.vault, this.rootPath); return entry; } async deleteFile(entryId: string): Promise { await super.deleteFile(entryId); await this.engine.sync(this.vault, this.rootPath); } async copyFile(entryId: string): Promise { const entry = await super.copyFile(entryId); await this.engine.pushFile(this.vault, this.rootPath, this.rel(entry.id)); return entry; } }