import {fault} from '../canvas-core/values.ts'; import {importSourceDigest} from './import-integrity.ts'; export type DigestPort={digest(bytes:Blob,signal?:AbortSignal):Promise;close():void}; /** Read-only compute scheduling: bounded active slots + FIFO Map, never durable operation facts. */ export function createDigestPool(options:{execute?:(bytes:Blob,signal:AbortSignal)=>Promise;concurrency?:number;maxQueued?:number}={}):DigestPort{ const concurrency=options.concurrency??2,maxQueued=options.maxQueued??64,execute=options.execute??importSourceDigest; if(!Number.isSafeInteger(concurrency)||concurrency<1||concurrency>8||!Number.isSafeInteger(maxQueued)||maxQueued<1)throw fault('invalid_digest_budget'); type Task={id:number;bytes:Blob;controller:AbortController;resolve:(value:string)=>void;reject:(error:unknown)=>void;cleanup:()=>void;done:boolean}; const waiting=new Map(),active=new Map();let next=0,closed=false; function finish(task:Task,error?:unknown,value?:string){if(task.done)return;task.done=true;task.cleanup();waiting.delete(task.id);if(error)task.reject(error);else task.resolve(value!);} function pump(){ if(closed)return; while(active.size{if(task.controller.signal.aborted)throw fault('import_aborted');return execute(task.bytes,task.controller.signal);}).then(value=>{if(task.controller.signal.aborted)throw fault('import_aborted');if(!/^[a-f0-9]{64}$/.test(value))throw fault('invalid_digest_response');finish(task,undefined,value);},error=>finish(task,error)).catch(error=>finish(task,error)).finally(()=>{active.delete(task.id);pump();}); } } return Object.freeze({ digest(bytes:Blob,signal?:AbortSignal):Promise{ if(closed)return Promise.reject(fault('digest_pool_closed'));if(signal?.aborted)return Promise.reject(fault('import_aborted'));if(!(bytes instanceof Blob))return Promise.reject(fault('invalid_digest_input'));if(active.size>=concurrency&&waiting.size>=maxQueued)return Promise.reject(fault('digest_capacity')); return new Promise((resolve,reject)=>{const task:Task={id:++next,bytes,controller:new AbortController(),resolve,reject,cleanup:()=>signal?.removeEventListener('abort',abort),done:false},abort=()=>{task.controller.abort();finish(task,fault('import_aborted'));pump();};signal?.addEventListener('abort',abort,{once:true});waiting.set(task.id,task);pump();}); }, close(){if(closed)return;closed=true;for(const task of [...waiting.values(),...active.values()]){task.controller.abort();finish(task,fault('import_aborted'));}}, }); }