/** * Internal concurrency helpers — bounded `Promise.all` for the few sites * where unbounded fan-out would risk resource exhaustion. * * - `MAX_FILE_CONCURRENCY` — parallel `readFile` cap. Bounds FD pressure so * projects with thousands of source files don't trip `EMFILE` against the * typical 1024-FD ulimit. Used by `files.globFiles` and `exports.discoverFromExports`. * - `MAX_RESOLVE_CONCURRENCY` — parallel resolver-call cap for session phase 2. * Async resolvers (Vite/Rollup `resolveId`, user-supplied) get backpressure * instead of a 20k+ task fan-out at once. Sync resolvers (TS default, * no-deps stub) settle in microtasks regardless of the bound. * * Same numerical value today, separately named so a future tuning pass can * move them independently (FD pressure and resolver backpressure are unrelated * limits that happen to coincide at this cap). * * @module */ export const MAX_FILE_CONCURRENCY = 100; export const MAX_RESOLVE_CONCURRENCY = 100; /** * Maps over `items` with a bounded number of in-flight promises, preserving * input order. * * Fail-fast: when `fn` rejects, the outer promise rejects with that error and * idle workers stop pulling from the queue (already-in-flight `fn` calls run * to completion — there's no `AbortSignal` plumbing). Subsequent rejections * from in-flight calls are swallowed by `Promise.all` (only the first wins), * so a misbehaving `fn` can't surface a second error after the function * returns. * * @param items - input array * @param concurrency - maximum number of concurrent operations * @param fn - mapping function (receives item and index) * @returns array of results in input order */ export const map_concurrent = async <T, R>( items: ReadonlyArray<T>, concurrency: number, fn: (item: T, index: number) => Promise<R>, ): Promise<Array<R>> => { const results: Array<R> = new Array(items.length); let next = 0; let failed = false; const worker = async (): Promise<void> => { while (!failed) { const i = next++; if (i >= items.length) return; try { results[i] = await fn(items[i]!, i); } catch (err) { failed = true; throw err; } } }; await Promise.all(Array.from({length: Math.min(concurrency, items.length)}, () => worker())); return results; };
{ "path": "concurrency.ts", "declarations": [ { "name": "MAX_FILE_CONCURRENCY", "kind": "variable", "typeSignature": "100", "sourceLine": 20 }, { "name": "MAX_RESOLVE_CONCURRENCY", "kind": "variable", "typeSignature": "100", "sourceLine": 22 }, { "name": "map_concurrent", "kind": "function", "docComment": "Maps over `items` with a bounded number of in-flight promises, preserving\ninput order.\n\nFail-fast: when `fn` rejects, the outer promise rejects with that error and\nidle workers stop pulling from the queue (already-in-flight `fn` calls run\nto completion — there's no `AbortSignal` plumbing). Subsequent rejections\nfrom in-flight calls are swallowed by `Promise.all` (only the first wins),\nso a misbehaving `fn` can't surface a second error after the function\nreturns.", "typeSignature": "<T, R>(items: readonly T[], concurrency: number, fn: (item: T, index: number) => Promise<R>): Promise<R[]>", "sourceLine": 40, "genericParams": [ { "name": "T" }, { "name": "R" } ], "parameters": [ { "name": "items", "type": "readonly T[]", "description": "input array" }, { "name": "concurrency", "type": "number", "description": "maximum number of concurrent operations" }, { "name": "fn", "type": "(item: T, index: number) => Promise<R>", "description": "mapping function (receives item and index)" } ], "returnType": "Promise<R[]>", "returnDescription": "array of results in input order" } ], "moduleComment": "Internal concurrency helpers — bounded `Promise.all` for the few sites\nwhere unbounded fan-out would risk resource exhaustion.\n\n- `MAX_FILE_CONCURRENCY` — parallel `readFile` cap. Bounds FD pressure so\n projects with thousands of source files don't trip `EMFILE` against the\n typical 1024-FD ulimit. Used by `files.globFiles` and `exports.discoverFromExports`.\n- `MAX_RESOLVE_CONCURRENCY` — parallel resolver-call cap for session phase 2.\n Async resolvers (Vite/Rollup `resolveId`, user-supplied) get backpressure\n instead of a 20k+ task fan-out at once. Sync resolvers (TS default,\n no-deps stub) settle in microtasks regardless of the bound.\n\nSame numerical value today, separately named so a future tuning pass can\nmove them independently (FD pressure and resolver backpressure are unrelated\nlimits that happen to coincide at this cap).", "dependents": [ "exports.ts", "files.ts", "session.ts" ] }