# `preload.ts` — bounded-concurrency bulk load ## Purpose This module implements the bounded-concurrency `preload` algorithm backing `IrLoader.preload`. It launches up to `concurrency` workers (each a `while`-loop that pops the next id off a shared cursor), threads the caller's `AbortSignal` through every per-id load, and produces either a fully-populated array of `ImpulseResponse` (in input order) on success or an aggregate `AUDIO_IR_LOADER.DECODE_FAILED` error carrying every per-id failure on partial failure. ## Features - Concurrency clamped to `[1, 32]` to prevent caller mistakes from saturating the runtime. - Input order preserved on success — the `results` array is indexed by the id's position in `ids`, not by completion order. - Aggregate failure: every failing id is collected before the throw, so callers get a complete picture rather than the first error. - Abort threading: workers stop launching new loads as soon as `signal.aborted` becomes true; the throw is `ABORTED` not `DECODE_FAILED`. ## Usage: Basic ```ts const irs = await loader.preload( ["club-a", "club-b", "club-c"], { concurrency: 2 } ); ``` ## Usage: Advanced The aggregate carries every failure for ergonomic dashboards: ```ts import { AUDIO_IR_LOADER_CODES, AudioIrLoaderError, } from "@suaveplan/audio-ir-loader"; try { await loader.preload(ids); } catch (err) { if (err instanceof AudioIrLoaderError && err.errors !== undefined) { for (const { id, error } of err.errors) { metrics.recordLoadFailure({ id, code: error.code }); } } throw err; } ``` Best-effort callers that want to keep going past failures can call `Promise.allSettled(ids.map((id) => loader.load(id)))` directly. ## API Reference ### `runPreload(loadFn, ids, options?): Promise` - `loadFn` — the per-id load function (the loader's closure). - `ids: readonly string[]` — input ids; order preserved on success. - `options.signal?: AbortSignal` — shared abort. - `options.concurrency?: number` — clamped to `[1, 32]`, default `4`. On full success returns a frozen `ImpulseResponse[]` in input order. On partial failure throws `AUDIO_IR_LOADER.DECODE_FAILED` with the failure list on `errors`. On signal abort throws `AUDIO_IR_LOADER.ABORTED` (carrying any pre-abort failures on `errors`). ## Implementation Notes The worker pool is a simple "N parallel tasks, all draining a shared cursor" pattern. The cursor is captured in `takeNext()` which also checks abort state on entry, so a mid-flight abort halts every worker on its next iteration. Non-`SuaveplanError` throwables are wrapped as `AUDIO_IR_LOADER.RESOLVE_FAILED` before being added to the aggregate; this preserves the invariant that every entry in `errors[]` is a `SuaveplanError` (and therefore carries a stable `.code`). The success result is `Object.freeze`d so callers can't mutate the array in place.