fix: a slow entry in the VFS was blocking the rest

Massimo Melina committed Nov 25, 2025 at 10:41 UTC 42d49a75635ecf01b79a0d1ac7e502d4976bef00
2 files changed +8 -7
src/vfs.ts
+7 -6
@@ -319,26 +319,26 @@ export async function* walkNode(parent: VfsNode, {
319 async read() {
320 if (started) return // for simplicity, we care about starting, and never suspend
321 started = true
322 - const { children, source } = parent
322 + const { source } = parent
323 const taken = prefixPath ? undefined : new Set()
324 const maskApplier = parentMaskApplier(parent)
325 const visitLater: any = []
326 - if (children) for (const child of children) {
326 + const childrenWorking = parent.children?.length && Promise.all(parent.children.map(async child => {
327 const nodeName = getNodeName(child)
328 const name = prefixPath + nodeName
329 taken?.add(normalizeFilename(name))
330 const item = { ...child, original: child, name, parent }
331 - if (await cantSee(item)) continue
331 + if (await cantSee(item)) return
332 if (item.source && !item.children?.length) // real items must be accessible, unless there's more to it
333 try { await fs.access(item.source) }
334 - catch { continue }
334 + catch { return }
335 const isFolder = nodeIsFolder(child)
336 if (onlyFiles ? !isFolder : (!onlyFolders || isFolder))
337 stream.push(item)
338 - if (!depth || !isFolder || cantRecur(item)) continue
338 + if (!depth || !isFolder || cantRecur(item)) return
339 inheritMasks(item, parent)
340 visitLater.push([item, name]) // prioritize siblings
341 - }
341 + }))
342
343 try {
344 if (!source)
@@ -382,6 +382,7 @@ export async function* walkNode(parent: VfsNode, {
382 }
383 }
384 finally {
385 + await childrenWorking
386 for (const [item, name] of visitLater)
387 for await (const x of walkNode(item, { depth: depth - 1, prefixPath: name + '/', ctx, requiredPerm, onlyFolders, parallelizeRecursion }))
388 stream.push(x)
tests/test.ts
+1 -1
@@ -118,7 +118,7 @@ describe('basics', () => {
118 test('inheritNegativeMask', reqList('/tests/page', { outList: ['index.html'] }))
119
120 const zipSize = 13242
121 - const zipOfs = 0x32EF
121 + const zipOfs = 0x194E
122 const zipLength = 4
123 test('zip.head', req('/f1/?get=zip', { empty:true, length:zipSize }, { method:'HEAD' }) )
124 test('zip.partial', req('/f1/?get=zip', { re:/^page$/, length: zipLength }, { headers: { Range: `bytes=${zipOfs}-${zipOfs+zipLength-1}` } }) )