import type { FilePath, UXDataWriteOptions } from "@vrtmrz/livesync-commonlib/compat/common/types.js"; import type { IVaultAdapter } from "@vrtmrz/livesync-commonlib/compat/serviceModules/adapters.js"; import type { NodeFile, NodeFolder } from "./NodeTypes.js"; import { NodeStorageAdapter } from "@vrtmrz/livesync-commonlib/node.js"; /** * Vault adapter implementation for Node.js */ export class NodeVaultAdapter implements IVaultAdapter { private readonly storage: NodeStorageAdapter; constructor(rootPathOrStorage: string | NodeStorageAdapter) { this.storage = typeof rootPathOrStorage === "string" ? new NodeStorageAdapter(rootPathOrStorage) : rootPathOrStorage; } async read(file: NodeFile): Promise { const content = await this.storage.read(file.path); // Correct stale stat.size — chokidar stats may be from a poll before the final write. // The downstream document integrity check compares stat.size to content length, so // they must agree or other clients reject the file as corrupted. file.stat.size = Buffer.byteLength(content, "utf-8"); return content; } async cachedRead(file: NodeFile): Promise { // No caching in CLI version, just read directly return await this.read(file); } async readBinary(file: NodeFile): Promise { const buffer = await this.storage.readBinary(file.path); // Same correction as read() — ensure stat.size matches actual byte length. file.stat.size = buffer.byteLength; return buffer; } async modify(file: NodeFile, data: string, options?: UXDataWriteOptions): Promise { await this.storage.write(file.path, data, options); } async modifyBinary(file: NodeFile, data: ArrayBuffer, options?: UXDataWriteOptions): Promise { await this.storage.writeBinary(file.path, data, options); } async create(p: string, data: string, options?: UXDataWriteOptions): Promise { await this.storage.write(p, data, options); return await this.toNodeFile(p); } async createBinary(p: string, data: ArrayBuffer, options?: UXDataWriteOptions): Promise { await this.storage.writeBinary(p, data, options); return await this.toNodeFile(p); } async rename(file: NodeFile, newPath: string): Promise { await this.storage.rename(file.path, newPath); file.path = newPath as FilePath; } async delete(file: NodeFile | NodeFolder, force = false): Promise { await this.storage.remove(file.path); } async trash(file: NodeFile | NodeFolder, force = false): Promise { // In CLI, trash is the same as delete (no recycle bin) await this.delete(file, force); } trigger(name: string, ...data: unknown[]): void { // No-op in CLI version (no event system) return undefined; } private async toNodeFile(path: string): Promise { const stat = await this.storage.stat(path); if (stat?.type !== "file") throw new Error(`Could not read created file metadata: ${path}`); return { path: path as FilePath, stat }; } }