Cleanup pass over the previous commit. No behaviour change except the
cache header noted below.
app/api_docs.py (111 -> 59 lines):
- Cut the rationale comments. The file was ~32% commentary against 2-6%
in neighbouring app/ modules, and most of it argued decisions that
belong in the PR rather than the source, including a TODO explaining
why an SRI hash could not be computed.
- Inline the spec and bundle URLs into the HTML. The __TOKEN__ replace()
machinery existed only to hoist two string literals, and then needed a
comment defending its own existence.
- Drop the hand-rolled <style> block for two inline style attributes.
- Drop the os.path.isfile guard. FileResponse already answers a missing
file with a 404, so the check was a second stat for the same result.
- Serve the spec with Cache-Control: no-cache rather than no-store.
Both mean "never use a stale copy", but no-store also forbids storing
it, so every docs page load re-transferred all 230 KB. no-cache lets
the browser revalidate against the ETag FileResponse already sets; an
unchanged spec now costs a 304 instead of a full download.
tests-unit/server_test/test_api_docs.py (11 tests -> 6, 144 -> 70 lines):
- Drop the local copy of server.py's /api prefix loop and the two tests
that depended on it. They exercised the copy, not the real loop, so
they would have stayed green through a change to the thing they
claimed to protect. What actually makes prefixing work is that the
spec URL is relative, which is now asserted directly.
- Drop the static-catch-all test. It asserted aiohttp's own route
precedence against a synthetic app, and could not fail if the
registration call moved after the catch-all in server.py.
- Drop the tautological SPEC_PATH assertion, already covered by fetching
the spec through the route.
- Loosen the fallback assertions, which pinned the exact quoting and
inline-handler style of the HTML.
Claude-Session: https://claude.ai/code/session_01BvUveU9ofyGrSz3QxYeecB
openapi.yaml has been linted in CI but never reachable over HTTP. Add
--enable-api-docs (off by default) to serve it at /openapi.yaml and render
it at /api-docs.
The viewer is Redoc rather than Swagger UI specifically because it has no
request-execution feature. The local server is unauthenticated by default,
so a docs page with "Try it out" would give one-click access to
/api/interrupt, /api/free and DELETE /api/userdata/{file}.
Notes on the implementation:
- Routes are registered on PromptServer's route table, not on the app, so
they land before the web.static('/') catch-all that would shadow them.
This also means they are served at both /openapi.yaml and
/api/openapi.yaml; the docs page references the spec by a relative URL so
it resolves from either mount point.
- The spec path resolves from __file__, since ComfyUI is routinely launched
from other directories.
- Cache headers are set explicitly: the cache_control middleware only
special-cases js/css/images, so a .yaml response would otherwise be
served stale after an edit.
- /docs is left alone; it already serves embedded node help content.
The Redoc bundle comes from a pinned CDN URL, so the page needs outbound
network access. Offline installs still get the spec itself, and the page
degrades to a notice pointing at it.
Claude-Session: https://claude.ai/code/session_01BvUveU9ofyGrSz3QxYeecB
After a frontend update (e.g. nightly build), browsers could load
outdated cached index.html and JS/CSS chunks, causing dynamically
imported modules to fail with MIME type errors and vite:preloadError.
Hard refresh (Ctrl+Shift+R) was insufficient to fix the issue because
Cache-Control: no-cache still allows the browser to cache and
revalidate via ETags. aiohttp's FileResponse auto-generates ETags
based on file mtime+size, which may not change after pip reinstall,
so the browser gets 304 Not Modified and serves stale content.
Clearing ALL site data in DevTools did fix it, confirming the HTTP
cache was the root cause.
The fix changes:
- index.html: no-cache -> no-store, must-revalidate
- JS/CSS/JSON entry points: no-cache -> no-store
no-store instructs browsers to never cache these responses, ensuring
every page load fetches the current index.html with correct chunk
references. This is a small tradeoff (~5KB re-download per page load)
for guaranteed correctness after updates.