ai-chatbot/lib/video-canvas/save-queue.ts

61 lines
1.9 KiB
TypeScript

export type VideoCanvasSaveStatus = "saved" | "pending" | "saving" | "error";
/** Keep edits made during a request; never retry a stale revision as an overwrite. */
export class VideoCanvasSaveQueue {
private latest: string;
private saved: string;
private running: Promise<void> | null = null;
private blocked = false;
private status: VideoCanvasSaveStatus = "saved";
constructor(
baseline: string,
private revision: string,
private readonly write: (snapshot: string, revision: string) => Promise<string>,
private readonly changed: (status: VideoCanvasSaveStatus, message?: string) => void,
) {
this.latest = baseline;
this.saved = baseline;
}
get failed() { return this.status === "error"; }
get dirty() { return this.latest !== this.saved || this.running !== null; }
update(snapshot: string) {
this.latest = snapshot;
if (!this.running && this.status !== "error") {
this.setStatus(this.dirty ? "pending" : "saved");
}
}
private setStatus(status: VideoCanvasSaveStatus, message?: string) {
this.status = status;
this.changed(status, message);
}
flush(): Promise<void> {
if (this.running) return this.running;
if (this.blocked || !this.dirty) return Promise.resolve();
this.running = this.drain().finally(() => { this.running = null; });
return this.running;
}
private async drain() {
this.setStatus("saving");
try {
while (this.latest !== this.saved) {
const snapshot = this.latest;
this.revision = await this.write(snapshot, this.revision);
this.saved = snapshot;
}
this.setStatus("saved");
} catch (error) {
this.blocked = error instanceof CanvasRevisionConflict;
this.setStatus("error", error instanceof Error ? error.message : "保存失败,请重试");
}
}
}
export class CanvasRevisionConflict extends Error {}