diff --git a/.memory/worklog.json b/.memory/worklog.json index ee2d4ec..7623ffb 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1853,6 +1853,13 @@ "message": "auto-save 2026-05-20 22:48 (~2)", "hash": "a3d0c97", "files_changed": 2 + }, + { + "ts": "2026-05-20T22:54:10+08:00", + "type": "commit", + "message": "auto-save 2026-05-20 22:54 (~3)", + "hash": "7697754", + "files_changed": 3 } ] } diff --git a/src/lib/packGenerator.ts b/src/lib/packGenerator.ts index cdede29..6591c3a 100644 --- a/src/lib/packGenerator.ts +++ b/src/lib/packGenerator.ts @@ -389,24 +389,38 @@ export async function generateAssetPack(opts: { }; } - while (remainingTemplates.length > 0) { - const readyTemplates = remainingTemplates - .filter(template => !template.anchorTemplateId || generatedTemplateIds.has(template.anchorTemplateId)) - .slice(0, PACK_ASSET_CONCURRENCY); - if (!readyTemplates.length) { + const inFlight = new Set>(); + function takeReadyTemplate(): AssetTemplate | undefined { + const index = remainingTemplates.findIndex(template => !template.anchorTemplateId || generatedTemplateIds.has(template.anchorTemplateId)); + if (index === -1) return undefined; + const [template] = remainingTemplates.splice(index, 1); + return template; + } + + function scheduleReadyTemplates() { + while (inFlight.size < PACK_ASSET_CONCURRENCY) { + const template = takeReadyTemplate(); + if (!template) return; + const task = (async () => { + const asset = await createAsset(template); + assets.push(asset); + generatedTemplateIds.add(template.id); + await opts.onProgress?.(pack); + })(); + inFlight.add(task); + task.then( + () => inFlight.delete(task), + () => inFlight.delete(task), + ); + } + } + + while (remainingTemplates.length > 0 || inFlight.size > 0) { + scheduleReadyTemplates(); + if (inFlight.size === 0) { throw new Error(`template anchor cycle or missing root: ${remainingTemplates.map(template => template.id).join(', ')}`); } - - for (const template of readyTemplates) { - remainingTemplates.splice(remainingTemplates.indexOf(template), 1); - } - - await Promise.all(readyTemplates.map(async template => { - const asset = await createAsset(template); - assets.push(asset); - generatedTemplateIds.add(template.id); - await opts.onProgress?.(pack); - })); + await Promise.race(inFlight); } const templateOrder = new Map(templates.map((template, index) => [template.id, index]));