Files
gregory-richard-ComfyUI-War…/web/library.js
T
Gregory RichardandClaude Opus 5 56cc87fd92 fix: correctness and cleanup from the project review
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>
2026-09-01 21:46:47 +02:00

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;
}