mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Adds experimental session sync (`@earendil-works/pi-durable/sync`). Devices upload and pull a session's commits through a remote, reading them from the Chord store's outbox and applying them with `applyBatch`. If two devices continue the same session, the one that uploads second forks to a new session. I took out the extra sequence indexes from the earlier version of this branch, since the Chord outbox already persists every commit, so this no longer touches storage. --------- Co-authored-by: Armin Ronacher <armin.ronacher@active-4.com>
59 lines
2.3 KiB
TypeScript
59 lines
2.3 KiB
TypeScript
// `SyncFileSystem` over Node's file system, for the sync tests (a host passes its own environment, such as Pi Durable's
|
|
// `NodeExecutionEnv`).
|
|
|
|
import { appendFile, mkdir, open, readdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import type { FileResult, SyncFileInfo, SyncFileReader, SyncFileSystem } from "../../src/sync/index.ts";
|
|
|
|
/** An error with a `code`: `not_found` for a missing file or directory, as `SyncFileSystem` reports it. */
|
|
class FileError extends Error {
|
|
readonly code: string;
|
|
constructor(code: string, cause: unknown) {
|
|
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
this.code = code;
|
|
}
|
|
}
|
|
|
|
async function attempt<T>(operation: () => Promise<T>): Promise<FileResult<T>> {
|
|
try {
|
|
return { ok: true, value: await operation() };
|
|
} catch (error) {
|
|
const code = (error as NodeJS.ErrnoException).code === "ENOENT" ? "not_found" : "unknown";
|
|
return { ok: false, error: new FileError(code, error) };
|
|
}
|
|
}
|
|
|
|
export const nodeFiles: SyncFileSystem = {
|
|
joinPath: async (parts) => ({ ok: true, value: join(...parts) }),
|
|
readTextFile: (path) => attempt(() => readFile(path, "utf8")),
|
|
readBinaryFile: (path) => attempt(async () => new Uint8Array(await readFile(path))),
|
|
openBinaryReader: (path) =>
|
|
attempt(async (): Promise<SyncFileReader> => {
|
|
const file = await open(path, "r");
|
|
return {
|
|
read: (offset, length) =>
|
|
attempt(async () => {
|
|
const buffer = new Uint8Array(length);
|
|
const { bytesRead } = await file.read(buffer, 0, length, offset);
|
|
return buffer.subarray(0, bytesRead);
|
|
}),
|
|
close: async () => {
|
|
await file.close();
|
|
},
|
|
};
|
|
}),
|
|
writeFile: (path, content) => attempt(() => writeFile(path, content)),
|
|
appendFile: (path, content) => attempt(() => appendFile(path, content)),
|
|
renameFile: (source, destination) => attempt(() => rename(source, destination)),
|
|
listDir: (path) =>
|
|
attempt(async () =>
|
|
(await readdir(path, { withFileTypes: true })).map(
|
|
(entry): SyncFileInfo => ({
|
|
name: entry.name,
|
|
kind: entry.isDirectory() ? "directory" : entry.isSymbolicLink() ? "symlink" : "file",
|
|
}),
|
|
),
|
|
),
|
|
createDir: (path, options) => attempt(async () => void (await mkdir(path, options))),
|
|
remove: (path, options) => attempt(() => rm(path, options)),
|
|
};
|