Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107// The doc-store seam (docs/WEB-APP.md, Phase A): everything the portable// core — tile engine, registry, provider — needs from a filesystem, behind// one interface. On the desktop this is Deno's fs, unchanged. On the web// there IS no authoritative disk (the home space is the disk): the memory// implementation is the cache tier, and losing it loses nothing durable.//// Deliberately tiny: exactly the operations the core performs today, with// the semantics the core relies on (stat throws when absent; rename is the// atomic-write commit; mkdir is always recursive).
export interface ZiranFs { readFile(path: string): Promise<Uint8Array>; writeFile(path: string, bytes: Uint8Array): Promise<void>; readTextFile(path: string): Promise<string>; writeTextFile(path: string, text: string): Promise<void>; mkdir(path: string): Promise<void>; remove(path: string): Promise<void>; rename(from: string, to: string): Promise<void>; /** Throws when the path does not exist (the free-name probe relies on it). */ stat(path: string): Promise<{ mtime: number; size: number }>;}
const denoFs = (): ZiranFs => ({ readFile: (p) => Deno.readFile(p), writeFile: (p, bytes) => Deno.writeFile(p, bytes), readTextFile: (p) => Deno.readTextFile(p), writeTextFile: (p, text) => Deno.writeTextFile(p, text), mkdir: (p) => Deno.mkdir(p, { recursive: true }), remove: (p) => Deno.remove(p), rename: (from, to) => Deno.rename(from, to), stat: async (p) => { const s = await Deno.stat(p); return { mtime: s.mtime?.getTime() ?? 0, size: s.size }; },});
/** In-memory store: the web cache tier, and the proof harness that the core needs no disk. Paths are opaque keys; directories don't exist. */export class MemoryFs implements ZiranFs { #files = new Map<string, { bytes: Uint8Array; mtime: number }>();
get fileCount(): number { return this.#files.size; } paths(): string[] { return [...this.#files.keys()]; }
readFile(path: string): Promise<Uint8Array> { const f = this.#files.get(path); if (!f) return Promise.reject(new Error(`No such file: ${path}`)); return Promise.resolve(f.bytes.slice()); } writeFile(path: string, bytes: Uint8Array): Promise<void> { this.#files.set(path, { bytes: bytes.slice(), mtime: Date.now() }); return Promise.resolve(); } readTextFile(path: string): Promise<string> { return this.readFile(path).then((b) => new TextDecoder().decode(b)); } writeTextFile(path: string, text: string): Promise<void> { return this.writeFile(path, new TextEncoder().encode(text)); } mkdir(_path: string): Promise<void> { return Promise.resolve(); } remove(path: string): Promise<void> { this.#files.delete(path); return Promise.resolve(); } rename(from: string, to: string): Promise<void> { const f = this.#files.get(from); if (!f) return Promise.reject(new Error(`No such file: ${from}`)); this.#files.delete(from); this.#files.set(to, f); return Promise.resolve(); } stat(path: string): Promise<{ mtime: number; size: number }> { const f = this.#files.get(path); if (!f) return Promise.reject(new Error(`No such file: ${path}`)); return Promise.resolve({ mtime: f.mtime, size: f.bytes.length }); }}
// Default: the host's real fs when there is one (desktop, tests), memory// when there isn't (a browser) — so importing this module is browser-safe// with no ceremony.let active: ZiranFs = typeof Deno !== 'undefined' && typeof Deno.readFile === 'function' ? denoFs() : new MemoryFs();
export function setZiranFs(fs: ZiranFs): void { active = fs;}
/** The seam call sites import: stable identity, swappable backing. */export const zfs: ZiranFs = { readFile: (p) => active.readFile(p), writeFile: (p, b) => active.writeFile(p, b), readTextFile: (p) => active.readTextFile(p), writeTextFile: (p, t) => active.writeTextFile(p, t), mkdir: (p) => active.mkdir(p), remove: (p) => active.remove(p), rename: (f, t) => active.rename(f, t), stat: (p) => active.stat(p),};