/** * 浏览器摘要适配:每个活跃任务创建一个 module Worker,由通用 digest pool 限制并发。 * 没有 Worker 时才使用分块 fallback;Worker 创建/运行失败明确报错,不悄悄把整缓冲原生哈希移到 UI 线程。 */ import {fault} from '../canvas-core/values.ts'; import {createDigestPool} from './digest-pool.ts'; /** Browser capability stays at the adapter; WorkspaceRuntime owns the pool's lifetime. */ /** 在构造时选择能力:Worker 不存在就使用池的默认分块执行器,否则每个 execute 新建专用 Worker。 * 此选择不随后续 Worker 全局变化自动重算;创建/运行失败也不降级重试。 * 使用默认池预算,响应按实例内递增 ID/输入长度/版本和摘要格式核对,不独立重算摘要。 * 没有超时,线程无响应需外部取消/close;Worker 内原生哈希阈值由 digestWorkerSource 控制。 */ export function createBrowserDigests(){ let next=0; return createDigestPool({execute:typeof Worker==='undefined'?undefined:(bytes,signal)=>new Promise((resolve,reject)=>{ if(signal.aborted){reject(fault('import_aborted'));return;} // 每请求独立 Worker,所以成功、失败或 abort 都可直接 terminate,不影响其它任务。 let worker:Worker;try{worker=new Worker(new URL('./digest.worker.ts',import.meta.url),{type:'module'});}catch{reject(fault('digest_worker_unavailable'));return;} const id=++next;let done=false; /** 幂等完成:解绑所有消息/错误/abort 监听并终止 Worker,防止迟到响应泄漏或重复 resolve。 */ const finish=(error?:unknown,sha256?:string)=>{if(done)return;done=true;signal.removeEventListener('abort',abort);worker.onmessage=null;worker.onerror=null;worker.onmessageerror=null;worker.terminate();if(error)reject(error);else resolve(sha256!);},abort=()=>finish(fault('import_aborted')); // 稳定 digest_worker_failed 只有身份匹配才采纳;其它响应走摘要字段校验,不把任意 error 字段当服务错误。 // 格式正确只表明消息可解释,真实性来自受信的专用 Worker 算法;不在 UI 线程重复计算。 // 消息须精确匹配版本、任务 ID 和输入长度,再验64位十六进制摘要;已知错误也必须匹配身份。 signal.addEventListener('abort',abort,{once:true});worker.onmessage=event=>{const value=event.data;if(value?.version===1&&value.id===id&&value.byteLength===bytes.size&&value.error==='digest_worker_failed'){finish(fault('digest_worker_failed'));return;}if(!value||value.version!==1||value.id!==id||value.byteLength!==bytes.size||typeof value.sha256!=='string'||!(/^[a-f0-9]{64}$/.test(value.sha256)))finish(fault('invalid_digest_response'));else finish(undefined,value.sha256);};worker.onerror=event=>{event.preventDefault();finish(fault('digest_worker_failed'));};worker.onmessageerror=()=>finish(fault('invalid_digest_response')); // Blob 通过结构化克隆传递,不在 UI 主线程先读取整份 arrayBuffer。 try{worker.postMessage({version:1,id,bytes});}catch{finish(fault('digest_worker_failed'));} })}); }