Bugs found reading the pack end to end. V3 mode registered five of the seven nodes: Save Image (Civitai) and Prompt + LoRAs had no schema, so a workflow using either failed to open with WARPPIPE_ENABLE_V3=1 and nothing said why. Both registration paths are now built from one NODE_DEFINITIONS table, a missing V3 schema is reported at startup, and a test asserts the two paths agree. The built wheel shipped no JavaScript at all - package-data listed only web/docs/*.md - so a registry install had a frontend directory holding documentation and nothing else. A test now reads pyproject.toml and checks every file under web/ is matched by a glob. Also fixed: - ruff format failed on two files, so CI was red on every push - the nearest-model tie-break compared whole tuples, breaking ties on the filename rather than on the order the graph listed them - SAFE_SAMPLERS and SAFE_SCHEDULERS referenced ComfyUI's own lists rather than copying them, so anything another pack appended later walked through coerce_scheduler untouched - the hash and thumbnail caches wrote straight to their final paths, leaving a truncated file behind if anything interrupted them - the Prompt + LoRAs node left a 100ms timer, a MutationObserver and its strip running after the node was removed - model names and base models from downloaded sidecars were written into the browser's rows as markup without escaping - a branch in the save node preferring "LoRAs recorded in the warp" could never run, because nothing ever wrote them there - indexing the library read every .civitai.info twice - the two views each held their own copy of the index and fetched it separately The frontend had no automated checks at all, which is how two unused arguments and an unescaped innerHTML survived. eslint and prettier now run over web/ in their own CI job, with the lockfile committed for the same reason the Python tools are pinned. Five regression tests, each checked against the unfixed code. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
52 lines
1.7 KiB
JavaScript
52 lines
1.7 KiB
JavaScript
// The model library, fetched once and shared.
|
|
//
|
|
// Both views want it: the prompt colours its tags against it and completes from
|
|
// it, and the browser lists it. They used to hold a cache each and fetch
|
|
// /warppipe/loras separately, which is the expensive call on the server - it
|
|
// reads a sidecar for every file in the folder. One cache means one fetch, and
|
|
// it also means the two can never be describing different libraries.
|
|
|
|
let pending = null;
|
|
let cached = null;
|
|
|
|
// Throws on failure rather than returning nothing: the browser wants to say why
|
|
// it is empty, and the prompt - which does not care - catches at its own call.
|
|
async function ask(url, key) {
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error(`${url} answered ${response.status}`);
|
|
return (await response.json())[key] || [];
|
|
}
|
|
|
|
/** Every LoRA. */
|
|
export async function loras() {
|
|
return (await everything()).filter((entry) => entry.kind !== "embeddings");
|
|
}
|
|
|
|
/** Every LoRA and embedding, in one list. Each carries its own `kind`. */
|
|
export function everything() {
|
|
if (cached) return Promise.resolve(cached);
|
|
if (pending) return pending;
|
|
|
|
pending = Promise.all([
|
|
ask("/warppipe/loras", "loras"),
|
|
// An installation may have no embeddings folder configured, and that is not
|
|
// a reason for the LoRAs to be unavailable too.
|
|
ask("/warppipe/embeddings", "embeddings").catch(() => []),
|
|
])
|
|
.then(([lora, embedding]) => {
|
|
cached = [...lora, ...embedding];
|
|
return cached;
|
|
})
|
|
.finally(() => {
|
|
pending = null;
|
|
});
|
|
|
|
return pending;
|
|
}
|
|
|
|
/** Forget it, so a model added since is picked up on the next ask. */
|
|
export function forget() {
|
|
cached = null;
|
|
pending = null;
|
|
}
|