fix(module): derive media list items from keys instead of N+1 fetches - #527
Merged
Conversation
host.media.list() called storage.getItem() per key on top of getKeys(), firing one HTTP request per file through the dev-mode HTTP storage driver. On a public/ folder with many files this floods the browser's per-origin connection limit and never resolves, which blocks isReady (and everything gated behind it, e.g. the floating "Edit this page" button) forever. All fields needed for listing (id, extension, stem, path, fsPath) are derivable from the storage key alone, so list() no longer needs a per-item round trip. Full metadata is still fetched lazily via host.media.get() when a specific file is opened.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
…ix mismatch
list()'s key-parsing assumed every storage key was raw and unprefixed, but
production's pre-baked publicAssetsStorage (templates.ts) stores items under
keys already prefixed with the virtual media collection name, so list() was
double-prefixing id/fsPath/path for every asset in a standard prod deploy.
Extracts mediaItemFieldsFromKey() into utils/media.ts as the single place
that derives id/extension/stem/path/fsPath from a raw key, used by host.ts,
templates.ts, and the dev public route. Also fixes the pre-existing stem
no-op (split('.').join('.') simply reconstructs the input) in all three.
- mediaItemFieldsFromKey: compute stem from fsPath instead of the colon-joined key, matching the existing pattern in medias/[...path].ts, so nested paths don't leak colons into stem - host.ts: hoist the collection-prefix RegExp out of the per-key map() loop - add unit tests for mediaItemFieldsFromKey
larbish
approved these changes
Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
host.media.list()(src/module/src/runtime/host.ts) calledstorage.getItem()once per key on top ofstorage.getKeys(), firing one HTTP request per file wheneverpublicAssetsStorageis backed by an HTTP driver (dev mode,/__nuxt_studio/dev/public/**).On a
public/folder with many files, this floods the browser's per-origin connection limit — many requests get stuck inpendingandPromise.allnever resolves.Since
host.media.list()is awaited insidedraftMedias.load()(via thestudio:draft:media:updatedhook →handleDraftUpdate()), andisReadyinuseStudio.tsis only set after bothdraftDocuments.load()anddraftMedias.load()resolve, a stuck media listing blocksisReadyforever — with no console error, since nothing actually fails, it just never completes.This silently broke the whole "auto-open on mount" flow in
app.vue(and by extension the floating "Edit this page" button, gated onisReady), since #504 made that flow explicitly wait onisReadyinstead of firing on a bare timeout.Fix
All fields needed to populate the media tree/browser (
id,extension,stem,path,fsPath) are derivable directly from the storage key — no need to fetch each item's content/metadata just to list it.list()now does a singlegetKeys()call and computes these fields client-side, matching the same derivation already used in the prod build-time template (templates.ts:getAssetsDefaultStorageTemplate) and the dev server route (server/routes/dev/public/[...path].ts).Verified
generateMediaIdFromFsPath()produces identicalidstrings to those two existing code paths, so this doesn't introduce anyidmismatch that could affect draft status detection (getStatus()comparesoriginal.id !== modified.id).Full per-file metadata is still fetched lazily via
host.media.get(fsPath)when a specific file is actually opened — only the eager, unbounded fan-out at listing time is removed.