@exige/web-scanner-sdk 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/document-worker.js +1 -1
- package/dist/assets/document-worker.js.map +1 -1
- package/dist/components/scanner-camera-overlay.css +1 -1
- package/dist/{finder-BeINpYm3.js → finder-B8yMQmN9.js} +156 -141
- package/dist/finder-B8yMQmN9.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/react/parts.d.ts +1 -1
- package/dist/react/types.d.ts +18 -1
- package/dist/react.js +262 -226
- package/dist/react.js.map +1 -1
- package/dist/scanner-sdk.js +286 -286
- package/dist/scanner-sdk.js.map +1 -1
- package/dist/sdk/model-download.d.ts +1 -1
- package/package.json +1 -1
- package/dist/finder-BeINpYm3.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function t(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n],i=e[(n+1)%e.length];t+=r.x*i.y-i.x*r.y}return Math.abs(t)/2}function n(e){if(e.length<4)return;let t=e.reduce((e,t)=>t.x+t.y<e.x+e.y?t:e),n=e.reduce((e,t)=>t.x+t.y>e.x+e.y?t:e),r=e.reduce((e,t)=>t.x-t.y>e.x-e.y?t:e),i=e.reduce((e,t)=>t.x-t.y<e.x-e.y?t:e);if((/* @__PURE__ */ new Set([t,r,n,i])).size===4)return{topLeft:t,topRight:r,bottomRight:n,bottomLeft:i}}function r(e){return Math.max(0,Math.min(1,e))}function i(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function a(e,t,n){let r=e.x-t.x,i=e.y-t.y,a=n.x-t.x,o=n.y-t.y,s=Math.max(1e-6,Math.hypot(r,i)*Math.hypot(a,o));return Math.acos(Math.max(-1,Math.min(1,(r*a+i*o)/s)))*180/Math.PI}function o(e,t,n,r){let i=Math.atan2(t.y-e.y,t.x-e.x),a=Math.atan2(r.y-n.y,r.x-n.x),o=Math.abs(i-a)%Math.PI;return o>Math.PI/2&&(o=Math.PI-o),o}function s(e){let{topLeft:t,topRight:n,bottomRight:r,bottomLeft:i}=e,a=n.x-r.x,o=i.x-r.x,s=n.y-r.y,c=i.y-r.y,l=t.x-n.x+r.x-i.x,u=t.y-n.y+r.y-i.y,d=a*c-o*s,f=Math.abs(d)<1e-6?0:(l*c-o*u)/d,p=Math.abs(d)<1e-6?0:(a*u-l*s)/d;return{a:n.x-t.x+f*n.x,b:i.x-t.x+p*i.x,c:t.x,d:n.y-t.y+f*n.y,e:i.y-t.y+p*i.y,f:t.y,g:f,h:p}}function c(e,t){let{a:n,b:r,c:i,d:a,e:o,f:s,g:c,h:l}=t,u=n-e.x*c,d=r-e.x*l,f=a-e.y*c,p=o-e.y*l,m=e.x-i,h=e.y-s,g=u*p-d*f;if(!(Math.abs(g)<1e-8))return{x:(m*p-d*h)/g,y:(u*h-m*f)/g}}function l(e,t){if(!t.length)return 0;let n=s(e),r=0,i=0,a=Math.max(1,Math.floor(t.length/80));for(let e=0;e<t.length;e+=a){let a=c(t[e],n);if(!a)return 0;let o=Math.min(Math.abs(a.x),Math.abs(1-a.x),Math.abs(a.y),Math.abs(1-a.y))+Math.max(0,-a.x,a.x-1,-a.y,a.y-1)*2;r+=o*o,i+=1}let o=Math.sqrt(r/Math.max(1,i));return Math.exp(-o/.08)}function u(n,s,c,u=[n.topLeft,n.topRight,n.bottomRight,n.bottomLeft]){let d=[n.topLeft,n.topRight,n.bottomRight,n.bottomLeft],f=Math.max(1,Math.min(s,c)),p=Math.max(0,...d.map(e=>Math.max(-e.x,-e.y,e.x-s,e.y-c)/f)),m=d.map((t,n)=>e(t,d[(n+1)%4],d[(n+2)%4])),h=m.every(e=>e>0)||m.every(e=>e<0),g=t(d)/Math.max(1,s*c);if(!h||g<.04||g>.995||p>.12)return{total:0,areaRatio:g,angleScore:0,oppositeAngleScore:0,parallelScore:0,homographyScore:0,aspectBoost:0};let _=d.map((e,t)=>a(d[(t+3)%4],e,d[(t+1)%4])),v=d.map((e,t)=>i(e,d[(t+1)%4])),y=Math.max(v[0],v[2])/Math.max(1,Math.min(v[0],v[2])),b=Math.max(v[1],v[3])/Math.max(1,Math.min(v[1],v[3]));if(_.some(e=>e<18||e>162)||y>4||b>4)return{total:0,areaRatio:g,angleScore:0,oppositeAngleScore:0,parallelScore:0,homographyScore:0,aspectBoost:0};let x=_.reduce((e,t)=>e+Math.exp(-Math.abs(t-90)/48),0)/4,S=(Math.exp(-Math.abs(_[0]+_[2]-180)/55)+Math.exp(-Math.abs(_[1]+_[3]-180)/55))/2,C=(Math.exp(-o(d[0],d[1],d[3],d[2])/.62)+Math.exp(-o(d[0],d[3],d[1],d[2])/.62))/2,w=l(n,u),T=r((g-.04)/.5)*(g>.94?r((.995-g)/.055):1),E=Math.exp(-Math.max(0,Math.max(y,b)-1.6)/1.5),D=p?Math.exp(-p/.07):1,O=Math.max(i(d[0],d[1]),i(d[3],d[2])),k=Math.max(i(d[0],d[3]),i(d[1],d[2])),A=Math.max(O,k)/Math.max(1,Math.min(O,k)),j=Math.exp(-Math.abs(A-1.58)/.18),M=Math.exp(-Math.abs(A-1.29)/.18),N=A>=2?r((A-2)/2):0,P=Math.max(j,M,N)*.08;return{total:r(T*.18+x*.2+S*.14+C*.18+w*.3+P)*(.72+E*.28)*D,areaRatio:g,angleScore:x,oppositeAngleScore:S,parallelScore:C,homographyScore:w,aspectBoost:P}}function d(e,t,n,r=256){let i=r*r,a=new Float32Array(i*3);for(let o=0;o<r;o+=1){let s=Math.min(n-1,Math.round((o+.5)*n/r-.5));for(let n=0;n<r;n+=1){let c=Math.min(t-1,Math.round((n+.5)*t/r-.5)),l=(s*t+c)*4,u=o*r+n;a[u]=e[l]/255,a[i+u]=e[l+1]/255,a[i*2+u]=e[l+2]/255}}return{data:a,dimensions:[1,3,r,r]}}function f(e,t,n,r,i){let a=new Uint8Array(n*r),o=new Int32Array(n*r),s=0,c,l=0;for(let u=0;u<n*r;u+=1){if(a[u]||e[t+u]<i)continue;let d=0,f=0,p=0,m=0,h=0,g=0;for(o[f++]=u,a[u]=1;d<f;){let s=o[d++],c=s%n,l=Math.floor(s/n),u=Math.max(0,e[t+s]);p+=(c+.5)*u,m+=(l+.5)*u,h+=u,g=Math.max(g,u);let _=[s-1,s+1,s-n,s+n];for(let s of _){if(s<0||s>=n*r||a[s]||e[t+s]<i)continue;let l=s%n;Math.abs(l-c)>1||(a[s]=1,o[f++]=s)}}f>s&&h>0&&(s=f,l=g,c={x:p/h,y:m/h})}return c?{point:c,peak:l}:void 0}function p(e,t,r,i,a=.3){if(t.length!==4||Number(t[0])!==1||Number(t[1])!==4)throw Error(`Expected document heatmaps shaped [1,4,H,W], got [${t.join(`,`)}].`);let o=Number(t[2]),s=Number(t[3]),c=s*o;if(e.length<c*4||!s||!o)throw Error(`Document heatmap tensor is incomplete.`);let l=[],d=[];for(let t=0;t<4;t+=1){let n=f(e,t*c,s,o,a);if(!n)return{cornerConfidences:d};l.push({x:Math.max(0,Math.min(r-1,n.point.x/s*r)),y:Math.max(0,Math.min(i-1,n.point.y/o*i))}),d.push(n.peak)}let p=n(l);if(!p)return{cornerConfidences:d};let m=u(p,r,i);if(m.total<.35)return{cornerConfidences:d};let h=d.reduce((e,t)=>e+t,0)/4;return{candidate:{corners:p,areaRatio:m.areaRatio,confidence:Math.min(.99,h*.55+m.total*.45)},cornerConfidences:d}}var m=class extends Error{code;constructor(e,t){super(t),this.name=`ScannerError`,this.code=e}};function h(e){let t=Number(e.headers.get(`content-length`));return Number.isFinite(t)&&t>0?t:null}async function g(e,t){let n=h(e);if(!e.body){let r=await e.arrayBuffer();return t?.({loaded:r.byteLength,total:n??r.byteLength}),r}let r=e.body.getReader(),i=[],a=0;for(;;){let{done:e,value:o}=await r.read();if(e)break;o?.byteLength&&(i.push(o),a+=o.byteLength,t?.({loaded:a,total:n}))}let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return a===0&&t?.({loaded:0,total:n}),o.buffer}const _=`1.30.0`,v=`https://exige-scanner-sdk.vercel.app/ort/${_}/`,y=`https://cdn.jsdelivr.net/npm/onnxruntime-web@${_}/dist/`,b=`ort.min.mjs`,x=`ort-wasm-simd-threaded.jsep.wasm`,S=`ort-wasm-simd-threaded.jsep.mjs`;function C(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t}`}function w(){return[C(v,b),C(y,b)]}function T(e=v){return{wasm:C(e,x),mjs:C(e,S)}}function E(e){return typeof e==`string`?{wasm:C(e,x),mjs:C(e,S)}:{wasm:e.wasm,mjs:e.mjs}}function D(e){return typeof e==`string`?e.trim().length>0:!!(e?.wasm.trim()&&e.mjs.trim())}function O(e){return!e.ok&&e.status!==206?!1:!(e.headers.get(`content-type`)??``).toLowerCase().includes(`text/html`)}async function k(e,t=fetch){if(!e)return!1;try{let n=await t(e,{method:`HEAD`});if(O(n))return!0;if(n.status===404||n.status===410)return!1}catch{}try{return O(await t(e,{method:`GET`,headers:{Range:`bytes=0-63`}}))}catch{return!1}}async function A(e,t=fetch){let{wasm:n,mjs:r}=E(e),[i,a]=await Promise.all([k(n,t),k(r,t)]);return i&&a}async function j(e,t=fetch){if(D(e)&&await A(e,t))return typeof e==`string`?e.trim():{wasm:e.wasm.trim(),mjs:e.mjs.trim()};let n=T(v);return await A(n,t)?n:T(y)}async function M(e){return import(e)}async function N(e=M){let t=[];for(let n of w())try{return await e(n)}catch(e){t.push(e)}throw Error(`Failed to load onnxruntime-web@${_} from the pinned CDN.`,{cause:t.at(-1)})}const P=globalThis,F=/* @__PURE__ */ new Map,I=/* @__PURE__ */ new Map,L=`model-bytes`;function R(){return new Promise((e,t)=>{let n=indexedDB.open(`exige-scanner-sdk-models`,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(L)||n.result.createObjectStore(L)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}async function z(e){if(typeof indexedDB>`u`)return;let t=await R();try{return await new Promise((n,r)=>{let i=t.transaction(L).objectStore(L).get(e);i.onsuccess=()=>n(i.result instanceof ArrayBuffer?i.result:void 0),i.onerror=()=>r(i.error)})}finally{t.close()}}async function B(e,t){if(typeof indexedDB>`u`)return;let n=await R();try{await new Promise((r,i)=>{let a=n.transaction(L,`readwrite`);a.objectStore(L).put(t,e),a.oncomplete=()=>r(),a.onerror=()=>i(a.error)})}finally{n.close()}}async function V(e,t,n){let r=F.get(e);return r||(r=(async()=>{if(t)try{let t=await z(e);if(t)return t}catch{}n?.({loaded:0,total:null});let r;try{let i=await fetch(e,{cache:t?`force-cache`:`no-store`});if(!i.ok)throw new m(`model-http`,`Document model request failed with HTTP ${i.status}.`);r=await g(i,n)}catch(e){throw n?.({loaded:0,total:null,done:!0}),e}if(n?.({loaded:r.byteLength,total:r.byteLength,done:!0}),t)try{await B(e,r)}catch{}return r})(),F.set(e,r)),r}function H(e){return e?typeof e==`string`?e:`${e.wasm}|${e.mjs}`:``}async function U(){try{return await import("./ort.min.js")}catch{return N()}}async function W(e,t,n,r){let i=`${e}|${H(n)}`,a=I.get(i);return a||(a=(async()=>{let[i,a]=await Promise.all([U(),V(e,t,r)]);if(i.env.wasm.wasmPaths=await j(n),typeof navigator<`u`&&`gpu`in navigator)try{return{session:await i.InferenceSession.create(a,{executionProviders:[`webgpu`],graphOptimizationLevel:`all`}),ort:i,provider:`webgpu`}}catch{}return{session:await i.InferenceSession.create(a,{executionProviders:[`wasm`],graphOptimizationLevel:`all`}),ort:i,provider:`wasm`}})(),I.set(i,a)),a}async function G(e){if(!e.modelUrl)throw Error(`A DocAligner modelUrl is required.`);let t=d(new Uint8ClampedArray(e.pixels),e.width,e.height),{session:n,ort:r,provider:i}=await W(e.modelUrl,e.cacheModel!==!1,e.ortWasmPaths,t=>{P.postMessage({id:e.id,download:{url:e.modelUrl??``,...t}})}),a=performance.now(),o=(await n.run({[n.inputNames[0]]:new r.Tensor(`float32`,t.data,t.dimensions)}))[n.outputNames[0]];if(!o)throw Error(`Document model returned no output tensor.`);return{sharpness:20,candidate:p(o.data,o.dims.map(Number),e.width,e.height,e.heatmapThreshold??.3).candidate,detector:`ml`,modelStatus:`ready`,modelProvider:i,inferenceMs:performance.now()-a}}async function K(e){try{let t=await G(e);return t.candidate?t:{...t,modelStatus:`failed`,modelError:`ML detector returned no geometrically valid corners.`}}catch(e){if(e instanceof m&&e.code===`model-http`)throw e;return{sharpness:20,detector:`ml`,modelStatus:`failed`,modelError:e instanceof Error?e.message:`Document model failed.`}}}let q=Promise.resolve();P.onmessage=e=>{let t=e.data;q=q.then(async()=>{try{P.postMessage({id:t.id,result:await K(t)})}catch(e){P.postMessage({id:t.id,error:e instanceof Error?e.message:`Document analysis failed.`,errorCode:e instanceof m?e.code:void 0})}})};
|
|
1
|
+
function e(e,t,n){return(t.x-e.x)*(n.y-e.y)-(t.y-e.y)*(n.x-e.x)}function t(e){let t=0;for(let n=0;n<e.length;n+=1){let r=e[n],i=e[(n+1)%e.length];t+=r.x*i.y-i.x*r.y}return Math.abs(t)/2}function n(e){if(e.length<4)return;let t=e.reduce((e,t)=>t.x+t.y<e.x+e.y?t:e),n=e.reduce((e,t)=>t.x+t.y>e.x+e.y?t:e),r=e.reduce((e,t)=>t.x-t.y>e.x-e.y?t:e),i=e.reduce((e,t)=>t.x-t.y<e.x-e.y?t:e);if((/* @__PURE__ */ new Set([t,r,n,i])).size===4)return{topLeft:t,topRight:r,bottomRight:n,bottomLeft:i}}function r(e){return Math.max(0,Math.min(1,e))}function i(e,t){return Math.hypot(e.x-t.x,e.y-t.y)}function a(e,t,n){let r=e.x-t.x,i=e.y-t.y,a=n.x-t.x,o=n.y-t.y,s=Math.max(1e-6,Math.hypot(r,i)*Math.hypot(a,o));return Math.acos(Math.max(-1,Math.min(1,(r*a+i*o)/s)))*180/Math.PI}function o(e,t,n,r){let i=Math.atan2(t.y-e.y,t.x-e.x),a=Math.atan2(r.y-n.y,r.x-n.x),o=Math.abs(i-a)%Math.PI;return o>Math.PI/2&&(o=Math.PI-o),o}function s(e){let{topLeft:t,topRight:n,bottomRight:r,bottomLeft:i}=e,a=n.x-r.x,o=i.x-r.x,s=n.y-r.y,c=i.y-r.y,l=t.x-n.x+r.x-i.x,u=t.y-n.y+r.y-i.y,d=a*c-o*s,f=Math.abs(d)<1e-6?0:(l*c-o*u)/d,p=Math.abs(d)<1e-6?0:(a*u-l*s)/d;return{a:n.x-t.x+f*n.x,b:i.x-t.x+p*i.x,c:t.x,d:n.y-t.y+f*n.y,e:i.y-t.y+p*i.y,f:t.y,g:f,h:p}}function c(e,t){let{a:n,b:r,c:i,d:a,e:o,f:s,g:c,h:l}=t,u=n-e.x*c,d=r-e.x*l,f=a-e.y*c,p=o-e.y*l,m=e.x-i,h=e.y-s,g=u*p-d*f;if(!(Math.abs(g)<1e-8))return{x:(m*p-d*h)/g,y:(u*h-m*f)/g}}function l(e,t){if(!t.length)return 0;let n=s(e),r=0,i=0,a=Math.max(1,Math.floor(t.length/80));for(let e=0;e<t.length;e+=a){let a=c(t[e],n);if(!a)return 0;let o=Math.min(Math.abs(a.x),Math.abs(1-a.x),Math.abs(a.y),Math.abs(1-a.y))+Math.max(0,-a.x,a.x-1,-a.y,a.y-1)*2;r+=o*o,i+=1}let o=Math.sqrt(r/Math.max(1,i));return Math.exp(-o/.08)}function u(n,s,c,u=[n.topLeft,n.topRight,n.bottomRight,n.bottomLeft]){let d=[n.topLeft,n.topRight,n.bottomRight,n.bottomLeft],f=Math.max(1,Math.min(s,c)),p=Math.max(0,...d.map(e=>Math.max(-e.x,-e.y,e.x-s,e.y-c)/f)),m=d.map((t,n)=>e(t,d[(n+1)%4],d[(n+2)%4])),h=m.every(e=>e>0)||m.every(e=>e<0),g=t(d)/Math.max(1,s*c);if(!h||g<.04||g>.995||p>.12)return{total:0,areaRatio:g,angleScore:0,oppositeAngleScore:0,parallelScore:0,homographyScore:0,aspectBoost:0};let _=d.map((e,t)=>a(d[(t+3)%4],e,d[(t+1)%4])),v=d.map((e,t)=>i(e,d[(t+1)%4])),y=Math.max(v[0],v[2])/Math.max(1,Math.min(v[0],v[2])),b=Math.max(v[1],v[3])/Math.max(1,Math.min(v[1],v[3]));if(_.some(e=>e<18||e>162)||y>4||b>4)return{total:0,areaRatio:g,angleScore:0,oppositeAngleScore:0,parallelScore:0,homographyScore:0,aspectBoost:0};let x=_.reduce((e,t)=>e+Math.exp(-Math.abs(t-90)/48),0)/4,S=(Math.exp(-Math.abs(_[0]+_[2]-180)/55)+Math.exp(-Math.abs(_[1]+_[3]-180)/55))/2,C=(Math.exp(-o(d[0],d[1],d[3],d[2])/.62)+Math.exp(-o(d[0],d[3],d[1],d[2])/.62))/2,w=l(n,u),T=r((g-.04)/.5)*(g>.94?r((.995-g)/.055):1),E=Math.exp(-Math.max(0,Math.max(y,b)-1.6)/1.5),D=p?Math.exp(-p/.07):1,O=Math.max(i(d[0],d[1]),i(d[3],d[2])),k=Math.max(i(d[0],d[3]),i(d[1],d[2])),A=Math.max(O,k)/Math.max(1,Math.min(O,k)),j=Math.exp(-Math.abs(A-1.58)/.18),M=Math.exp(-Math.abs(A-1.29)/.18),N=A>=2?r((A-2)/2):0,P=Math.max(j,M,N)*.08;return{total:r(T*.18+x*.2+S*.14+C*.18+w*.3+P)*(.72+E*.28)*D,areaRatio:g,angleScore:x,oppositeAngleScore:S,parallelScore:C,homographyScore:w,aspectBoost:P}}function d(e,t,n,r=256){let i=r*r,a=new Float32Array(i*3);for(let o=0;o<r;o+=1){let s=Math.min(n-1,Math.round((o+.5)*n/r-.5));for(let n=0;n<r;n+=1){let c=Math.min(t-1,Math.round((n+.5)*t/r-.5)),l=(s*t+c)*4,u=o*r+n;a[u]=e[l]/255,a[i+u]=e[l+1]/255,a[i*2+u]=e[l+2]/255}}return{data:a,dimensions:[1,3,r,r]}}function f(e,t,n,r,i){let a=new Uint8Array(n*r),o=new Int32Array(n*r),s=0,c,l=0;for(let u=0;u<n*r;u+=1){if(a[u]||e[t+u]<i)continue;let d=0,f=0,p=0,m=0,h=0,g=0;for(o[f++]=u,a[u]=1;d<f;){let s=o[d++],c=s%n,l=Math.floor(s/n),u=Math.max(0,e[t+s]);p+=(c+.5)*u,m+=(l+.5)*u,h+=u,g=Math.max(g,u);let _=[s-1,s+1,s-n,s+n];for(let s of _){if(s<0||s>=n*r||a[s]||e[t+s]<i)continue;let l=s%n;Math.abs(l-c)>1||(a[s]=1,o[f++]=s)}}f>s&&h>0&&(s=f,l=g,c={x:p/h,y:m/h})}return c?{point:c,peak:l}:void 0}function p(e,t,r,i,a=.3){if(t.length!==4||Number(t[0])!==1||Number(t[1])!==4)throw Error(`Expected document heatmaps shaped [1,4,H,W], got [${t.join(`,`)}].`);let o=Number(t[2]),s=Number(t[3]),c=s*o;if(e.length<c*4||!s||!o)throw Error(`Document heatmap tensor is incomplete.`);let l=[],d=[];for(let t=0;t<4;t+=1){let n=f(e,t*c,s,o,a);if(!n)return{cornerConfidences:d};l.push({x:Math.max(0,Math.min(r-1,n.point.x/s*r)),y:Math.max(0,Math.min(i-1,n.point.y/o*i))}),d.push(n.peak)}let p=n(l);if(!p)return{cornerConfidences:d};let m=u(p,r,i);if(m.total<.35)return{cornerConfidences:d};let h=d.reduce((e,t)=>e+t,0)/4;return{candidate:{corners:p,areaRatio:m.areaRatio,confidence:Math.min(.99,h*.55+m.total*.45)},cornerConfidences:d}}var m=class extends Error{code;constructor(e,t){super(t),this.name=`ScannerError`,this.code=e}};const h=Symbol.for(`exige.web-scanner-sdk.model-downloads`);function g(){let e=globalThis;return e[h]||(e[h]={active:/* @__PURE__ */ new Map,listeners:/* @__PURE__ */ new Set,cache:/* @__PURE__ */ new Map,inflight:/* @__PURE__ */ new Map}),e[h]}g().active,g().listeners,g().cache,g().inflight;function _(e){let t=Number(e.headers.get(`content-length`));return Number.isFinite(t)&&t>0?t:null}async function v(e,t){let n=_(e);if(!e.body){let r=await e.arrayBuffer();return t?.({loaded:r.byteLength,total:n??r.byteLength}),r}let r=e.body.getReader(),i=[],a=0;for(;;){let{done:e,value:o}=await r.read();if(e)break;o?.byteLength&&(i.push(o),a+=o.byteLength,t?.({loaded:a,total:n}))}let o=new Uint8Array(a),s=0;for(let e of i)o.set(e,s),s+=e.byteLength;return a===0&&t?.({loaded:0,total:n}),o.buffer}const y=`1.30.0`,b=`https://exige-scanner-sdk.vercel.app/ort/${y}/`,x=`https://cdn.jsdelivr.net/npm/onnxruntime-web@${y}/dist/`,S=`ort.min.mjs`,C=`ort-wasm-simd-threaded.jsep.wasm`,w=`ort-wasm-simd-threaded.jsep.mjs`;function T(e,t){return`${e.endsWith(`/`)?e:`${e}/`}${t}`}function E(){return[T(b,S),T(x,S)]}function D(e=b){return{wasm:T(e,C),mjs:T(e,w)}}function O(e){return typeof e==`string`?{wasm:T(e,C),mjs:T(e,w)}:{wasm:e.wasm,mjs:e.mjs}}function k(e){return typeof e==`string`?e.trim().length>0:!!(e?.wasm.trim()&&e.mjs.trim())}function A(e){return!e.ok&&e.status!==206?!1:!(e.headers.get(`content-type`)??``).toLowerCase().includes(`text/html`)}async function j(e,t=fetch){if(!e)return!1;try{let n=await t(e,{method:`HEAD`});if(A(n))return!0;if(n.status===404||n.status===410)return!1}catch{}try{return A(await t(e,{method:`GET`,headers:{Range:`bytes=0-63`}}))}catch{return!1}}async function M(e,t=fetch){let{wasm:n,mjs:r}=O(e),[i,a]=await Promise.all([j(n,t),j(r,t)]);return i&&a}async function N(e,t=fetch){if(k(e)&&await M(e,t))return typeof e==`string`?e.trim():{wasm:e.wasm.trim(),mjs:e.mjs.trim()};let n=D(b);return await M(n,t)?n:D(x)}async function P(e){return import(e)}async function F(e=P){let t=[];for(let n of E())try{return await e(n)}catch(e){t.push(e)}throw Error(`Failed to load onnxruntime-web@${y} from the pinned CDN.`,{cause:t.at(-1)})}const I=globalThis,L=/* @__PURE__ */ new Map,R=/* @__PURE__ */ new Map,z=`model-bytes`;function B(){return new Promise((e,t)=>{let n=indexedDB.open(`exige-scanner-sdk-models`,1);n.onupgradeneeded=()=>{n.result.objectStoreNames.contains(z)||n.result.createObjectStore(z)},n.onsuccess=()=>e(n.result),n.onerror=()=>t(n.error)})}async function V(e){if(typeof indexedDB>`u`)return;let t=await B();try{return await new Promise((n,r)=>{let i=t.transaction(z).objectStore(z).get(e);i.onsuccess=()=>n(i.result instanceof ArrayBuffer?i.result:void 0),i.onerror=()=>r(i.error)})}finally{t.close()}}async function H(e,t){if(typeof indexedDB>`u`)return;let n=await B();try{await new Promise((r,i)=>{let a=n.transaction(z,`readwrite`);a.objectStore(z).put(t,e),a.oncomplete=()=>r(),a.onerror=()=>i(a.error)})}finally{n.close()}}async function U(e,t,n){let r=L.get(e);return r||(r=(async()=>{if(t)try{let t=await V(e);if(t)return t}catch{}n?.({loaded:0,total:null});let r;try{let i=await fetch(e,{cache:t?`force-cache`:`no-store`});if(!i.ok)throw new m(`model-http`,`Document model request failed with HTTP ${i.status}.`);r=await v(i,n)}catch(e){throw n?.({loaded:0,total:null,done:!0}),e}if(n?.({loaded:r.byteLength,total:r.byteLength,done:!0}),t)try{await H(e,r)}catch{}return r})(),L.set(e,r)),r}function W(e){return e?typeof e==`string`?e:`${e.wasm}|${e.mjs}`:``}async function G(){try{return await import("./ort.min.js")}catch{return F()}}async function K(e,t,n,r){let i=`${e}|${W(n)}`,a=R.get(i);return a||(a=(async()=>{let[i,a]=await Promise.all([G(),U(e,t,r)]);if(i.env.wasm.wasmPaths=await N(n),typeof navigator<`u`&&`gpu`in navigator)try{return{session:await i.InferenceSession.create(a,{executionProviders:[`webgpu`],graphOptimizationLevel:`all`}),ort:i,provider:`webgpu`}}catch{}return{session:await i.InferenceSession.create(a,{executionProviders:[`wasm`],graphOptimizationLevel:`all`}),ort:i,provider:`wasm`}})(),R.set(i,a)),a}async function q(e){if(!e.modelUrl)throw Error(`A DocAligner modelUrl is required.`);let t=d(new Uint8ClampedArray(e.pixels),e.width,e.height),{session:n,ort:r,provider:i}=await K(e.modelUrl,e.cacheModel!==!1,e.ortWasmPaths,t=>{I.postMessage({id:e.id,download:{url:e.modelUrl??``,...t}})}),a=performance.now(),o=(await n.run({[n.inputNames[0]]:new r.Tensor(`float32`,t.data,t.dimensions)}))[n.outputNames[0]];if(!o)throw Error(`Document model returned no output tensor.`);return{sharpness:20,candidate:p(o.data,o.dims.map(Number),e.width,e.height,e.heatmapThreshold??.3).candidate,detector:`ml`,modelStatus:`ready`,modelProvider:i,inferenceMs:performance.now()-a}}async function J(e){try{let t=await q(e);return t.candidate?t:{...t,modelStatus:`failed`,modelError:`ML detector returned no geometrically valid corners.`}}catch(e){if(e instanceof m&&e.code===`model-http`)throw e;return{sharpness:20,detector:`ml`,modelStatus:`failed`,modelError:e instanceof Error?e.message:`Document model failed.`}}}let Y=Promise.resolve();I.onmessage=e=>{let t=e.data;Y=Y.then(async()=>{try{I.postMessage({id:t.id,result:await J(t)})}catch(e){I.postMessage({id:t.id,error:e instanceof Error?e.message:`Document analysis failed.`,errorCode:e instanceof m?e.code:void 0})}})};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-worker.js","names":[],"sources":["../../src/sdk/document-geometry.ts","../../src/sdk/document-ml.ts","../../src/sdk/errors.ts","../../src/sdk/model-download.ts","../../src/sdk/ort-cdn.ts","../../src/sdk/document-worker.ts"],"sourcesContent":["import type { DocumentCorners, Point } from './types'\n\nexport interface QuadCandidate {\n corners: DocumentCorners\n confidence: number\n areaRatio: number\n}\n\nexport interface PerspectiveQuadScore {\n total: number\n areaRatio: number\n angleScore: number\n oppositeAngleScore: number\n parallelScore: number\n homographyScore: number\n aspectBoost: number\n}\n\nfunction cross(origin: Point, a: Point, b: Point) {\n return (a.x - origin.x) * (b.y - origin.y) - (a.y - origin.y) * (b.x - origin.x)\n}\n\nexport function polygonArea(points: Point[]) {\n let sum = 0\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index]\n const next = points[(index + 1) % points.length]\n sum += current.x * next.y - next.x * current.y\n }\n return Math.abs(sum) / 2\n}\n\nexport function orderCorners(points: Point[]): DocumentCorners | undefined {\n if (points.length < 4) return undefined\n const topLeft = points.reduce((best, point) => point.x + point.y < best.x + best.y ? point : best)\n const bottomRight = points.reduce((best, point) => point.x + point.y > best.x + best.y ? point : best)\n const topRight = points.reduce((best, point) => point.x - point.y > best.x - best.y ? point : best)\n const bottomLeft = points.reduce((best, point) => point.x - point.y < best.x - best.y ? point : best)\n const unique = new Set([topLeft, topRight, bottomRight, bottomLeft])\n if (unique.size !== 4) return undefined\n return { topLeft, topRight, bottomRight, bottomLeft }\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value))\n}\n\nfunction distance(a: Point, b: Point) {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n\nfunction interiorAngle(previous: Point, vertex: Point, next: Point) {\n const ax = previous.x - vertex.x\n const ay = previous.y - vertex.y\n const bx = next.x - vertex.x\n const by = next.y - vertex.y\n const denominator = Math.max(1e-6, Math.hypot(ax, ay) * Math.hypot(bx, by))\n return Math.acos(Math.max(-1, Math.min(1, (ax * bx + ay * by) / denominator))) * 180 / Math.PI\n}\n\nfunction directionDifference(a: Point, b: Point, c: Point, d: Point) {\n const first = Math.atan2(b.y - a.y, b.x - a.x)\n const second = Math.atan2(d.y - c.y, d.x - c.x)\n let difference = Math.abs(first - second) % Math.PI\n if (difference > Math.PI / 2) difference = Math.PI - difference\n return difference\n}\n\nfunction unitToQuadCoefficients(corners: DocumentCorners) {\n const { topLeft: tl, topRight: tr, bottomRight: br, bottomLeft: bl } = corners\n const dx1 = tr.x - br.x\n const dx2 = bl.x - br.x\n const dy1 = tr.y - br.y\n const dy2 = bl.y - br.y\n const sx = tl.x - tr.x + br.x - bl.x\n const sy = tl.y - tr.y + br.y - bl.y\n const denominator = dx1 * dy2 - dx2 * dy1\n const g = Math.abs(denominator) < 1e-6 ? 0 : (sx * dy2 - dx2 * sy) / denominator\n const h = Math.abs(denominator) < 1e-6 ? 0 : (dx1 * sy - sx * dy1) / denominator\n return {\n a: tr.x - tl.x + g * tr.x,\n b: bl.x - tl.x + h * bl.x,\n c: tl.x,\n d: tr.y - tl.y + g * tr.y,\n e: bl.y - tl.y + h * bl.y,\n f: tl.y,\n g,\n h,\n }\n}\n\nfunction quadToUnit(point: Point, coefficients: ReturnType<typeof unitToQuadCoefficients>) {\n const { a, b, c, d, e, f, g, h } = coefficients\n const aa = a - point.x * g\n const bb = b - point.x * h\n const dd = d - point.y * g\n const ee = e - point.y * h\n const xx = point.x - c\n const yy = point.y - f\n const determinant = aa * ee - bb * dd\n if (Math.abs(determinant) < 1e-8) return undefined\n return {\n x: (xx * ee - bb * yy) / determinant,\n y: (aa * yy - xx * dd) / determinant,\n }\n}\n\nfunction homographyBoundaryScore(corners: DocumentCorners, hull: Point[]) {\n if (!hull.length) return 0\n const coefficients = unitToQuadCoefficients(corners)\n let squaredResidual = 0\n let samples = 0\n const stride = Math.max(1, Math.floor(hull.length / 80))\n for (let index = 0; index < hull.length; index += stride) {\n const unit = quadToUnit(hull[index], coefficients)\n if (!unit) return 0\n const boundaryDistance = Math.min(\n Math.abs(unit.x),\n Math.abs(1 - unit.x),\n Math.abs(unit.y),\n Math.abs(1 - unit.y),\n )\n const outside = Math.max(0, -unit.x, unit.x - 1, -unit.y, unit.y - 1)\n const residual = boundaryDistance + outside * 2\n squaredResidual += residual * residual\n samples += 1\n }\n const rms = Math.sqrt(squaredResidual / Math.max(1, samples))\n return Math.exp(-rms / 0.08)\n}\n\nexport function scorePerspectiveQuad(\n corners: DocumentCorners,\n frameWidth: number,\n frameHeight: number,\n hull: Point[] = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft],\n): PerspectiveQuadScore {\n const ordered = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft]\n const frameScale = Math.max(1, Math.min(frameWidth, frameHeight))\n const outsideRatio = Math.max(\n 0,\n ...ordered.map((point) => Math.max(\n -point.x,\n -point.y,\n point.x - frameWidth,\n point.y - frameHeight,\n ) / frameScale),\n )\n const signedCrosses = ordered.map((point, index) =>\n cross(point, ordered[(index + 1) % 4], ordered[(index + 2) % 4]),\n )\n const convex = signedCrosses.every((value) => value > 0) ||\n signedCrosses.every((value) => value < 0)\n const areaRatio = polygonArea(ordered) / Math.max(1, frameWidth * frameHeight)\n if (\n !convex ||\n areaRatio < 0.04 ||\n areaRatio > 0.995 ||\n outsideRatio > 0.12\n ) {\n return {\n total: 0,\n areaRatio,\n angleScore: 0,\n oppositeAngleScore: 0,\n parallelScore: 0,\n homographyScore: 0,\n aspectBoost: 0,\n }\n }\n\n const angles = ordered.map((point, index) =>\n interiorAngle(ordered[(index + 3) % 4], point, ordered[(index + 1) % 4]),\n )\n const edgeLengths = ordered.map((point, index) => distance(point, ordered[(index + 1) % 4]))\n const horizontalScaleRatio = Math.max(edgeLengths[0], edgeLengths[2]) /\n Math.max(1, Math.min(edgeLengths[0], edgeLengths[2]))\n const verticalScaleRatio = Math.max(edgeLengths[1], edgeLengths[3]) /\n Math.max(1, Math.min(edgeLengths[1], edgeLengths[3]))\n if (\n angles.some((angle) => angle < 18 || angle > 162) ||\n horizontalScaleRatio > 4 ||\n verticalScaleRatio > 4\n ) {\n return {\n total: 0,\n areaRatio,\n angleScore: 0,\n oppositeAngleScore: 0,\n parallelScore: 0,\n homographyScore: 0,\n aspectBoost: 0,\n }\n }\n const angleScore = angles.reduce(\n (sum, angle) => sum + Math.exp(-Math.abs(angle - 90) / 48),\n 0,\n ) / 4\n const oppositeAngleScore = (\n Math.exp(-Math.abs(angles[0] + angles[2] - 180) / 55) +\n Math.exp(-Math.abs(angles[1] + angles[3] - 180) / 55)\n ) / 2\n const parallelScore = (\n Math.exp(-directionDifference(ordered[0], ordered[1], ordered[3], ordered[2]) / 0.62) +\n Math.exp(-directionDifference(ordered[0], ordered[3], ordered[1], ordered[2]) / 0.62)\n ) / 2\n const homographyScore = homographyBoundaryScore(corners, hull)\n const areaScore = clamp01((areaRatio - 0.04) / 0.5) *\n (areaRatio > 0.94 ? clamp01((0.995 - areaRatio) / 0.055) : 1)\n const perspectiveScaleScore = Math.exp(\n -Math.max(0, Math.max(horizontalScaleRatio, verticalScaleRatio) - 1.6) / 1.5,\n )\n const frameVisibilityScore = outsideRatio ? Math.exp(-outsideRatio / 0.07) : 1\n const width = Math.max(distance(ordered[0], ordered[1]), distance(ordered[3], ordered[2]))\n const height = Math.max(distance(ordered[0], ordered[3]), distance(ordered[1], ordered[2]))\n const aspect = Math.max(width, height) / Math.max(1, Math.min(width, height))\n const idBoost = Math.exp(-Math.abs(aspect - 1.58) / 0.18)\n const letterBoost = Math.exp(-Math.abs(aspect - 1.29) / 0.18)\n const receiptBoost = aspect >= 2 ? clamp01((aspect - 2) / 2) : 0\n const aspectBoost = Math.max(idBoost, letterBoost, receiptBoost) * 0.08\n const geometricScore = clamp01(\n areaScore * 0.18 +\n angleScore * 0.2 +\n oppositeAngleScore * 0.14 +\n parallelScore * 0.18 +\n homographyScore * 0.3 +\n aspectBoost\n )\n const total = geometricScore * (0.72 + perspectiveScaleScore * 0.28) * frameVisibilityScore\n return {\n total,\n areaRatio,\n angleScore,\n oppositeAngleScore,\n parallelScore,\n homographyScore,\n aspectBoost,\n }\n}\n","import { orderCorners, scorePerspectiveQuad, type QuadCandidate } from './document-geometry'\nimport type { Point } from './types'\n\nexport interface DocumentModelInput {\n data: Float32Array\n dimensions: [1, 3, number, number]\n}\n\nexport interface DecodedDocumentHeatmaps {\n candidate?: QuadCandidate\n cornerConfidences: number[]\n}\n\nexport function preprocessDocumentModel(\n pixels: Uint8ClampedArray,\n width: number,\n height: number,\n inputSize = 256,\n): DocumentModelInput {\n const planeSize = inputSize * inputSize\n const data = new Float32Array(planeSize * 3)\n for (let y = 0; y < inputSize; y += 1) {\n const sourceY = Math.min(height - 1, Math.round((y + 0.5) * height / inputSize - 0.5))\n for (let x = 0; x < inputSize; x += 1) {\n const sourceX = Math.min(width - 1, Math.round((x + 0.5) * width / inputSize - 0.5))\n const sourceOffset = (sourceY * width + sourceX) * 4\n const targetOffset = y * inputSize + x\n data[targetOffset] = pixels[sourceOffset] / 255\n data[planeSize + targetOffset] = pixels[sourceOffset + 1] / 255\n data[planeSize * 2 + targetOffset] = pixels[sourceOffset + 2] / 255\n }\n }\n return { data, dimensions: [1, 3, inputSize, inputSize] }\n}\n\nfunction largestHeatmapComponent(\n values: Float32Array,\n base: number,\n width: number,\n height: number,\n threshold: number,\n) {\n const visited = new Uint8Array(width * height)\n const queue = new Int32Array(width * height)\n let bestCount = 0\n let bestPoint: Point | undefined\n let bestPeak = 0\n for (let seed = 0; seed < width * height; seed += 1) {\n if (visited[seed] || values[base + seed] < threshold) continue\n let read = 0\n let write = 0\n let weightedX = 0\n let weightedY = 0\n let totalWeight = 0\n let peak = 0\n queue[write++] = seed\n visited[seed] = 1\n while (read < write) {\n const index = queue[read++]\n const x = index % width\n const y = Math.floor(index / width)\n const weight = Math.max(0, values[base + index])\n weightedX += (x + 0.5) * weight\n weightedY += (y + 0.5) * weight\n totalWeight += weight\n peak = Math.max(peak, weight)\n const neighbors = [index - 1, index + 1, index - width, index + width]\n for (const neighbor of neighbors) {\n if (\n neighbor < 0 ||\n neighbor >= width * height ||\n visited[neighbor] ||\n values[base + neighbor] < threshold\n ) continue\n const neighborX = neighbor % width\n if (Math.abs(neighborX - x) > 1) continue\n visited[neighbor] = 1\n queue[write++] = neighbor\n }\n }\n if (write > bestCount && totalWeight > 0) {\n bestCount = write\n bestPeak = peak\n bestPoint = {\n x: weightedX / totalWeight,\n y: weightedY / totalWeight,\n }\n }\n }\n return bestPoint ? { point: bestPoint, peak: bestPeak } : undefined\n}\n\nexport function decodeDocumentHeatmaps(\n heatmaps: Float32Array,\n dimensions: readonly number[],\n imageWidth: number,\n imageHeight: number,\n threshold = 0.3,\n): DecodedDocumentHeatmaps {\n if (\n dimensions.length !== 4 ||\n Number(dimensions[0]) !== 1 ||\n Number(dimensions[1]) !== 4\n ) {\n throw new Error(`Expected document heatmaps shaped [1,4,H,W], got [${dimensions.join(',')}].`)\n }\n const heatmapHeight = Number(dimensions[2])\n const heatmapWidth = Number(dimensions[3])\n const planeSize = heatmapWidth * heatmapHeight\n if (heatmaps.length < planeSize * 4 || !heatmapWidth || !heatmapHeight) {\n throw new Error('Document heatmap tensor is incomplete.')\n }\n const points: Point[] = []\n const cornerConfidences: number[] = []\n for (let channel = 0; channel < 4; channel += 1) {\n const component = largestHeatmapComponent(\n heatmaps,\n channel * planeSize,\n heatmapWidth,\n heatmapHeight,\n threshold,\n )\n if (!component) return { cornerConfidences }\n points.push({\n x: Math.max(0, Math.min(imageWidth - 1, component.point.x / heatmapWidth * imageWidth)),\n y: Math.max(0, Math.min(imageHeight - 1, component.point.y / heatmapHeight * imageHeight)),\n })\n cornerConfidences.push(component.peak)\n }\n const corners = orderCorners(points)\n if (!corners) return { cornerConfidences }\n const geometry = scorePerspectiveQuad(corners, imageWidth, imageHeight)\n if (geometry.total < 0.35) return { cornerConfidences }\n const heatmapConfidence = cornerConfidences.reduce((sum, value) => sum + value, 0) / 4\n return {\n candidate: {\n corners,\n areaRatio: geometry.areaRatio,\n confidence: Math.min(0.99, heatmapConfidence * 0.55 + geometry.total * 0.45),\n },\n cornerConfidences,\n }\n}\n","/**\n * Operational failure from the scanner runtime.\n *\n * Scan quality stays a returned status (`success`, `no-document`,\n * `low-confidence`, or `low-quality`). Redaction match quality stays\n * `success`, `partial`, or `unmatched`. Neither of those outcomes uses this error.\n * A bad image does not throw.\n */\nexport type ScannerErrorCode =\n | 'secure-context'\n | 'worker'\n | 'model-http'\n | 'language'\n | 'canvas'\n\nexport class ScannerError extends Error {\n readonly code: ScannerErrorCode\n\n constructor(code: ScannerErrorCode, message: string) {\n super(message)\n this.name = 'ScannerError'\n this.code = code\n }\n}\n\nexport function canvasUnavailable(): ScannerError {\n return new ScannerError('canvas', 'Canvas 2D is unavailable in this browser.')\n}\n","import { ScannerError } from './errors'\n\nexport interface ModelDownloadProgress {\n loaded: number\n /** Byte length from Content-Length. Null when that header is absent. */\n total: number | null\n done?: boolean\n}\n\nexport interface ModelDownloadSnapshot {\n downloading: boolean\n loaded: number\n /** Sum of Content-Length values when every active transfer has one. */\n total: number | null\n}\n\nconst active = new Map<string, { loaded: number; total: number | null }>()\nconst listeners = new Set<(snapshot: ModelDownloadSnapshot) => void>()\nconst cache = new Map<string, ArrayBuffer>()\nconst inflight = new Map<string, Promise<ArrayBuffer>>()\n\nfunction contentLength(response: Response) {\n const parsed = Number(response.headers.get('content-length'))\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null\n}\n\nexport function modelDownloadSnapshot(): ModelDownloadSnapshot {\n if (active.size === 0) return { downloading: false, loaded: 0, total: null }\n let loaded = 0\n let total = 0\n let known = true\n for (const item of active.values()) {\n loaded += item.loaded\n if (item.total == null) known = false\n else total += item.total\n }\n return { downloading: true, loaded, total: known ? total : null }\n}\n\nfunction emit() {\n const snapshot = modelDownloadSnapshot()\n for (const listener of listeners) listener(snapshot)\n}\n\n/** Subscribing does not fetch. Downloads start only from `downloadModelBytes`. */\nexport function subscribeModelDownloads(listener: (snapshot: ModelDownloadSnapshot) => void) {\n listener(modelDownloadSnapshot())\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Progress reported from another realm, such as the document worker. */\nexport function reportModelDownload(url: string, progress: ModelDownloadProgress) {\n if (progress.done) active.delete(url)\n else active.set(url, { loaded: progress.loaded, total: progress.total })\n emit()\n}\n\nexport async function readResponseBytes(\n response: Response,\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n const total = contentLength(response)\n if (!response.body) {\n const bytes = await response.arrayBuffer()\n onProgress?.({ loaded: bytes.byteLength, total: total ?? bytes.byteLength })\n return bytes\n }\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value?.byteLength) continue\n chunks.push(value)\n loaded += value.byteLength\n onProgress?.({ loaded, total })\n }\n const bytes = new Uint8Array(loaded)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n if (loaded === 0) onProgress?.({ loaded: 0, total })\n return bytes.buffer\n}\n\n/**\n * Fetch a model file and report loaded/total while the body transfers.\n * A second call for the same URL reuses the cached bytes and does not fetch.\n */\nexport async function downloadModelBytes(url: string, init?: { cache?: RequestCache }) {\n const cached = cache.get(url)\n if (cached) return cached.slice(0)\n const existing = inflight.get(url)\n if (existing) return existing.then((bytes) => bytes.slice(0))\n const request = (async () => {\n reportModelDownload(url, { loaded: 0, total: null })\n const response = await fetch(url, { cache: init?.cache })\n if (!response.ok) {\n throw new ScannerError('model-http', `Model request failed with HTTP ${response.status}.`)\n }\n return readResponseBytes(response, (progress) => reportModelDownload(url, progress))\n })()\n inflight.set(url, request)\n try {\n const bytes = await request\n cache.set(url, bytes)\n return bytes.slice(0)\n } finally {\n inflight.delete(url)\n if (active.has(url)) {\n active.delete(url)\n emit()\n }\n }\n}\n","export interface PackagedOrtWasmPaths {\n wasm: string\n mjs: string\n}\n\n/**\n * Override for the packaged ONNX Runtime files.\n * A string is a directory that contains `ort-wasm-simd-threaded.jsep.wasm`\n * and `ort-wasm-simd-threaded.jsep.mjs`. An object sets each file URL.\n * Omit the option to use the copies shipped with the SDK.\n */\nexport type OrtWasmPaths = string | { wasm: string; mjs: string }\n\n/** Pinned onnxruntime-web version. Do not float `latest`. */\nexport const PINNED_ONNXRUNTIME_WEB_VERSION = '1.30.0' as const\n\nexport const PREFERRED_ORT_CDN_BASE =\n `https://exige-scanner-sdk.vercel.app/ort/${PINNED_ONNXRUNTIME_WEB_VERSION}/`\n\nexport const LAST_RESORT_ORT_CDN_BASE =\n `https://cdn.jsdelivr.net/npm/onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION}/dist/`\n\nexport const SIBLING_ORT_MODULE = 'onnxruntime-web'\nexport const ORT_JS_FILENAME = 'ort.min.mjs'\nexport const ORT_WASM_FILENAME = 'ort-wasm-simd-threaded.jsep.wasm'\nexport const ORT_WASM_MJS_FILENAME = 'ort-wasm-simd-threaded.jsep.mjs'\n\nexport type OrtModule = typeof import('onnxruntime-web')\nexport type OrtLoadSource = 'sibling' | 'preferred-cdn' | 'last-resort-cdn'\nexport type OrtImporter = (specifier: string) => Promise<OrtModule>\n\nexport function joinOrtCdnUrl(base: string, fileName: string): string {\n const root = base.endsWith('/') ? base : `${base}/`\n return `${root}${fileName}`\n}\n\nexport function siblingOrtUrl(workerHref: string): string {\n return new URL('./ort.min.js', workerHref).href\n}\n\nexport function pinnedOrtJsUrls(): readonly [string, string] {\n return [\n joinOrtCdnUrl(PREFERRED_ORT_CDN_BASE, ORT_JS_FILENAME),\n joinOrtCdnUrl(LAST_RESORT_ORT_CDN_BASE, ORT_JS_FILENAME),\n ]\n}\n\nexport function pinnedOrtWasmPaths(base: string = PREFERRED_ORT_CDN_BASE): PackagedOrtWasmPaths {\n return {\n wasm: joinOrtCdnUrl(base, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(base, ORT_WASM_MJS_FILENAME),\n }\n}\n\nexport function expandOrtWasmPaths(value: OrtWasmPaths): PackagedOrtWasmPaths {\n if (typeof value === 'string') {\n return {\n wasm: joinOrtCdnUrl(value, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(value, ORT_WASM_MJS_FILENAME),\n }\n }\n return { wasm: value.wasm, mjs: value.mjs }\n}\n\nfunction hasOrtWasmValue(value?: OrtWasmPaths): value is OrtWasmPaths {\n if (typeof value === 'string') return value.trim().length > 0\n return Boolean(value?.wasm.trim() && value.mjs.trim())\n}\n\nfunction isSuccessfulAssetResponse(response: Response): boolean {\n if (!response.ok && response.status !== 206) return false\n const type = response.headers.get('content-type') ?? ''\n return !type.toLowerCase().includes('text/html')\n}\n\n/** HEAD, then GET. Treat HTML (SPA fallback) and 404 as missing. */\nexport async function probeHttpUrl(\n url: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n if (!url) return false\n try {\n const head = await fetchImpl(url, { method: 'HEAD' })\n if (isSuccessfulAssetResponse(head)) return true\n if (head.status === 404 || head.status === 410) return false\n } catch {\n // Some hosts reject HEAD; try GET.\n }\n try {\n const get = await fetchImpl(url, {\n method: 'GET',\n headers: { Range: 'bytes=0-63' },\n })\n return isSuccessfulAssetResponse(get)\n } catch {\n return false\n }\n}\n\nexport async function probeOrtWasmPaths(\n value: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n const { wasm, mjs } = expandOrtWasmPaths(value)\n const [wasmOk, mjsOk] = await Promise.all([\n probeHttpUrl(wasm, fetchImpl),\n probeHttpUrl(mjs, fetchImpl),\n ])\n return wasmOk && mjsOk\n}\n\n/**\n * Prefer same-origin / caller wasm when those URLs exist.\n * A 404 override still falls back to the pinned CDN pair.\n */\nexport async function resolveUsableOrtWasmPaths(\n value?: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<OrtWasmPaths> {\n if (hasOrtWasmValue(value) && await probeOrtWasmPaths(value, fetchImpl)) {\n if (typeof value === 'string') return value.trim()\n return { wasm: value.wasm.trim(), mjs: value.mjs.trim() }\n }\n const preferred = pinnedOrtWasmPaths(PREFERRED_ORT_CDN_BASE)\n if (await probeOrtWasmPaths(preferred, fetchImpl)) return preferred\n return pinnedOrtWasmPaths(LAST_RESORT_ORT_CDN_BASE)\n}\n\nexport async function importOrtSpecifier(specifier: string): Promise<OrtModule> {\n return import(specifier) as Promise<OrtModule>\n}\n\nexport async function loadOrtJsFromPinnedCdn(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<OrtModule> {\n const errors: unknown[] = []\n for (const url of pinnedOrtJsUrls()) {\n try {\n return await importModule(url)\n } catch (error) {\n errors.push(error)\n }\n }\n throw new Error(\n `Failed to load onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION} from the pinned CDN.`,\n { cause: errors.at(-1) },\n )\n}\n\n/**\n * Sibling `onnxruntime-web` first (webpack copies `./ort.min.js`).\n * Next 15 turbo leaves that sibling missing, so use the pinned CDN.\n */\nexport async function loadOrtRuntime(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<{ ort: OrtModule; source: OrtLoadSource }> {\n try {\n return { ort: await importModule(SIBLING_ORT_MODULE), source: 'sibling' }\n } catch {\n // Next turbo emits the worker without a sibling ort.min.js.\n }\n const [preferred, lastResort] = pinnedOrtJsUrls()\n try {\n return { ort: await importModule(preferred), source: 'preferred-cdn' }\n } catch {\n return { ort: await importModule(lastResort), source: 'last-resort-cdn' }\n }\n}\n","import {\n decodeDocumentHeatmaps,\n preprocessDocumentModel,\n} from './document-ml'\nimport { ScannerError, type ScannerErrorCode } from './errors'\nimport { readResponseBytes, type ModelDownloadProgress } from './model-download'\nimport {\n loadOrtJsFromPinnedCdn,\n resolveUsableOrtWasmPaths,\n} from './ort-cdn'\nimport type { DocumentModelStatus } from './types'\nimport type { QuadCandidate } from './document-geometry'\n\ninterface PixelAnalysis {\n sharpness: number\n candidate?: QuadCandidate\n detector?: 'ml'\n modelStatus?: DocumentModelStatus\n modelProvider?: 'webgpu' | 'wasm'\n modelError?: string\n inferenceMs?: number\n}\n\ninterface AnalysisRequest {\n id: number\n pixels: ArrayBuffer\n width: number\n height: number\n modelUrl?: string\n heatmapThreshold?: number\n cacheModel?: boolean\n ortWasmPaths?: string | { wasm: string; mjs: string }\n}\n\ninterface AnalysisResponse {\n id: number\n result?: PixelAnalysis\n error?: string\n errorCode?: ScannerErrorCode\n download?: {\n url: string\n loaded: number\n total: number | null\n done?: boolean\n }\n}\n\ninterface WorkerScope {\n onmessage: ((event: MessageEvent<AnalysisRequest>) => void) | null\n postMessage: (message: AnalysisResponse) => void\n}\n\nconst workerScope = globalThis as unknown as WorkerScope\nconst modelBytes = new Map<string, Promise<ArrayBuffer>>()\nconst sessions = new Map<string, Promise<{\n session: import('onnxruntime-web').InferenceSession\n ort: typeof import('onnxruntime-web')\n provider: 'webgpu' | 'wasm'\n}>>()\n\nconst MODEL_DATABASE = 'exige-scanner-sdk-models'\nconst MODEL_STORE = 'model-bytes'\n\nfunction openModelDatabase() {\n return new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(MODEL_DATABASE, 1)\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(MODEL_STORE)) {\n request.result.createObjectStore(MODEL_STORE)\n }\n }\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n}\n\nasync function readCachedModel(modelUrl: string) {\n if (typeof indexedDB === 'undefined') return undefined\n const database = await openModelDatabase()\n try {\n return await new Promise<ArrayBuffer | undefined>((resolve, reject) => {\n const request = database.transaction(MODEL_STORE).objectStore(MODEL_STORE).get(modelUrl)\n request.onsuccess = () => resolve(\n request.result instanceof ArrayBuffer ? request.result : undefined,\n )\n request.onerror = () => reject(request.error)\n })\n } finally {\n database.close()\n }\n}\n\nasync function writeCachedModel(modelUrl: string, bytes: ArrayBuffer) {\n if (typeof indexedDB === 'undefined') return\n const database = await openModelDatabase()\n try {\n await new Promise<void>((resolve, reject) => {\n const transaction = database.transaction(MODEL_STORE, 'readwrite')\n transaction.objectStore(MODEL_STORE).put(bytes, modelUrl)\n transaction.oncomplete = () => resolve()\n transaction.onerror = () => reject(transaction.error)\n })\n } finally {\n database.close()\n }\n}\n\nasync function fetchModel(\n modelUrl: string,\n cacheModel: boolean,\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n let request = modelBytes.get(modelUrl)\n if (!request) {\n request = (async () => {\n if (cacheModel) {\n try {\n const cached = await readCachedModel(modelUrl)\n if (cached) return cached\n } catch {\n // IndexedDB may be unavailable in private/embedded contexts.\n }\n }\n onProgress?.({ loaded: 0, total: null })\n let bytes: ArrayBuffer\n try {\n const response = await fetch(modelUrl, {\n cache: cacheModel ? 'force-cache' : 'no-store',\n })\n if (!response.ok) {\n throw new ScannerError(\n 'model-http',\n `Document model request failed with HTTP ${response.status}.`,\n )\n }\n bytes = await readResponseBytes(response, onProgress)\n } catch (error) {\n onProgress?.({ loaded: 0, total: null, done: true })\n throw error\n }\n onProgress?.({ loaded: bytes.byteLength, total: bytes.byteLength, done: true })\n if (cacheModel) {\n try {\n await writeCachedModel(modelUrl, bytes)\n } catch {\n // HTTP caching and the in-worker promise cache still apply.\n }\n }\n return bytes\n })()\n modelBytes.set(modelUrl, request)\n }\n return request\n}\n\nfunction ortWasmKey(ortWasmPaths?: string | { wasm: string; mjs: string }) {\n if (!ortWasmPaths) return ''\n if (typeof ortWasmPaths === 'string') return ortWasmPaths\n return `${ortWasmPaths.wasm}|${ortWasmPaths.mjs}`\n}\n\nasync function loadOrtModule() {\n try {\n return await import('onnxruntime-web')\n } catch {\n return loadOrtJsFromPinnedCdn()\n }\n}\n\nasync function loadSession(\n modelUrl: string,\n cacheModel: boolean,\n ortWasmPaths?: string | { wasm: string; mjs: string },\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n const sessionKey = `${modelUrl}|${ortWasmKey(ortWasmPaths)}`\n let request = sessions.get(sessionKey)\n if (!request) {\n request = (async () => {\n const [ort, bytes] = await Promise.all([\n loadOrtModule(),\n fetchModel(modelUrl, cacheModel, onProgress),\n ])\n ort.env.wasm.wasmPaths = await resolveUsableOrtWasmPaths(ortWasmPaths)\n const hasWebGpu = typeof navigator !== 'undefined' &&\n 'gpu' in (navigator as Navigator & { gpu?: unknown })\n if (hasWebGpu) {\n try {\n const session = await ort.InferenceSession.create(bytes, {\n executionProviders: ['webgpu'],\n graphOptimizationLevel: 'all',\n })\n return { session, ort, provider: 'webgpu' as const }\n } catch {\n // Fall through to portable WASM.\n }\n }\n const session = await ort.InferenceSession.create(bytes, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n return { session, ort, provider: 'wasm' as const }\n })()\n sessions.set(sessionKey, request)\n }\n return request\n}\n\nasync function analyzeWithModel(request: AnalysisRequest): Promise<PixelAnalysis> {\n if (!request.modelUrl) {\n throw new Error('A DocAligner modelUrl is required.')\n }\n const pixels = new Uint8ClampedArray(request.pixels)\n const input = preprocessDocumentModel(pixels, request.width, request.height)\n const { session, ort, provider } = await loadSession(\n request.modelUrl,\n request.cacheModel !== false,\n request.ortWasmPaths,\n (progress) => {\n workerScope.postMessage({\n id: request.id,\n download: { url: request.modelUrl ?? '', ...progress },\n })\n },\n )\n const startedAt = performance.now()\n const outputs = await session.run({\n [session.inputNames[0]]: new ort.Tensor('float32', input.data, input.dimensions),\n })\n const tensor = outputs[session.outputNames[0]]\n if (!tensor) throw new Error('Document model returned no output tensor.')\n const decoded = decodeDocumentHeatmaps(\n tensor.data as Float32Array,\n tensor.dims.map(Number),\n request.width,\n request.height,\n request.heatmapThreshold ?? 0.3,\n )\n return {\n sharpness: 20,\n candidate: decoded.candidate,\n detector: 'ml',\n modelStatus: 'ready',\n modelProvider: provider,\n inferenceMs: performance.now() - startedAt,\n }\n}\n\nasync function analyze(request: AnalysisRequest): Promise<PixelAnalysis> {\n try {\n const ml = await analyzeWithModel(request)\n if (!ml.candidate) {\n return {\n ...ml,\n modelStatus: 'failed',\n modelError: 'ML detector returned no geometrically valid corners.',\n }\n }\n return ml\n } catch (error) {\n if (error instanceof ScannerError && error.code === 'model-http') throw error\n const message = error instanceof Error ? error.message : 'Document model failed.'\n return {\n sharpness: 20,\n detector: 'ml',\n modelStatus: 'failed',\n modelError: message,\n }\n }\n}\n\nlet analysisQueue = Promise.resolve()\nworkerScope.onmessage = (event) => {\n const request = event.data\n analysisQueue = analysisQueue.then(async () => {\n try {\n workerScope.postMessage({ id: request.id, result: await analyze(request) })\n } catch (error) {\n workerScope.postMessage({\n id: request.id,\n error: error instanceof Error ? error.message : 'Document analysis failed.',\n errorCode: error instanceof ScannerError ? error.code : undefined,\n })\n }\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"document-worker.js","names":[],"sources":["../../src/sdk/document-geometry.ts","../../src/sdk/document-ml.ts","../../src/sdk/errors.ts","../../src/sdk/model-download.ts","../../src/sdk/ort-cdn.ts","../../src/sdk/document-worker.ts"],"sourcesContent":["import type { DocumentCorners, Point } from './types'\n\nexport interface QuadCandidate {\n corners: DocumentCorners\n confidence: number\n areaRatio: number\n}\n\nexport interface PerspectiveQuadScore {\n total: number\n areaRatio: number\n angleScore: number\n oppositeAngleScore: number\n parallelScore: number\n homographyScore: number\n aspectBoost: number\n}\n\nfunction cross(origin: Point, a: Point, b: Point) {\n return (a.x - origin.x) * (b.y - origin.y) - (a.y - origin.y) * (b.x - origin.x)\n}\n\nexport function polygonArea(points: Point[]) {\n let sum = 0\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index]\n const next = points[(index + 1) % points.length]\n sum += current.x * next.y - next.x * current.y\n }\n return Math.abs(sum) / 2\n}\n\nexport function orderCorners(points: Point[]): DocumentCorners | undefined {\n if (points.length < 4) return undefined\n const topLeft = points.reduce((best, point) => point.x + point.y < best.x + best.y ? point : best)\n const bottomRight = points.reduce((best, point) => point.x + point.y > best.x + best.y ? point : best)\n const topRight = points.reduce((best, point) => point.x - point.y > best.x - best.y ? point : best)\n const bottomLeft = points.reduce((best, point) => point.x - point.y < best.x - best.y ? point : best)\n const unique = new Set([topLeft, topRight, bottomRight, bottomLeft])\n if (unique.size !== 4) return undefined\n return { topLeft, topRight, bottomRight, bottomLeft }\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value))\n}\n\nfunction distance(a: Point, b: Point) {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n\nfunction interiorAngle(previous: Point, vertex: Point, next: Point) {\n const ax = previous.x - vertex.x\n const ay = previous.y - vertex.y\n const bx = next.x - vertex.x\n const by = next.y - vertex.y\n const denominator = Math.max(1e-6, Math.hypot(ax, ay) * Math.hypot(bx, by))\n return Math.acos(Math.max(-1, Math.min(1, (ax * bx + ay * by) / denominator))) * 180 / Math.PI\n}\n\nfunction directionDifference(a: Point, b: Point, c: Point, d: Point) {\n const first = Math.atan2(b.y - a.y, b.x - a.x)\n const second = Math.atan2(d.y - c.y, d.x - c.x)\n let difference = Math.abs(first - second) % Math.PI\n if (difference > Math.PI / 2) difference = Math.PI - difference\n return difference\n}\n\nfunction unitToQuadCoefficients(corners: DocumentCorners) {\n const { topLeft: tl, topRight: tr, bottomRight: br, bottomLeft: bl } = corners\n const dx1 = tr.x - br.x\n const dx2 = bl.x - br.x\n const dy1 = tr.y - br.y\n const dy2 = bl.y - br.y\n const sx = tl.x - tr.x + br.x - bl.x\n const sy = tl.y - tr.y + br.y - bl.y\n const denominator = dx1 * dy2 - dx2 * dy1\n const g = Math.abs(denominator) < 1e-6 ? 0 : (sx * dy2 - dx2 * sy) / denominator\n const h = Math.abs(denominator) < 1e-6 ? 0 : (dx1 * sy - sx * dy1) / denominator\n return {\n a: tr.x - tl.x + g * tr.x,\n b: bl.x - tl.x + h * bl.x,\n c: tl.x,\n d: tr.y - tl.y + g * tr.y,\n e: bl.y - tl.y + h * bl.y,\n f: tl.y,\n g,\n h,\n }\n}\n\nfunction quadToUnit(point: Point, coefficients: ReturnType<typeof unitToQuadCoefficients>) {\n const { a, b, c, d, e, f, g, h } = coefficients\n const aa = a - point.x * g\n const bb = b - point.x * h\n const dd = d - point.y * g\n const ee = e - point.y * h\n const xx = point.x - c\n const yy = point.y - f\n const determinant = aa * ee - bb * dd\n if (Math.abs(determinant) < 1e-8) return undefined\n return {\n x: (xx * ee - bb * yy) / determinant,\n y: (aa * yy - xx * dd) / determinant,\n }\n}\n\nfunction homographyBoundaryScore(corners: DocumentCorners, hull: Point[]) {\n if (!hull.length) return 0\n const coefficients = unitToQuadCoefficients(corners)\n let squaredResidual = 0\n let samples = 0\n const stride = Math.max(1, Math.floor(hull.length / 80))\n for (let index = 0; index < hull.length; index += stride) {\n const unit = quadToUnit(hull[index], coefficients)\n if (!unit) return 0\n const boundaryDistance = Math.min(\n Math.abs(unit.x),\n Math.abs(1 - unit.x),\n Math.abs(unit.y),\n Math.abs(1 - unit.y),\n )\n const outside = Math.max(0, -unit.x, unit.x - 1, -unit.y, unit.y - 1)\n const residual = boundaryDistance + outside * 2\n squaredResidual += residual * residual\n samples += 1\n }\n const rms = Math.sqrt(squaredResidual / Math.max(1, samples))\n return Math.exp(-rms / 0.08)\n}\n\nexport function scorePerspectiveQuad(\n corners: DocumentCorners,\n frameWidth: number,\n frameHeight: number,\n hull: Point[] = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft],\n): PerspectiveQuadScore {\n const ordered = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft]\n const frameScale = Math.max(1, Math.min(frameWidth, frameHeight))\n const outsideRatio = Math.max(\n 0,\n ...ordered.map((point) => Math.max(\n -point.x,\n -point.y,\n point.x - frameWidth,\n point.y - frameHeight,\n ) / frameScale),\n )\n const signedCrosses = ordered.map((point, index) =>\n cross(point, ordered[(index + 1) % 4], ordered[(index + 2) % 4]),\n )\n const convex = signedCrosses.every((value) => value > 0) ||\n signedCrosses.every((value) => value < 0)\n const areaRatio = polygonArea(ordered) / Math.max(1, frameWidth * frameHeight)\n if (\n !convex ||\n areaRatio < 0.04 ||\n areaRatio > 0.995 ||\n outsideRatio > 0.12\n ) {\n return {\n total: 0,\n areaRatio,\n angleScore: 0,\n oppositeAngleScore: 0,\n parallelScore: 0,\n homographyScore: 0,\n aspectBoost: 0,\n }\n }\n\n const angles = ordered.map((point, index) =>\n interiorAngle(ordered[(index + 3) % 4], point, ordered[(index + 1) % 4]),\n )\n const edgeLengths = ordered.map((point, index) => distance(point, ordered[(index + 1) % 4]))\n const horizontalScaleRatio = Math.max(edgeLengths[0], edgeLengths[2]) /\n Math.max(1, Math.min(edgeLengths[0], edgeLengths[2]))\n const verticalScaleRatio = Math.max(edgeLengths[1], edgeLengths[3]) /\n Math.max(1, Math.min(edgeLengths[1], edgeLengths[3]))\n if (\n angles.some((angle) => angle < 18 || angle > 162) ||\n horizontalScaleRatio > 4 ||\n verticalScaleRatio > 4\n ) {\n return {\n total: 0,\n areaRatio,\n angleScore: 0,\n oppositeAngleScore: 0,\n parallelScore: 0,\n homographyScore: 0,\n aspectBoost: 0,\n }\n }\n const angleScore = angles.reduce(\n (sum, angle) => sum + Math.exp(-Math.abs(angle - 90) / 48),\n 0,\n ) / 4\n const oppositeAngleScore = (\n Math.exp(-Math.abs(angles[0] + angles[2] - 180) / 55) +\n Math.exp(-Math.abs(angles[1] + angles[3] - 180) / 55)\n ) / 2\n const parallelScore = (\n Math.exp(-directionDifference(ordered[0], ordered[1], ordered[3], ordered[2]) / 0.62) +\n Math.exp(-directionDifference(ordered[0], ordered[3], ordered[1], ordered[2]) / 0.62)\n ) / 2\n const homographyScore = homographyBoundaryScore(corners, hull)\n const areaScore = clamp01((areaRatio - 0.04) / 0.5) *\n (areaRatio > 0.94 ? clamp01((0.995 - areaRatio) / 0.055) : 1)\n const perspectiveScaleScore = Math.exp(\n -Math.max(0, Math.max(horizontalScaleRatio, verticalScaleRatio) - 1.6) / 1.5,\n )\n const frameVisibilityScore = outsideRatio ? Math.exp(-outsideRatio / 0.07) : 1\n const width = Math.max(distance(ordered[0], ordered[1]), distance(ordered[3], ordered[2]))\n const height = Math.max(distance(ordered[0], ordered[3]), distance(ordered[1], ordered[2]))\n const aspect = Math.max(width, height) / Math.max(1, Math.min(width, height))\n const idBoost = Math.exp(-Math.abs(aspect - 1.58) / 0.18)\n const letterBoost = Math.exp(-Math.abs(aspect - 1.29) / 0.18)\n const receiptBoost = aspect >= 2 ? clamp01((aspect - 2) / 2) : 0\n const aspectBoost = Math.max(idBoost, letterBoost, receiptBoost) * 0.08\n const geometricScore = clamp01(\n areaScore * 0.18 +\n angleScore * 0.2 +\n oppositeAngleScore * 0.14 +\n parallelScore * 0.18 +\n homographyScore * 0.3 +\n aspectBoost\n )\n const total = geometricScore * (0.72 + perspectiveScaleScore * 0.28) * frameVisibilityScore\n return {\n total,\n areaRatio,\n angleScore,\n oppositeAngleScore,\n parallelScore,\n homographyScore,\n aspectBoost,\n }\n}\n","import { orderCorners, scorePerspectiveQuad, type QuadCandidate } from './document-geometry'\nimport type { Point } from './types'\n\nexport interface DocumentModelInput {\n data: Float32Array\n dimensions: [1, 3, number, number]\n}\n\nexport interface DecodedDocumentHeatmaps {\n candidate?: QuadCandidate\n cornerConfidences: number[]\n}\n\nexport function preprocessDocumentModel(\n pixels: Uint8ClampedArray,\n width: number,\n height: number,\n inputSize = 256,\n): DocumentModelInput {\n const planeSize = inputSize * inputSize\n const data = new Float32Array(planeSize * 3)\n for (let y = 0; y < inputSize; y += 1) {\n const sourceY = Math.min(height - 1, Math.round((y + 0.5) * height / inputSize - 0.5))\n for (let x = 0; x < inputSize; x += 1) {\n const sourceX = Math.min(width - 1, Math.round((x + 0.5) * width / inputSize - 0.5))\n const sourceOffset = (sourceY * width + sourceX) * 4\n const targetOffset = y * inputSize + x\n data[targetOffset] = pixels[sourceOffset] / 255\n data[planeSize + targetOffset] = pixels[sourceOffset + 1] / 255\n data[planeSize * 2 + targetOffset] = pixels[sourceOffset + 2] / 255\n }\n }\n return { data, dimensions: [1, 3, inputSize, inputSize] }\n}\n\nfunction largestHeatmapComponent(\n values: Float32Array,\n base: number,\n width: number,\n height: number,\n threshold: number,\n) {\n const visited = new Uint8Array(width * height)\n const queue = new Int32Array(width * height)\n let bestCount = 0\n let bestPoint: Point | undefined\n let bestPeak = 0\n for (let seed = 0; seed < width * height; seed += 1) {\n if (visited[seed] || values[base + seed] < threshold) continue\n let read = 0\n let write = 0\n let weightedX = 0\n let weightedY = 0\n let totalWeight = 0\n let peak = 0\n queue[write++] = seed\n visited[seed] = 1\n while (read < write) {\n const index = queue[read++]\n const x = index % width\n const y = Math.floor(index / width)\n const weight = Math.max(0, values[base + index])\n weightedX += (x + 0.5) * weight\n weightedY += (y + 0.5) * weight\n totalWeight += weight\n peak = Math.max(peak, weight)\n const neighbors = [index - 1, index + 1, index - width, index + width]\n for (const neighbor of neighbors) {\n if (\n neighbor < 0 ||\n neighbor >= width * height ||\n visited[neighbor] ||\n values[base + neighbor] < threshold\n ) continue\n const neighborX = neighbor % width\n if (Math.abs(neighborX - x) > 1) continue\n visited[neighbor] = 1\n queue[write++] = neighbor\n }\n }\n if (write > bestCount && totalWeight > 0) {\n bestCount = write\n bestPeak = peak\n bestPoint = {\n x: weightedX / totalWeight,\n y: weightedY / totalWeight,\n }\n }\n }\n return bestPoint ? { point: bestPoint, peak: bestPeak } : undefined\n}\n\nexport function decodeDocumentHeatmaps(\n heatmaps: Float32Array,\n dimensions: readonly number[],\n imageWidth: number,\n imageHeight: number,\n threshold = 0.3,\n): DecodedDocumentHeatmaps {\n if (\n dimensions.length !== 4 ||\n Number(dimensions[0]) !== 1 ||\n Number(dimensions[1]) !== 4\n ) {\n throw new Error(`Expected document heatmaps shaped [1,4,H,W], got [${dimensions.join(',')}].`)\n }\n const heatmapHeight = Number(dimensions[2])\n const heatmapWidth = Number(dimensions[3])\n const planeSize = heatmapWidth * heatmapHeight\n if (heatmaps.length < planeSize * 4 || !heatmapWidth || !heatmapHeight) {\n throw new Error('Document heatmap tensor is incomplete.')\n }\n const points: Point[] = []\n const cornerConfidences: number[] = []\n for (let channel = 0; channel < 4; channel += 1) {\n const component = largestHeatmapComponent(\n heatmaps,\n channel * planeSize,\n heatmapWidth,\n heatmapHeight,\n threshold,\n )\n if (!component) return { cornerConfidences }\n points.push({\n x: Math.max(0, Math.min(imageWidth - 1, component.point.x / heatmapWidth * imageWidth)),\n y: Math.max(0, Math.min(imageHeight - 1, component.point.y / heatmapHeight * imageHeight)),\n })\n cornerConfidences.push(component.peak)\n }\n const corners = orderCorners(points)\n if (!corners) return { cornerConfidences }\n const geometry = scorePerspectiveQuad(corners, imageWidth, imageHeight)\n if (geometry.total < 0.35) return { cornerConfidences }\n const heatmapConfidence = cornerConfidences.reduce((sum, value) => sum + value, 0) / 4\n return {\n candidate: {\n corners,\n areaRatio: geometry.areaRatio,\n confidence: Math.min(0.99, heatmapConfidence * 0.55 + geometry.total * 0.45),\n },\n cornerConfidences,\n }\n}\n","/**\n * Operational failure from the scanner runtime.\n *\n * Scan quality stays a returned status (`success`, `no-document`,\n * `low-confidence`, or `low-quality`). Redaction match quality stays\n * `success`, `partial`, or `unmatched`. Neither of those outcomes uses this error.\n * A bad image does not throw.\n */\nexport type ScannerErrorCode =\n | 'secure-context'\n | 'worker'\n | 'model-http'\n | 'language'\n | 'canvas'\n\nexport class ScannerError extends Error {\n readonly code: ScannerErrorCode\n\n constructor(code: ScannerErrorCode, message: string) {\n super(message)\n this.name = 'ScannerError'\n this.code = code\n }\n}\n\nexport function canvasUnavailable(): ScannerError {\n return new ScannerError('canvas', 'Canvas 2D is unavailable in this browser.')\n}\n","import { ScannerError } from './errors'\n\nexport interface ModelDownloadProgress {\n loaded: number\n /** Byte length from Content-Length. Null when that header is absent. */\n total: number | null\n done?: boolean\n}\n\nexport interface ModelDownloadSnapshot {\n downloading: boolean\n loaded: number\n /** Sum of Content-Length values when every active transfer has one. */\n total: number | null\n}\n\nconst STORE = Symbol.for('exige.web-scanner-sdk.model-downloads')\n\ninterface ModelDownloadStore {\n active: Map<string, { loaded: number; total: number | null }>\n listeners: Set<(snapshot: ModelDownloadSnapshot) => void>\n cache: Map<string, ArrayBuffer>\n inflight: Map<string, Promise<ArrayBuffer>>\n}\n\n/** One store across the package entry and the React entry. */\nfunction store(): ModelDownloadStore {\n const scope = globalThis as typeof globalThis & { [STORE]?: ModelDownloadStore }\n if (!scope[STORE]) {\n scope[STORE] = {\n active: new Map(),\n listeners: new Set(),\n cache: new Map(),\n inflight: new Map(),\n }\n }\n return scope[STORE]\n}\n\nconst active = store().active\nconst listeners = store().listeners\nconst cache = store().cache\nconst inflight = store().inflight\n\nfunction contentLength(response: Response) {\n const parsed = Number(response.headers.get('content-length'))\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null\n}\n\nexport function modelDownloadSnapshot(): ModelDownloadSnapshot {\n if (active.size === 0) return { downloading: false, loaded: 0, total: null }\n let loaded = 0\n let total = 0\n let known = true\n for (const item of active.values()) {\n loaded += item.loaded\n if (item.total == null) known = false\n else total += item.total\n }\n return { downloading: true, loaded, total: known ? total : null }\n}\n\nfunction emit() {\n const snapshot = modelDownloadSnapshot()\n for (const listener of listeners) listener(snapshot)\n}\n\n/** Subscribing does not fetch. The document worker and `downloadModelBytes` report into the same snapshot. */\nexport function subscribeModelDownloads(listener: (snapshot: ModelDownloadSnapshot) => void) {\n listener(modelDownloadSnapshot())\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Progress reported from another realm, such as the document worker. */\nexport function reportModelDownload(url: string, progress: ModelDownloadProgress) {\n if (progress.done) active.delete(url)\n else active.set(url, { loaded: progress.loaded, total: progress.total })\n emit()\n}\n\nexport async function readResponseBytes(\n response: Response,\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n const total = contentLength(response)\n if (!response.body) {\n const bytes = await response.arrayBuffer()\n onProgress?.({ loaded: bytes.byteLength, total: total ?? bytes.byteLength })\n return bytes\n }\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value?.byteLength) continue\n chunks.push(value)\n loaded += value.byteLength\n onProgress?.({ loaded, total })\n }\n const bytes = new Uint8Array(loaded)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n if (loaded === 0) onProgress?.({ loaded: 0, total })\n return bytes.buffer\n}\n\n/**\n * Fetch a model file and report loaded/total while the body transfers.\n * A second call for the same URL reuses the cached bytes and does not fetch.\n */\nexport async function downloadModelBytes(url: string, init?: { cache?: RequestCache }) {\n const cached = cache.get(url)\n if (cached) return cached.slice(0)\n const existing = inflight.get(url)\n if (existing) return existing.then((bytes) => bytes.slice(0))\n const request = (async () => {\n reportModelDownload(url, { loaded: 0, total: null })\n const response = await fetch(url, { cache: init?.cache })\n if (!response.ok) {\n throw new ScannerError('model-http', `Model request failed with HTTP ${response.status}.`)\n }\n return readResponseBytes(response, (progress) => reportModelDownload(url, progress))\n })()\n inflight.set(url, request)\n try {\n const bytes = await request\n cache.set(url, bytes)\n return bytes.slice(0)\n } finally {\n inflight.delete(url)\n if (active.has(url)) {\n active.delete(url)\n emit()\n }\n }\n}\n","export interface PackagedOrtWasmPaths {\n wasm: string\n mjs: string\n}\n\n/**\n * Override for the packaged ONNX Runtime files.\n * A string is a directory that contains `ort-wasm-simd-threaded.jsep.wasm`\n * and `ort-wasm-simd-threaded.jsep.mjs`. An object sets each file URL.\n * Omit the option to use the copies shipped with the SDK.\n */\nexport type OrtWasmPaths = string | { wasm: string; mjs: string }\n\n/** Pinned onnxruntime-web version. Do not float `latest`. */\nexport const PINNED_ONNXRUNTIME_WEB_VERSION = '1.30.0' as const\n\nexport const PREFERRED_ORT_CDN_BASE =\n `https://exige-scanner-sdk.vercel.app/ort/${PINNED_ONNXRUNTIME_WEB_VERSION}/`\n\nexport const LAST_RESORT_ORT_CDN_BASE =\n `https://cdn.jsdelivr.net/npm/onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION}/dist/`\n\nexport const SIBLING_ORT_MODULE = 'onnxruntime-web'\nexport const ORT_JS_FILENAME = 'ort.min.mjs'\nexport const ORT_WASM_FILENAME = 'ort-wasm-simd-threaded.jsep.wasm'\nexport const ORT_WASM_MJS_FILENAME = 'ort-wasm-simd-threaded.jsep.mjs'\n\nexport type OrtModule = typeof import('onnxruntime-web')\nexport type OrtLoadSource = 'sibling' | 'preferred-cdn' | 'last-resort-cdn'\nexport type OrtImporter = (specifier: string) => Promise<OrtModule>\n\nexport function joinOrtCdnUrl(base: string, fileName: string): string {\n const root = base.endsWith('/') ? base : `${base}/`\n return `${root}${fileName}`\n}\n\nexport function siblingOrtUrl(workerHref: string): string {\n return new URL('./ort.min.js', workerHref).href\n}\n\nexport function pinnedOrtJsUrls(): readonly [string, string] {\n return [\n joinOrtCdnUrl(PREFERRED_ORT_CDN_BASE, ORT_JS_FILENAME),\n joinOrtCdnUrl(LAST_RESORT_ORT_CDN_BASE, ORT_JS_FILENAME),\n ]\n}\n\nexport function pinnedOrtWasmPaths(base: string = PREFERRED_ORT_CDN_BASE): PackagedOrtWasmPaths {\n return {\n wasm: joinOrtCdnUrl(base, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(base, ORT_WASM_MJS_FILENAME),\n }\n}\n\nexport function expandOrtWasmPaths(value: OrtWasmPaths): PackagedOrtWasmPaths {\n if (typeof value === 'string') {\n return {\n wasm: joinOrtCdnUrl(value, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(value, ORT_WASM_MJS_FILENAME),\n }\n }\n return { wasm: value.wasm, mjs: value.mjs }\n}\n\nfunction hasOrtWasmValue(value?: OrtWasmPaths): value is OrtWasmPaths {\n if (typeof value === 'string') return value.trim().length > 0\n return Boolean(value?.wasm.trim() && value.mjs.trim())\n}\n\nfunction isSuccessfulAssetResponse(response: Response): boolean {\n if (!response.ok && response.status !== 206) return false\n const type = response.headers.get('content-type') ?? ''\n return !type.toLowerCase().includes('text/html')\n}\n\n/** HEAD, then GET. Treat HTML (SPA fallback) and 404 as missing. */\nexport async function probeHttpUrl(\n url: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n if (!url) return false\n try {\n const head = await fetchImpl(url, { method: 'HEAD' })\n if (isSuccessfulAssetResponse(head)) return true\n if (head.status === 404 || head.status === 410) return false\n } catch {\n // Some hosts reject HEAD; try GET.\n }\n try {\n const get = await fetchImpl(url, {\n method: 'GET',\n headers: { Range: 'bytes=0-63' },\n })\n return isSuccessfulAssetResponse(get)\n } catch {\n return false\n }\n}\n\nexport async function probeOrtWasmPaths(\n value: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n const { wasm, mjs } = expandOrtWasmPaths(value)\n const [wasmOk, mjsOk] = await Promise.all([\n probeHttpUrl(wasm, fetchImpl),\n probeHttpUrl(mjs, fetchImpl),\n ])\n return wasmOk && mjsOk\n}\n\n/**\n * Prefer same-origin / caller wasm when those URLs exist.\n * A 404 override still falls back to the pinned CDN pair.\n */\nexport async function resolveUsableOrtWasmPaths(\n value?: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<OrtWasmPaths> {\n if (hasOrtWasmValue(value) && await probeOrtWasmPaths(value, fetchImpl)) {\n if (typeof value === 'string') return value.trim()\n return { wasm: value.wasm.trim(), mjs: value.mjs.trim() }\n }\n const preferred = pinnedOrtWasmPaths(PREFERRED_ORT_CDN_BASE)\n if (await probeOrtWasmPaths(preferred, fetchImpl)) return preferred\n return pinnedOrtWasmPaths(LAST_RESORT_ORT_CDN_BASE)\n}\n\nexport async function importOrtSpecifier(specifier: string): Promise<OrtModule> {\n return import(specifier) as Promise<OrtModule>\n}\n\nexport async function loadOrtJsFromPinnedCdn(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<OrtModule> {\n const errors: unknown[] = []\n for (const url of pinnedOrtJsUrls()) {\n try {\n return await importModule(url)\n } catch (error) {\n errors.push(error)\n }\n }\n throw new Error(\n `Failed to load onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION} from the pinned CDN.`,\n { cause: errors.at(-1) },\n )\n}\n\n/**\n * Sibling `onnxruntime-web` first (webpack copies `./ort.min.js`).\n * Next 15 turbo leaves that sibling missing, so use the pinned CDN.\n */\nexport async function loadOrtRuntime(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<{ ort: OrtModule; source: OrtLoadSource }> {\n try {\n return { ort: await importModule(SIBLING_ORT_MODULE), source: 'sibling' }\n } catch {\n // Next turbo emits the worker without a sibling ort.min.js.\n }\n const [preferred, lastResort] = pinnedOrtJsUrls()\n try {\n return { ort: await importModule(preferred), source: 'preferred-cdn' }\n } catch {\n return { ort: await importModule(lastResort), source: 'last-resort-cdn' }\n }\n}\n","import {\n decodeDocumentHeatmaps,\n preprocessDocumentModel,\n} from './document-ml'\nimport { ScannerError, type ScannerErrorCode } from './errors'\nimport { readResponseBytes, type ModelDownloadProgress } from './model-download'\nimport {\n loadOrtJsFromPinnedCdn,\n resolveUsableOrtWasmPaths,\n} from './ort-cdn'\nimport type { DocumentModelStatus } from './types'\nimport type { QuadCandidate } from './document-geometry'\n\ninterface PixelAnalysis {\n sharpness: number\n candidate?: QuadCandidate\n detector?: 'ml'\n modelStatus?: DocumentModelStatus\n modelProvider?: 'webgpu' | 'wasm'\n modelError?: string\n inferenceMs?: number\n}\n\ninterface AnalysisRequest {\n id: number\n pixels: ArrayBuffer\n width: number\n height: number\n modelUrl?: string\n heatmapThreshold?: number\n cacheModel?: boolean\n ortWasmPaths?: string | { wasm: string; mjs: string }\n}\n\ninterface AnalysisResponse {\n id: number\n result?: PixelAnalysis\n error?: string\n errorCode?: ScannerErrorCode\n download?: {\n url: string\n loaded: number\n total: number | null\n done?: boolean\n }\n}\n\ninterface WorkerScope {\n onmessage: ((event: MessageEvent<AnalysisRequest>) => void) | null\n postMessage: (message: AnalysisResponse) => void\n}\n\nconst workerScope = globalThis as unknown as WorkerScope\nconst modelBytes = new Map<string, Promise<ArrayBuffer>>()\nconst sessions = new Map<string, Promise<{\n session: import('onnxruntime-web').InferenceSession\n ort: typeof import('onnxruntime-web')\n provider: 'webgpu' | 'wasm'\n}>>()\n\nconst MODEL_DATABASE = 'exige-scanner-sdk-models'\nconst MODEL_STORE = 'model-bytes'\n\nfunction openModelDatabase() {\n return new Promise<IDBDatabase>((resolve, reject) => {\n const request = indexedDB.open(MODEL_DATABASE, 1)\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(MODEL_STORE)) {\n request.result.createObjectStore(MODEL_STORE)\n }\n }\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error)\n })\n}\n\nasync function readCachedModel(modelUrl: string) {\n if (typeof indexedDB === 'undefined') return undefined\n const database = await openModelDatabase()\n try {\n return await new Promise<ArrayBuffer | undefined>((resolve, reject) => {\n const request = database.transaction(MODEL_STORE).objectStore(MODEL_STORE).get(modelUrl)\n request.onsuccess = () => resolve(\n request.result instanceof ArrayBuffer ? request.result : undefined,\n )\n request.onerror = () => reject(request.error)\n })\n } finally {\n database.close()\n }\n}\n\nasync function writeCachedModel(modelUrl: string, bytes: ArrayBuffer) {\n if (typeof indexedDB === 'undefined') return\n const database = await openModelDatabase()\n try {\n await new Promise<void>((resolve, reject) => {\n const transaction = database.transaction(MODEL_STORE, 'readwrite')\n transaction.objectStore(MODEL_STORE).put(bytes, modelUrl)\n transaction.oncomplete = () => resolve()\n transaction.onerror = () => reject(transaction.error)\n })\n } finally {\n database.close()\n }\n}\n\nasync function fetchModel(\n modelUrl: string,\n cacheModel: boolean,\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n let request = modelBytes.get(modelUrl)\n if (!request) {\n request = (async () => {\n if (cacheModel) {\n try {\n const cached = await readCachedModel(modelUrl)\n if (cached) return cached\n } catch {\n // IndexedDB may be unavailable in private/embedded contexts.\n }\n }\n onProgress?.({ loaded: 0, total: null })\n let bytes: ArrayBuffer\n try {\n const response = await fetch(modelUrl, {\n cache: cacheModel ? 'force-cache' : 'no-store',\n })\n if (!response.ok) {\n throw new ScannerError(\n 'model-http',\n `Document model request failed with HTTP ${response.status}.`,\n )\n }\n bytes = await readResponseBytes(response, onProgress)\n } catch (error) {\n onProgress?.({ loaded: 0, total: null, done: true })\n throw error\n }\n onProgress?.({ loaded: bytes.byteLength, total: bytes.byteLength, done: true })\n if (cacheModel) {\n try {\n await writeCachedModel(modelUrl, bytes)\n } catch {\n // HTTP caching and the in-worker promise cache still apply.\n }\n }\n return bytes\n })()\n modelBytes.set(modelUrl, request)\n }\n return request\n}\n\nfunction ortWasmKey(ortWasmPaths?: string | { wasm: string; mjs: string }) {\n if (!ortWasmPaths) return ''\n if (typeof ortWasmPaths === 'string') return ortWasmPaths\n return `${ortWasmPaths.wasm}|${ortWasmPaths.mjs}`\n}\n\nasync function loadOrtModule() {\n try {\n return await import('onnxruntime-web')\n } catch {\n return loadOrtJsFromPinnedCdn()\n }\n}\n\nasync function loadSession(\n modelUrl: string,\n cacheModel: boolean,\n ortWasmPaths?: string | { wasm: string; mjs: string },\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n const sessionKey = `${modelUrl}|${ortWasmKey(ortWasmPaths)}`\n let request = sessions.get(sessionKey)\n if (!request) {\n request = (async () => {\n const [ort, bytes] = await Promise.all([\n loadOrtModule(),\n fetchModel(modelUrl, cacheModel, onProgress),\n ])\n ort.env.wasm.wasmPaths = await resolveUsableOrtWasmPaths(ortWasmPaths)\n const hasWebGpu = typeof navigator !== 'undefined' &&\n 'gpu' in (navigator as Navigator & { gpu?: unknown })\n if (hasWebGpu) {\n try {\n const session = await ort.InferenceSession.create(bytes, {\n executionProviders: ['webgpu'],\n graphOptimizationLevel: 'all',\n })\n return { session, ort, provider: 'webgpu' as const }\n } catch {\n // Fall through to portable WASM.\n }\n }\n const session = await ort.InferenceSession.create(bytes, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n return { session, ort, provider: 'wasm' as const }\n })()\n sessions.set(sessionKey, request)\n }\n return request\n}\n\nasync function analyzeWithModel(request: AnalysisRequest): Promise<PixelAnalysis> {\n if (!request.modelUrl) {\n throw new Error('A DocAligner modelUrl is required.')\n }\n const pixels = new Uint8ClampedArray(request.pixels)\n const input = preprocessDocumentModel(pixels, request.width, request.height)\n const { session, ort, provider } = await loadSession(\n request.modelUrl,\n request.cacheModel !== false,\n request.ortWasmPaths,\n (progress) => {\n workerScope.postMessage({\n id: request.id,\n download: { url: request.modelUrl ?? '', ...progress },\n })\n },\n )\n const startedAt = performance.now()\n const outputs = await session.run({\n [session.inputNames[0]]: new ort.Tensor('float32', input.data, input.dimensions),\n })\n const tensor = outputs[session.outputNames[0]]\n if (!tensor) throw new Error('Document model returned no output tensor.')\n const decoded = decodeDocumentHeatmaps(\n tensor.data as Float32Array,\n tensor.dims.map(Number),\n request.width,\n request.height,\n request.heatmapThreshold ?? 0.3,\n )\n return {\n sharpness: 20,\n candidate: decoded.candidate,\n detector: 'ml',\n modelStatus: 'ready',\n modelProvider: provider,\n inferenceMs: performance.now() - startedAt,\n }\n}\n\nasync function analyze(request: AnalysisRequest): Promise<PixelAnalysis> {\n try {\n const ml = await analyzeWithModel(request)\n if (!ml.candidate) {\n return {\n ...ml,\n modelStatus: 'failed',\n modelError: 'ML detector returned no geometrically valid corners.',\n }\n }\n return ml\n } catch (error) {\n if (error instanceof ScannerError && error.code === 'model-http') throw error\n const message = error instanceof Error ? error.message : 'Document model failed.'\n return {\n sharpness: 20,\n detector: 'ml',\n modelStatus: 'failed',\n modelError: message,\n }\n }\n}\n\nlet analysisQueue = Promise.resolve()\nworkerScope.onmessage = (event) => {\n const request = event.data\n analysisQueue = analysisQueue.then(async () => {\n try {\n workerScope.postMessage({ id: request.id, result: await analyze(request) })\n } catch (error) {\n workerScope.postMessage({\n id: request.id,\n error: error instanceof Error ? error.message : 'Document analysis failed.',\n errorCode: error instanceof ScannerError ? error.code : undefined,\n })\n }\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.scanner-camera-overlay{color:#fff;isolation:isolate;overscroll-behavior:none;touch-action:none;--scanner-overlay-primary:#c8ff45;--scanner-overlay-seeking:#ef6a5b;--scanner-overlay-detecting:#ef6a5b;--scanner-overlay-unstable:#ffbe40;--scanner-overlay-ready:#8af25a;--scanner-overlay-captured:#fff;background:#050806;width:100%;height:100%;font-family:Manrope,ui-sans-serif,system-ui,sans-serif;position:relative;overflow:hidden}.scanner-camera-overlay.is-captured:after{content:"";z-index:10;pointer-events:none;background:#fff;animation:.14s ease-out forwards scanner-camera-flash;position:absolute;inset:0}@keyframes scanner-camera-flash{0%{opacity:.92}to{opacity:0}}.scanner-camera-overlay__video,.scanner-camera-overlay__quad{width:100%;height:100%;position:absolute;inset:0}.scanner-camera-overlay__video{object-fit:cover;background:#050806;display:block}.model-download-ring{z-index:6;color:#fff;pointer-events:none;background:#080d0ac7;border-radius:50%;place-items:center;width:96px;height:96px;display:grid;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 10px 30px #00000047}.model-download-ring svg{width:100%;height:100%;position:absolute;inset:0;transform:rotate(-90deg)}.model-download-ring circle{fill:none;stroke-width:6px}.model-download-ring .track{stroke:#ffffff47}.model-download-ring .value{stroke:var(--scanner-overlay-primary,#c8ff45);stroke-linecap:round}.model-download-ring.is-indeterminate svg{animation:.9s linear infinite model-download-spin}.model-download-ring.is-indeterminate .value{stroke-dasharray:48 128}.model-download-ring strong{z-index:1;font:600 13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;position:relative}@keyframes model-download-spin{to{transform:rotate(270deg)}}.scanner-camera-overlay__quad.seeking polygon{fill:color-mix(in srgb, var(--scanner-overlay-seeking,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-seeking,#ef6a5b)}.scanner-camera-overlay__quad.detecting polygon{fill:color-mix(in srgb, var(--scanner-overlay-detecting,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-detecting,#ef6a5b)}.scanner-camera-overlay__quad{z-index:1;pointer-events:none}.scanner-camera-overlay__quad polygon{fill:color-mix(in srgb, var(--scanner-overlay-detecting,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-detecting,#ef6a5b);stroke-width:6px;vector-effect:non-scaling-stroke;transition:stroke .15s,fill .15s}.scanner-camera-overlay__quad.unstable polygon{fill:color-mix(in srgb, var(--scanner-overlay-unstable,#ffbe40) 10%, transparent);stroke:var(--scanner-overlay-unstable,#ffbe40)}.scanner-camera-overlay__quad.ready polygon{fill:color-mix(in srgb, var(--scanner-overlay-ready,#8af25a) 10%, transparent);stroke:var(--scanner-overlay-ready,#8af25a)}.scanner-camera-overlay__quad.captured polygon{fill:color-mix(in srgb, var(--scanner-overlay-captured,#fff) 18%, transparent);stroke:var(--scanner-overlay-captured,#fff)}.scanner-camera-overlay__topbar{z-index:3;padding:max(18px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) 48px max(18px, env(safe-area-inset-left));background:linear-gradient(#030704db,#0000);justify-content:space-between;align-items:flex-start;gap:20px;display:flex;position:absolute;top:0;left:0;right:0}.scanner-camera-overlay__topbar div{gap:5px;display:grid}.scanner-camera-overlay__topbar strong{font-size:clamp(18px,4vw,26px)}.scanner-camera-overlay__topbar span{color:#ffffffb8;font-size:12px}.scanner-camera-overlay__topbar button{color:#fff;background:#050a0799;border:1px solid #ffffff61;border-radius:50%;flex:none;width:46px;height:46px;font-size:28px;line-height:1}.scanner-camera-overlay__message{z-index:4;text-align:center;background:#0a100ceb;border:1px solid #fff3;border-radius:14px;justify-items:center;gap:12px;width:min(420px,100% - 40px);padding:26px;display:grid;position:absolute;top:50%;left:50%;translate:-50% -50%}.scanner-camera-overlay__message p{color:#ffffffb3;margin:0;font-size:13px;line-height:1.5}.scanner-camera-overlay__message div{gap:10px;display:flex}.scanner-camera-overlay__message button{color:#fff;background:0 0;border:1px solid #ffffff6b;border-radius:7px;min-height:44px;padding:0 16px}.scanner-camera-overlay__spinner{border:3px solid #fff3;border-top-color:var(--scanner-overlay-primary,#c8ff45);border-radius:50%;width:28px;height:28px;animation:.8s linear infinite scanner-camera-spin}@keyframes scanner-camera-spin{to{rotate:1turn}}.scanner-camera-overlay__readout{z-index:3;top:max(94px, calc(env(safe-area-inset-top) + 72px));left:max(18px, env(safe-area-inset-left));gap:8px;max-width:calc(100% - 36px);display:grid;position:absolute}.scanner-camera-overlay__status{text-transform:uppercase;background:#050a07c2;border-radius:6px;flex-direction:column;align-items:flex-start;gap:4px;max-width:100%;padding:8px 10px;font:9px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;display:flex}.scanner-camera-overlay__status-line{flex-wrap:wrap;align-items:center;gap:7px;max-width:100%;display:flex}.scanner-camera-overlay__status-line>span{background:var(--scanner-overlay-detecting,#ef6a5b);border-radius:50%;flex:none;width:7px;height:7px}.scanner-camera-overlay__status-line>span.seeking{background:var(--scanner-overlay-seeking,#ef6a5b)}.scanner-camera-overlay__status-line>span.detecting{background:var(--scanner-overlay-detecting,#ef6a5b)}.scanner-camera-overlay__status-line>span.unstable{background:var(--scanner-overlay-unstable,#ffbe40)}.scanner-camera-overlay__status-line>span.ready{background:var(--scanner-overlay-ready,#8af25a)}.scanner-camera-overlay__status-line>span.captured{background:var(--scanner-overlay-captured,#fff)}.scanner-camera-overlay__decoder{color:#d9f7c4;text-transform:none;font-size:12px;line-height:1.2}.scanner-camera-overlay__diagnostics{background:#050a07b8;border-radius:6px;gap:2px;max-width:100%;margin:0;padding:8px 10px;font:9px/1.35 ui-monospace,SFMono-Regular,Menlo,monospace;display:grid}.scanner-camera-overlay__diagnostics>div{gap:6px;display:flex}.scanner-camera-overlay__diagnostics dt{color:#ffffff8c;text-transform:uppercase;min-width:44px}.scanner-camera-overlay__diagnostics dd{color:#d9f7c4;margin:0}.scanner-camera-overlay__copy{color:#fff;font:inherit;text-underline-offset:2px;background:0 0;border:0;margin:0;padding:0;text-decoration:underline}.scanner-camera-overlay__copy:focus-visible{outline:2px solid var(--scanner-overlay-ready,#8af25a);outline-offset:2px}.scanner-camera-overlay__controls{z-index:3;padding:60px max(18px, env(safe-area-inset-right)) max(22px, env(safe-area-inset-bottom)) max(18px, env(safe-area-inset-left));background:linear-gradient(#0000,#030704eb);grid-template-columns:minmax(90px,1fr) minmax(140px,auto) minmax(90px,1fr);align-items:end;gap:18px;display:grid;position:absolute;bottom:0;left:0;right:0}.scanner-camera-overlay__cancel{color:#fff;background:#050a0766;border:1px solid #ffffff6b;border-radius:8px;min-height:46px}.scanner-camera-overlay__capture{justify-items:center;gap:10px;display:grid}.scanner-camera-overlay__toggles{flex-wrap:wrap;justify-content:center;align-items:center;gap:8px 14px;display:flex}.scanner-camera-overlay__toggles label{white-space:nowrap;align-items:center;gap:7px;min-height:34px;font-size:11px;display:flex}.scanner-camera-overlay__toggles input{width:22px;height:22px;accent-color:var(--scanner-overlay-primary,#c8ff45)}.scanner-camera-overlay__toggles input:disabled{opacity:.85}.scanner-camera-overlay__shutter{background:0 0;border:3px solid #fff;border-radius:50%;place-items:center;width:74px;height:74px;padding:6px;display:grid}.scanner-camera-overlay__shutter span{background:#fff;border-radius:50%;width:100%;height:100%;display:block}.scanner-camera-overlay__shutter:disabled{opacity:.42}.scanner-camera-overlay__quality{justify-items:end;gap:4px;padding-bottom:8px;font-size:10px;display:grid}.scanner-camera-overlay__quality span{color:#ffffff94;text-transform:uppercase}.scanner-camera-overlay__quality strong{font:10px ui-monospace,SFMono-Regular,Menlo,monospace}@media (width<=560px){.scanner-camera-overlay__controls{grid-template-columns:72px 1fr 72px;gap:10px}.scanner-camera-overlay__quality strong{text-align:right;max-width:72px}}@media (orientation:landscape) and (height<=560px){.scanner-camera-overlay__controls{padding-top:20px}.scanner-camera-overlay__shutter{width:60px;height:60px}}
|
|
1
|
+
.scanner-camera-overlay{color:#fff;isolation:isolate;overscroll-behavior:none;touch-action:none;--scanner-overlay-primary:#c8ff45;--scanner-overlay-seeking:#ef6a5b;--scanner-overlay-detecting:#ef6a5b;--scanner-overlay-unstable:#ffbe40;--scanner-overlay-ready:#8af25a;--scanner-overlay-captured:#fff;background:#050806;width:100%;height:100%;font-family:Manrope,ui-sans-serif,system-ui,sans-serif;position:relative;overflow:hidden}.scanner-camera-overlay.is-captured:after{content:"";z-index:10;pointer-events:none;background:#fff;animation:.14s ease-out forwards scanner-camera-flash;position:absolute;inset:0}@keyframes scanner-camera-flash{0%{opacity:.92}to{opacity:0}}.scanner-camera-overlay__video,.scanner-camera-overlay__quad{width:100%;height:100%;position:absolute;inset:0}.scanner-camera-overlay__video{object-fit:cover;background:#050806;display:block}.scanner-camera-overlay__still{z-index:0;pointer-events:none}.model-download-ring{z-index:6;color:#fff;pointer-events:none;background:#080d0ac7;border-radius:50%;place-items:center;width:96px;height:96px;display:grid;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);box-shadow:0 10px 30px #00000047}.model-download-ring svg{width:100%;height:100%;position:absolute;inset:0;transform:rotate(-90deg)}.model-download-ring circle{fill:none;stroke-width:6px}.model-download-ring .track{stroke:#ffffff47}.model-download-ring .value{stroke:var(--scanner-overlay-primary,#c8ff45);stroke-linecap:round}.model-download-ring.is-indeterminate svg{animation:.9s linear infinite model-download-spin}.model-download-ring.is-indeterminate .value{stroke-dasharray:48 128}.model-download-ring strong{z-index:1;font:600 13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;position:relative}@keyframes model-download-spin{to{transform:rotate(270deg)}}.scanner-camera-overlay__quad.seeking polygon{fill:color-mix(in srgb, var(--scanner-overlay-seeking,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-seeking,#ef6a5b)}.scanner-camera-overlay__quad.detecting polygon{fill:color-mix(in srgb, var(--scanner-overlay-detecting,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-detecting,#ef6a5b)}.scanner-camera-overlay__quad{z-index:1;pointer-events:none}.scanner-camera-overlay__quad polygon{fill:color-mix(in srgb, var(--scanner-overlay-detecting,#ef6a5b) 8%, transparent);stroke:var(--scanner-overlay-detecting,#ef6a5b);stroke-width:6px;vector-effect:non-scaling-stroke;transition:stroke .15s,fill .15s}.scanner-camera-overlay__quad.unstable polygon{fill:color-mix(in srgb, var(--scanner-overlay-unstable,#ffbe40) 10%, transparent);stroke:var(--scanner-overlay-unstable,#ffbe40)}.scanner-camera-overlay__quad.ready polygon{fill:color-mix(in srgb, var(--scanner-overlay-ready,#8af25a) 10%, transparent);stroke:var(--scanner-overlay-ready,#8af25a)}.scanner-camera-overlay__quad.captured polygon{fill:color-mix(in srgb, var(--scanner-overlay-captured,#fff) 18%, transparent);stroke:var(--scanner-overlay-captured,#fff)}.scanner-camera-overlay__topbar{z-index:3;padding:max(18px, env(safe-area-inset-top)) max(18px, env(safe-area-inset-right)) 48px max(18px, env(safe-area-inset-left));background:linear-gradient(#030704db,#0000);justify-content:space-between;align-items:flex-start;gap:20px;display:flex;position:absolute;top:0;left:0;right:0}.scanner-camera-overlay__topbar div{gap:5px;display:grid}.scanner-camera-overlay__topbar strong{font-size:clamp(18px,4vw,26px)}.scanner-camera-overlay__topbar span{color:#ffffffb8;font-size:12px}.scanner-camera-overlay__topbar button{color:#fff;background:#050a0799;border:1px solid #ffffff61;border-radius:50%;flex:none;width:46px;height:46px;font-size:28px;line-height:1}.scanner-camera-overlay__message{z-index:4;text-align:center;background:#0a100ceb;border:1px solid #fff3;border-radius:14px;justify-items:center;gap:12px;width:min(420px,100% - 40px);padding:26px;display:grid;position:absolute;top:50%;left:50%;translate:-50% -50%}.scanner-camera-overlay__message p{color:#ffffffb3;margin:0;font-size:13px;line-height:1.5}.scanner-camera-overlay__message div{gap:10px;display:flex}.scanner-camera-overlay__message button{color:#fff;background:0 0;border:1px solid #ffffff6b;border-radius:7px;min-height:44px;padding:0 16px}.scanner-camera-overlay__spinner{border:3px solid #fff3;border-top-color:var(--scanner-overlay-primary,#c8ff45);border-radius:50%;width:28px;height:28px;animation:.8s linear infinite scanner-camera-spin}@keyframes scanner-camera-spin{to{rotate:1turn}}.scanner-camera-overlay__readout{z-index:3;top:max(94px, calc(env(safe-area-inset-top) + 72px));left:max(18px, env(safe-area-inset-left));gap:8px;max-width:calc(100% - 36px);display:grid;position:absolute}.scanner-camera-overlay__status{text-transform:uppercase;background:#050a07c2;border-radius:6px;flex-direction:column;align-items:flex-start;gap:4px;max-width:100%;padding:8px 10px;font:9px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;display:flex}.scanner-camera-overlay__status-line{flex-wrap:wrap;align-items:center;gap:7px;max-width:100%;display:flex}.scanner-camera-overlay__status-line>span{background:var(--scanner-overlay-detecting,#ef6a5b);border-radius:50%;flex:none;width:7px;height:7px}.scanner-camera-overlay__status-line>span.seeking{background:var(--scanner-overlay-seeking,#ef6a5b)}.scanner-camera-overlay__status-line>span.detecting{background:var(--scanner-overlay-detecting,#ef6a5b)}.scanner-camera-overlay__status-line>span.unstable{background:var(--scanner-overlay-unstable,#ffbe40)}.scanner-camera-overlay__status-line>span.ready{background:var(--scanner-overlay-ready,#8af25a)}.scanner-camera-overlay__status-line>span.captured{background:var(--scanner-overlay-captured,#fff)}.scanner-camera-overlay__decoder{color:#d9f7c4;text-transform:none;font-size:12px;line-height:1.2}.scanner-camera-overlay__diagnostics{background:#050a07b8;border-radius:6px;gap:2px;max-width:100%;margin:0;padding:8px 10px;font:9px/1.35 ui-monospace,SFMono-Regular,Menlo,monospace;display:grid}.scanner-camera-overlay__diagnostics>div{gap:6px;display:flex}.scanner-camera-overlay__diagnostics dt{color:#ffffff8c;text-transform:uppercase;min-width:44px}.scanner-camera-overlay__diagnostics dd{color:#d9f7c4;margin:0}.scanner-camera-overlay__copy{color:#fff;font:inherit;text-underline-offset:2px;background:0 0;border:0;margin:0;padding:0;text-decoration:underline}.scanner-camera-overlay__copy:focus-visible{outline:2px solid var(--scanner-overlay-ready,#8af25a);outline-offset:2px}.scanner-camera-overlay__controls{z-index:3;padding:60px max(18px, env(safe-area-inset-right)) max(22px, env(safe-area-inset-bottom)) max(18px, env(safe-area-inset-left));background:linear-gradient(#0000,#030704eb);grid-template-columns:minmax(90px,1fr) minmax(140px,auto) minmax(90px,1fr);align-items:end;gap:18px;display:grid;position:absolute;bottom:0;left:0;right:0}.scanner-camera-overlay__cancel{color:#fff;background:#050a0766;border:1px solid #ffffff6b;border-radius:8px;min-height:46px}.scanner-camera-overlay__capture{justify-items:center;gap:10px;display:grid}.scanner-camera-overlay__toggles{flex-wrap:wrap;justify-content:center;align-items:center;gap:8px 14px;display:flex}.scanner-camera-overlay__toggles label{white-space:nowrap;align-items:center;gap:7px;min-height:34px;font-size:11px;display:flex}.scanner-camera-overlay__toggles input{width:22px;height:22px;accent-color:var(--scanner-overlay-primary,#c8ff45)}.scanner-camera-overlay__toggles input:disabled{opacity:.85}.scanner-camera-overlay__shutter{background:0 0;border:3px solid #fff;border-radius:50%;place-items:center;width:74px;height:74px;padding:6px;display:grid}.scanner-camera-overlay__shutter span{background:#fff;border-radius:50%;width:100%;height:100%;display:block}.scanner-camera-overlay__shutter:disabled{opacity:.42}.scanner-camera-overlay__quality{justify-items:end;gap:4px;padding-bottom:8px;font-size:10px;display:grid}.scanner-camera-overlay__quality span{color:#ffffff94;text-transform:uppercase}.scanner-camera-overlay__quality strong{font:10px ui-monospace,SFMono-Regular,Menlo,monospace}@media (width<=560px){.scanner-camera-overlay__controls{grid-template-columns:72px 1fr 72px;gap:10px}.scanner-camera-overlay__quality strong{text-align:right;max-width:72px}}@media (orientation:landscape) and (height<=560px){.scanner-camera-overlay__controls{padding-top:20px}.scanner-camera-overlay__shutter{width:60px;height:60px}}
|
|
2
2
|
/*$vite$:1*/
|