import { join } from "node:path"; import { closeMirrors } from "../mirror/store.ts"; import { openKeprDb } from "../server/db.ts"; import { openBucket } from "./reader.ts"; import { localSyncer, type Syncer, type SyncReply, type SyncRequest, startSyncLoop, } from "./sync.ts"; // The sync's own thread: started by `startSyncWorker`, it runs the loop and answers its requests. declare const self: Worker; const reply = (message: SyncReply) => self.postMessage(message); const settle = (id: number, work: Promise) => work.then( () => reply({ kind: "done", id }), (error) => reply({ kind: "done", id, error: String(error) }), ); const released = new Map void>(); // Settles when the hold in force, if any, is released: a rename moves mirror files under it. let unheld = Promise.resolve(); let syncer: Syncer; self.addEventListener("message", ({ data }: MessageEvent) => { switch (data.kind) { case "start": { const target = { bucket: openBucket(data.source), kepr: openKeprDb(data.dataDir), mirrorDir: join(data.dataDir, "mirror"), }; syncer = localSyncer(target); startSyncLoop(target); return; } case "sync": return void settle(data.id, syncer.syncAll()); case "hold": // Answered once no sync runs; mirrors are closed so a rename can move their files. return void syncer.hold(() => { const held = new Promise((release) => released.set(data.id, release)); unheld = held; closeMirrors(); reply({ kind: "done", id: data.id }); return held; }); case "release": released.get(data.id)?.(); released.delete(data.id); return; case "hidden": // Not behind a sync under way, which can take minutes; only behind a rename's hold. return void settle( data.id, unheld.then(() => syncer.applyHidden(data.repos)), ); case "bucket": return syncer.setBucket(data.source); } });