perf(studio): stream nitro repair progress over SSE and allow cancelling
This commit is contained in:
1 parent
ba81d16f00
commit
9d571e0c29
4 files changed
+249
-26
No files matched your search
@@ -584,15 +584,28 @@ async function copyFileToDirs(
|
||||
return errors;
|
||||
}
|
||||
|
||||
export interface NitroRepairProgress {
|
||||
completed: number;
|
||||
total: number;
|
||||
ok: number;
|
||||
failed: number;
|
||||
fileName?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-download broken .nitro bundles from the configured nitro sources. The
|
||||
* file stem matched an item in `items_base`, so the same classname candidates
|
||||
* used by repair-nitros.ts are tried; every downloaded bundle is validated
|
||||
* before it replaces the broken copies across all nitro directories.
|
||||
* before it replaces the broken copies across all nitro directories. Bundles
|
||||
* are repaired in parallel (pool of 6) and `onProgress` is called after each
|
||||
* one settles, so a huge batch can be streamed to the client and cancelled.
|
||||
*/
|
||||
export async function repairBrokenNitros(
|
||||
fileNames: string[],
|
||||
options: { onProgress?: (progress: NitroRepairProgress) => void } = {},
|
||||
): Promise<NitroRepairResult> {
|
||||
const { onProgress } = options;
|
||||
const targets = await getFurniAssetWriteTargets();
|
||||
const nitroDirs = dirsForKind(targets, "nitro");
|
||||
let sources = (await listSources().catch(() => [] as never)).filter(
|
||||
@@ -604,14 +617,23 @@ export async function repairBrokenNitros(
|
||||
let failed = 0;
|
||||
const errors: string[] = [];
|
||||
const files: NitroRepairResult["files"] = [];
|
||||
const uniqueFileNames = [...new Set(fileNames)];
|
||||
const total = uniqueFileNames.length;
|
||||
let completed = 0;
|
||||
const finish = (fileName: string, ok: boolean, error?: string) => {
|
||||
completed++;
|
||||
if (ok) repaired++;
|
||||
else failed++;
|
||||
onProgress?.({ completed, total, ok: repaired, failed, fileName, error });
|
||||
};
|
||||
|
||||
await runPool([...new Set(fileNames)], 6, async (rawName) => {
|
||||
await runPool(uniqueFileNames, 6, async (rawName) => {
|
||||
const fileName = String(rawName ?? "");
|
||||
if (!NITRO_FILE_RE.test(fileName) || !fileName.endsWith(".nitro")) {
|
||||
failed++;
|
||||
const message = `Invalid nitro file name: ${fileName}`;
|
||||
errors.push(message);
|
||||
files.push({ fileName, ok: false, error: message });
|
||||
finish(fileName, false, message);
|
||||
return;
|
||||
}
|
||||
const name = fileName.slice(0, -".nitro".length);
|
||||
@@ -648,16 +670,16 @@ export async function repairBrokenNitros(
|
||||
nitroDirs,
|
||||
);
|
||||
if (writeErrors.length === 0) {
|
||||
repaired++;
|
||||
files.push({ fileName, ok: true });
|
||||
finish(fileName, true);
|
||||
} else {
|
||||
failed++;
|
||||
errors.push(...writeErrors);
|
||||
files.push({
|
||||
fileName,
|
||||
ok: false,
|
||||
error: writeErrors[0],
|
||||
});
|
||||
finish(fileName, false, writeErrors[0]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -670,13 +692,14 @@ export async function repairBrokenNitros(
|
||||
}
|
||||
}
|
||||
}
|
||||
failed++;
|
||||
const message = "could not re-obtain bundle from any nitro source";
|
||||
errors.push(`could not re-obtain ${fileName} from any nitro source`);
|
||||
files.push({
|
||||
fileName,
|
||||
ok: false,
|
||||
error: "could not re-obtain bundle from any nitro source",
|
||||
error: message,
|
||||
});
|
||||
finish(fileName, false, message);
|
||||
});
|
||||
|
||||
return { repaired, failed, errors, files };
|
||||
|
||||
Reference in new issue
Block a user