mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 17:27:09 +00:00
fix(cli): tolerate removed files during desktop catch-up (#5350)
This commit is contained in:
@@ -23,6 +23,22 @@ import {
|
||||
} from "./appHistory.js";
|
||||
import { readRecord, writeRecord } from "./projectRecords.js";
|
||||
|
||||
const { afterDirectoryRead } = vi.hoisted(() => ({
|
||||
afterDirectoryRead: vi.fn<(path: unknown) => void>(),
|
||||
}));
|
||||
|
||||
vi.mock("node:fs", async (importOriginal) => {
|
||||
const original = await importOriginal<typeof import("node:fs")>();
|
||||
return {
|
||||
...original,
|
||||
readdirSync: vi.fn(original.readdirSync).mockImplementation((path, options) => {
|
||||
const entries = original.readdirSync(path, options);
|
||||
afterDirectoryRead(path);
|
||||
return entries;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const history = (dir: string) => join(dir, ".hyperframes", APP_HISTORY);
|
||||
|
||||
function project(lines: string[] = []): string {
|
||||
@@ -128,6 +144,33 @@ describe("app history", () => {
|
||||
expect(filesChangedSince(dir, 5000)).toEqual(["compositions/a.html"]);
|
||||
});
|
||||
|
||||
it("keeps listing changed files when one disappears after the directory read", () => {
|
||||
const dir = project();
|
||||
const removed = join(dir, "removed.html");
|
||||
writeFileSync(removed, "removed");
|
||||
writeFileSync(join(dir, "index.html"), "kept");
|
||||
mkdirSync(join(dir, "compositions"));
|
||||
writeFileSync(join(dir, "compositions", "scene.html"), "kept");
|
||||
afterDirectoryRead.mockImplementationOnce(() => {
|
||||
rmSync(removed);
|
||||
});
|
||||
|
||||
expect(filesChangedSince(dir, 0)).toEqual(["compositions/scene.html", "index.html"]);
|
||||
});
|
||||
|
||||
it("skips a file replaced by a directory after the directory read", () => {
|
||||
const dir = project();
|
||||
const replaced = join(dir, "replaced.html");
|
||||
writeFileSync(replaced, "replaced");
|
||||
writeFileSync(join(dir, "index.html"), "kept");
|
||||
afterDirectoryRead.mockImplementationOnce(() => {
|
||||
rmSync(replaced);
|
||||
mkdirSync(replaced);
|
||||
});
|
||||
|
||||
expect(filesChangedSince(dir, 0)).toEqual(["index.html"]);
|
||||
});
|
||||
|
||||
it("names unseen turns at the end of a command run in the project or on it", () => {
|
||||
const dir = project([turn("2026-01-01T10:00:00Z", "a"), turn("2026-01-01T11:00:00Z", "b")]);
|
||||
expect(appHistoryNotice(dir, []), "never handed over from here").toBeNull();
|
||||
|
||||
@@ -96,6 +96,15 @@ export function unseenTurns(dir: string, since: number, now = Date.now()): AppTu
|
||||
);
|
||||
}
|
||||
|
||||
function isChangedFile(path: string, since: number): boolean {
|
||||
try {
|
||||
const stats = lstatSync(path);
|
||||
return stats.isFile() && stats.mtimeMs > since;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Project files changed after `since`, by the app or by hand; hidden folders and outputs are not the video. */
|
||||
export function filesChangedSince(dir: string, since: number): string[] {
|
||||
const changed: string[] = [];
|
||||
@@ -112,7 +121,7 @@ export function filesChangedSince(dir: string, since: number): string[] {
|
||||
if (entry.name.startsWith(".") || SKIPPED_DIRS.has(entry.name)) continue;
|
||||
const path = join(folder, entry.name);
|
||||
if (entry.isDirectory()) walk(path);
|
||||
else if (entry.isFile() && lstatSync(path).mtimeMs > since)
|
||||
else if (entry.isFile() && isChangedFile(path, since))
|
||||
changed.push(relative(dir, path).split(sep).join("/"));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user