@samitouri / QOSami-HFS / commits / b63d0b4c

get-list now produces ordered results

Massimo Melina committed Apr 30, 2025 at 11:32 UTC b63d0b4cce0c37feec2b751a51d87e7e26dc9975
3 files changed +13 -7
src/serveGuiAndSharedFiles.ts
+1 -1
@@ -171,7 +171,7 @@ async function sendFolderList(node: VfsNode, ctx: Koa.Context) {
171 || URL.protocol + '//' + URL.host + ctx.state.revProxyPath
172 prepend = base + pathEncode(decodeURI(ctx.path)) // redo the encoding our way, keeping unicode chars unchanged
173 }
174 - const walker = walkNode(node, { ctx, depth: depth === '*' ? Infinity : Number(depth) })
174 + const walker = walkNode(node, { ctx, depth: depth === '*' ? Infinity : Number(depth), parallelizeRecursion: false })
175 ctx.body = asyncGeneratorToReadable(filterMapGenerator(walker, async el => {
176 const isFolder = await nodeIsDirectory(el)
177 return !folders && isFolder ? undefined
src/vfs.ts
+5 -3
@@ -275,7 +275,8 @@ interface WalkNodeOptions {
275 prefixPath?: string,
276 requiredPerm?: undefined | keyof VfsPerms,
277 onlyFolders?: boolean,
278 - onlyFiles?: boolean
278 + onlyFiles?: boolean,
279 + parallelizeRecursion?: boolean,
280 }
281 // it's the responsibility of the caller to verify you have list permission on parent, as callers have different needs.
282 export async function* walkNode(parent: VfsNode, {
@@ -285,6 +286,7 @@ export async function* walkNode(parent: VfsNode, {
286 requiredPerm,
287 onlyFolders = false,
288 onlyFiles = false,
289 + parallelizeRecursion = true,
290 }: WalkNodeOptions = {}) {
291 let started = false
292 const stream = new Readable({
@@ -325,7 +327,7 @@ export async function* walkNode(parent: VfsNode, {
327 try {
328 let lastDir = prefixPath.slice(0, -1) || '.'
329 parentsCache.set(lastDir, parent)
328 - await walkDir(source, { depth, ctx, hidden: showHiddenFiles.get() }, async entry => {
330 + await walkDir(source, { depth, ctx, hidden: showHiddenFiles.get(), parallelizeRecursion }, async entry => {
331 if (ctx?.isAborted()) {
332 stream.push(null)
333 return null
@@ -368,7 +370,7 @@ export async function* walkNode(parent: VfsNode, {
370 }
371 finally {
372 for (const [item, name] of visitLater)
371 - for await (const x of walkNode(item, { ctx, depth: depth - 1, prefixPath: name + '/', requiredPerm, onlyFolders }))
373 + for await (const x of walkNode(item, { depth: depth - 1, prefixPath: name + '/', ctx, requiredPerm, onlyFolders, parallelizeRecursion }))
374 stream.push(x)
375 stream.push(null)
376 }
src/walkDir.ts
+7 -3
@@ -18,9 +18,10 @@ export interface DirStreamEntry extends Dirent {
18 const dirQ = makeQ(3)
19
20 // cb returns void = just go on, null = stop, false = go on but don't recur (in case of depth)
21 -export function walkDir(path: string, { depth = 0, hidden = true, ctx }: {
21 +export function walkDir(path: string, { depth = 0, hidden = true, parallelizeRecursion = false, ctx }: {
22 depth?: number,
23 hidden?: boolean,
24 + parallelizeRecursion?: boolean,
25 ctx?: Context
26 }, cb: (e: DirStreamEntry) => Promisable<void | null | false>) {
27 let stopped = false
@@ -99,6 +100,7 @@ export function walkDir(path: string, { depth = 0, hidden = true, ctx }: {
100 n++
101 if (!depth || !entry.isDirectory()) return
102 const branchDone = pendingPromise() // per-job
103 + subDirsDone.push(branchDone)
104 const job = () =>
105 readDir(entry.path, depth - 1) // recur
106 .then(x => x, () => {}) // mute errors
@@ -108,8 +110,10 @@ export function walkDir(path: string, { depth = 0, hidden = true, ctx }: {
110 Promise.resolve(res?.branchDone).then(() =>
111 branchDone.resolve())
112 })
111 - dirQ.add(job) // this won't start until next tick
112 - subDirsDone.push(branchDone)
113 + if (parallelizeRecursion)
114 + dirQ.add(job)
115 + else
116 + await job()
117 }
118 }
119 }