@cap.js/widget 0.1.56 → 0.1.58

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/cap.d.ts CHANGED
@@ -2,6 +2,7 @@ declare global {
2
2
  interface Window {
3
3
  CAP_CUSTOM_FETCH?: typeof fetch;
4
4
  CAP_CUSTOM_WASM_URL?: string;
5
+ CAP_CUSTOM_HASHWX_URL?: string;
5
6
  CAP_PAKO_URL?: string;
6
7
  CAP_CSS_NONCE?: string;
7
8
  CAP_SCRIPT_NONCE?: string;
package/cap.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{const e="vibrate"in navigator&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches;if("undefined"==typeof window)return;const t=["#f5c2e7","#cba6f7","#f38ba8","#fab387","#f9e2af","#a6e3a1","#94e2d5","#89dceb","#b4befe"],i=(e,i,s)=>{const r=0===i?"9999px":"0",n=i===s-1?"9999px":"0";return`color:#1e1e2e;background:${(e=>{if("cap"===e)return"#89b4fa";let i=0;for(let t=0;t<e.length;t++)i=e.charCodeAt(t)+((i<<5)-i);return t[Math.abs(i)%t.length]})(e)};margin-left:${i?"-6px":0};padding:0 6px;border-radius:${r} ${n} ${n} ${r};font-weight:600`},s={};for(const e of["debug","info","warn","error"])s[e]=(t,...s)=>{if(window.CAP_SILENT||"debug"===e&&!window.CAP_DEBUG)return;const r=t.map(e=>`%c${e}`).join(" "),n=t.map((e,s)=>i(e,s,t.length));console["debug"===e?"log":e](r,...n,...s)};const r=e=>e?["cap",e]:["cap"],n=e=>`${Math.round(performance.now()-e)}ms`,a=(e,t)=>Object.assign(new Error(t),{code:e}),o=(e,t={})=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(e,t):fetch(e,t),l=(e,t)=>t?t.aborted?Promise.reject(a("aborted","aborted")):Promise.race([e,new Promise((e,i)=>t.addEventListener("abort",()=>i(a("aborted","aborted")),{once:!0}))]):e,c="initial-state,verifying-label,solved-label,error-label,required-label,troubleshooting-label,group-aria-label,verify-aria-label,verifying-aria-label,verified-aria-label,error-aria-label".split(","),h={"zh-cn":"确认你是真人/正在验证.../你是真人/出错了。请重试。/请确认你是真人/故障排除/Cap 验证/点击以确认你是真人/正在验证你是真人,请稍候/我们已确认你是真人,你可以继续了/发生错误,请重试",es:"Verifica que eres humano/Verificando.../Eres humano/Error. Inténtalo de nuevo./Verifica que eres humano/Solucionar problemas/Verificación de Cap/Haz clic para verificar que eres humano/Verificando que eres humano, espera/Hemos verificado que eres humano, ya puedes continuar/Se produjo un error, inténtalo de nuevo",hi:"पुष्टि करें कि आप मानव हैं/सत्यापित हो रहा है.../आप मानव हैं/त्रुटि। पुनः प्रयास करें।/कृपया पुष्टि करें कि आप मानव हैं/समस्या निवारण/Cap सत्यापन/पुष्टि करने के लिए क्लिक करें कि आप मानव हैं/सत्यापित किया जा रहा है कि आप मानव हैं, प्रतीक्षा करें/हमने पुष्टि कर दी है कि आप मानव हैं, अब आप जारी रख सकते हैं/एक त्रुटि हुई, पुनः प्रयास करें",ar:"تحقق من أنك إنسان/جارٍ التحقق.../أنت إنسان/خطأ. حاول مرة أخرى./يرجى التحقق من أنك إنسان/استكشاف الأخطاء وإصلاحها/تحقق Cap/انقر للتحقق من أنك إنسان/جارٍ التحقق من أنك إنسان، يرجى الانتظار/لقد تحققنا من أنك إنسان، يمكنك المتابعة الآن/حدث خطأ، يرجى المحاولة مرة أخرى",pt:"Sou humano/Verificando.../Você é humano/Erro. Tente novamente./Verifique que você é humano/Solução de problemas/Verificação Cap/Clique para verificar que você é humano/Verificando que você é humano, aguarde/Verificamos que você é humano, você pode continuar/Ocorreu um erro, tente novamente",ru:"Я человек/Проверка.../Вы человек/Ошибка. Повторите./Подтвердите, что вы человек/Устранение неполадок/Проверка Cap/Нажмите, чтобы подтвердить, что вы человек/Проверяем, что вы человек, подождите/Мы подтвердили, что вы человек, можете продолжить/Произошла ошибка, повторите попытку",ja:"私は人間です/確認中.../あなたは人間です/エラー。再試行を。/あなたが人間であることを確認してください/トラブルシューティング/Cap 認証/クリックして人間であることを確認/あなたが人間であることを確認しています。お待ちください/あなたが人間であることを確認しました。続行できます/エラーが発生しました。もう一度お試しください",de:"Ich bin ein Mensch/Wird überprüft.../Sie sind ein Mensch/Fehler. Erneut versuchen./Bitte bestätigen Sie, dass Sie ein Mensch sind/Fehlerbehebung/Cap-Verifizierung/Klicken, um zu bestätigen, dass Sie ein Mensch sind/Überprüfung, ob Sie ein Mensch sind, bitte warten/Wir haben bestätigt, dass Sie ein Mensch sind, Sie können fortfahren/Ein Fehler ist aufgetreten, bitte erneut versuchen",fr:"Je suis humain/Vérification.../Vous êtes humain/Erreur. Réessayez./Veuillez vérifier que vous êtes humain/Dépannage/Vérification Cap/Cliquez pour vérifier que vous êtes humain/Vérification que vous êtes humain, veuillez patienter/Nous avons vérifié que vous êtes humain, vous pouvez continuer/Une erreur s'est produite, veuillez réessayer"};function d(e,t){let i=function(e){let t=2166136261;for(let i=0;i<e.length;i++)t^=e.charCodeAt(i),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),s="";function r(){return i^=i<<13,i^=i>>>17,i^=i<<5,i>>>0}for(;s.length<t;){s+=r().toString(16).padStart(8,"0")}return s.substring(0,t)}let u=null;async function p(e){const t=(e=>{const t=atob(e),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);return i})(e),i=(new TextDecoder).decode(await async function(e){if("undefined"!=typeof DecompressionStream)try{const t=new DecompressionStream("deflate-raw"),i=t.writable.getWriter(),s=t.readable.getReader();i.write(e).then(()=>i.close()).catch(()=>{});const r=[];for(;;){const{done:e,value:t}=await s.read();if(e)break;r.push(t)}let n=0;for(const e of r)n+=e.length;const a=new Uint8Array(n);let o=0;for(const e of r)a.set(e,o),o+=e.length;return a}catch{}return u||(u=new Promise((e,t)=>{const i=window.CAP_PAKO_URL||"https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js";s.debug(r("instr"),"DecompressionStream unavailable, loading pako from",i);const n=document.createElement("script");n.src=i;const a=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE;a&&n.setAttribute("nonce",a),n.onload=()=>{window.pako?.inflateRaw?e(window.pako):t(new Error("pako loaded but inflateRaw is missing"))},n.onerror=()=>{u=null,t(new Error(`failed to load pako fallback from ${i}`))},document.head.appendChild(n)})),(await u).inflateRaw(e)}(t));return new Promise(e=>{var t=setTimeout(()=>{n(),e({__timeout:!0})},2e4),s=document.createElement("iframe");s.setAttribute("sandbox","allow-scripts"),s.setAttribute("aria-hidden","true"),s.style.cssText="position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";var r=!1;function n(){r||(r=!0,clearTimeout(t),window.removeEventListener("message",a),s.parentNode&&s.parentNode.removeChild(s))}function a(t){if(s.contentWindow&&t.source===s.contentWindow){var i=t.data;i&&"object"==typeof i&&("cap:instr"===i.type?(n(),i.blocked?e({__blocked:!0,blockReason:i.blockReason||"automated_browser"}):i.result?e(i.result):e({__timeout:!0})):"cap:error"===i.type&&(n(),e({__timeout:!0})))}}window.addEventListener("message",a);const o=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE,l=o?` nonce="${String(o).replace(/"/g,"&quot;")}"`:"";s.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script'+l+">"+i+"\n<\/script></body></html>",document.body.appendChild(s)})}let g=null;const v=()=>{if(g)return g;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.7/browser/cap_wasm_bg.wasm",t=performance.now();return s.debug(r("wasm"),"fetching",e),g=fetch(e).then(e=>{if(!e.ok)throw new Error(`Failed to fetch wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).then(e=>(s.debug(r("wasm"),`ready in ${n(t)}`),e)).catch(e=>{throw g=null,s.warn(r("wasm"),`load failed (${n(t)}):`,e.message||e),e}),g};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&v().catch(()=>{});let b=null;function m(){return b||(b=URL.createObjectURL(new Blob(['(()=>{const e=(e,...t)=>{self.CAP_SILENT||console[e]("[cap worker]",...t)},t=async({salt:t,target:n})=>{let s=0;const r=new TextEncoder,o=4*n.length,a=Math.floor(o/8),l=o%8,i=n.length%2==0?n:`${n}0`,c=i.length/2,f=new Uint8Array(c);for(let e=0;e<c;e++)f[e]=parseInt(i.substring(2*e,2*e+2),16);const g=l>0?255<<8-l&255:0;for(;;)try{for(let e=0;e<5e4;e++){const e=t+s,n=r.encode(e),o=await crypto.subtle.digest("SHA-256",n),i=new Uint8Array(o);let c=!0;for(let e=0;e<a;e++)if(i[e]!==f[e]){c=!1;break}if(c&&l>0&&(i[a]&g)!==(f[a]&g)&&(c=!1),c)return void self.postMessage({nonce:s,found:!0});s++}}catch(t){return e("error","fallback solver crashed:",t.message||t),void self.postMessage({found:!1,error:t.message})}},n=e=>{const t=BigInt("0x"+e.N);let n=BigInt("0x"+e.x);const s=0|e.t,r=Math.max(64,Math.floor(s/50)),o=performance.now();for(let e=0;e<s;e+=r){const o=Math.min(s,e+r);for(let s=e;s<o;s++)n=n*n%t;o<s&&self.postMessage({progress:o/s})}self.postMessage({found:!0,y:n.toString(16),durationMs:(performance.now()-o).toFixed(2)})};if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return e("warn","WebAssembly unavailable, using JS fallback solver (significantly slower)"),void(self.onmessage=async({data:e})=>"rsw"===e?.kind?n(e):t({salt:e.salt,target:e.target}));let s=null;self.onmessage=async({data:r})=>{if("rsw"===r?.kind)return n(r);const{salt:o,target:a,wasmModule:l}=r;if(l instanceof WebAssembly.Module&&null===s){const n=(t=>{try{let e,n=0,r=null;const o=()=>(null!==r&&0!==r.byteLength||(r=new Uint8Array(e.memory.buffer)),r),a=new TextEncoder,l=(e,t,s)=>{if(void 0===s){const s=a.encode(e),r=t(s.length,1)>>>0;return o().subarray(r,r+s.length).set(s),n=s.length,r}let r=e.length,l=t(r,1)>>>0;const i=o();let c=0;for(;c<r;c++){const t=e.charCodeAt(c);if(t>127)break;i[l+c]=t}if(c!==r){0!==c&&(e=e.slice(c));const t=c+3*e.length;l=s(l,r,t,1)>>>0,r=t;const n=o().subarray(l+c,l+r),{written:i}=a.encodeInto(e,n);c+=i,l=s(l,r,c,1)>>>0}return n=c,l},i={wbg:{}};i.wbg.__wbindgen_init_externref_table=()=>{const t=e.__wbindgen_export_0,n=t.grow(4);t.set(0,void 0),t.set(n+0,void 0),t.set(n+1,null),t.set(n+2,!0),t.set(n+3,!1)};const c=new WebAssembly.Instance(t,i);return e=c.exports,e.__wbindgen_start&&e.__wbindgen_start(),s=(t,s)=>{const r=l(t,e.__wbindgen_malloc,e.__wbindgen_realloc),o=n,a=l(s,e.__wbindgen_malloc,e.__wbindgen_realloc),i=n;return BigInt.asUintN(64,e.solve_pow(r,o,a,i))},!0}catch(t){return e("error","wasm init failed:",t.message||t),!1}})(l);if(!n)return e("warn","wasm init failed, falling back to JS solver"),t({salt:o,target:a})}if(null===s)return e("warn","no wasm module provided, falling back to JS solver"),t({salt:o,target:a});try{const e=performance.now(),t=s(o,a),n=performance.now();self.postMessage({nonce:Number(t),found:!0,durationMs:(n-e).toFixed(2)})}catch(t){e("error","solve_pow threw:",t.message||t),self.postMessage({found:!1,error:t.message||String(t)})}},self.onerror=e=>{self.postMessage({found:!1,error:e})}})();'],{type:"application/javascript"})),b)}class f{constructor(e){this._size=e,this._workers=[],this._idle=[],this._queue=[],this._wasmModule=null,this._spawnFailures=0}setWasm(e){this._wasmModule=e}_spawn(){const e=m(),t=new Worker(e);return t._busy=!1,this._workers.push(t),this._idle.push(t),t}_replaceWorker(e){const t=this._workers.indexOf(e);-1!==t&&this._workers.splice(t,1);const i=this._idle.indexOf(e);-1!==i&&this._idle.splice(i,1);try{e.terminate()}catch{}return this._spawnFailures++,s.warn(r("pool"),`worker died, replacing (attempt ${this._spawnFailures}/3)`),this._spawnFailures>3?(s.error(r("pool"),"worker spawn failed repeatedly, giving up"),null):this._spawn()}_ensureSize(e){for(;this._workers.length<e;)this._spawn()}run(e,t){return new Promise((i,s)=>{this._queue.push({salt:e,target:t,resolve:i,reject:s}),this._dispatch()})}runMsg(e,t){return new Promise((i,s)=>{this._queue.push({msg:e,onProgress:t,resolve:i,reject:s}),this._dispatch()})}_dispatch(){for(;this._idle.length>0&&this._queue.length>0;){const e=this._idle.shift(),t=this._queue.shift(),{resolve:i,reject:s}=t,r=void 0!==t.msg;let n=!1;const a=({data:l})=>{if(!n)if(!l||"number"!=typeof l.progress||l.found)n=!0,e.removeEventListener("message",a),e.removeEventListener("error",o),this._spawnFailures=0,this._idle.push(e),l.found?i(r?l:l.nonce):s(new Error(l.error||"worker failed")),this._dispatch();else if(t.onProgress)try{t.onProgress(l.progress)}catch{}},o=t=>{if(n)return;n=!0,e.removeEventListener("message",a),e.removeEventListener("error",o);const i=this._replaceWorker(e);s(t),i&&this._dispatch()};e.addEventListener("message",a),e.addEventListener("error",o),r?e.postMessage(t.msg):this._wasmModule?e.postMessage({salt:t.salt,target:t.target,wasmModule:this._wasmModule},[]):e.postMessage({salt:t.salt,target:t.target})}}terminate(){for(const e of this._workers)try{e.terminate()}catch{}this._workers=[],this._idle=[],this._queue=[]}}class w extends HTMLElement{static formAssociated=!0;#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#n;#a;#o;#l=!1;#c;#h;#d=null;#u=null;#p=null;#g=null;#v=null;#b=null;get#m(){return e&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#f(){return{state:"idle",challengeResp:null,challenges:null,results:[],completedCount:0,solvePromise:null,promoteFn:null,_listeners:[],pendingPromotion:null,token:null,tokenExpires:null,notify(){for(const e of this._listeners)e();this._listeners=[]},onSettled(e){"done"===this.state||"error"===this.state?e():this._listeners.push(e)}}}#w(){this.#d=this.#f(),this.#y()}#k(){this.#g&&(window.removeEventListener("mousemove",this.#g),window.removeEventListener("touchstart",this.#g),window.removeEventListener("keydown",this.#g),this.#g=null)}#y(){this.#k();const e=()=>{this.#k(),this.#x()};this.#g=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#_(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#E(){this.#_()||s.info(r("challenges"),"solved invisible challenge")}#x(){"idle"===this.#d.state&&this.#_()&&(this.#d.state="waiting",this.#u=setTimeout(()=>{this.#C()},2500))}async#C(){if("waiting"!==this.#d.state)return;this.#d.state="fetching",this.#d._t0=performance.now();let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH&&(e="/"),e){e.endsWith("/")||(e+="/");try{const t=await o(`${e}challenge`,{method:"POST",signal:this.#b?.signal});let i;try{i=await t.json()}catch{throw new Error("Failed to parse speculative challenge response")}if(i.error)throw new Error(i.error);if(!this.#d)return;if(i._apiEndpoint=e,this.#d.challengeResp=i,2===i.format&&Array.isArray(i.challenges))return this.#d.state="idle",void this.#d.notify();const{challenge:s,token:r}=i;let n=s;if(!Array.isArray(n)){let e=0;n=Array.from({length:s.c},()=>(e++,[d(`${r}${e}`,s.s),d(`${r}${e}d`,s.d)]))}this.#d.challenges=n,this.#d.state="solving",this.#d.solvePromise=this.#A(n)}catch{if(!this.#d)return;this.#d.state="error",this.#d.notify()}}else this.#d.state="idle"}async#A(e){m();let t=null;try{t=await v()}catch{}this.#p||(this.#p=new f(1),this.#p._spawn()),this.#p.setWasm(t);const i=e.length,s=new Array(i);let r=1,n=!1;this.#d.promoteFn=e=>{n||(n=!0,r=e,this.#p._size=e,this.#p._ensureSize(e))},null!==this.#d.pendingPromotion&&(this.#d.promoteFn(this.#d.pendingPromotion),this.#d.pendingPromotion=null);let a=0;for(;a<i;){const t=r,o=[],l=[];for(let s=0;s<t&&a<i;s++)l.push(a),o.push(e[a]),a++;this.#p._ensureSize(Math.max(r,t));const c=await Promise.all(o.map(e=>this.#p.run(e[0],e[1]).then(e=>(this.#d&&this.#d.completedCount++,e))));for(let e=0;e<l.length;e++)s[l[e]]=c[e];!n&&a<i&&await new Promise(e=>setTimeout(e,120))}return this.#d?(this.#d.results=s,this.#d.state="redeeming",this.#S(s),s):s}async#S(e){try{if(!this.#d)return;const t=this.#d.challengeResp,i=t._apiEndpoint;if(!i)throw a("missing_endpoint","speculative redeem: missing apiEndpoint");let s=null;if(t.instrumentation){if(s=await p(t.instrumentation),!this.#d)return;if(s?.__timeout||s?.__blocked)return this.#d.state="done",void this.#d.notify()}const r=await o(`${i}redeem`,{method:"POST",body:JSON.stringify({token:t.token,solutions:e,...s&&{instr:s}}),headers:{"Content-Type":"application/json"},signal:this.#b?.signal});let l;try{l=await r.json()}catch{throw new Error("Failed to parse speculative redeem response")}if(!this.#d)return;if(!l.success)throw new Error(l.error||"Speculative redeem failed");this.#d.token=l.token,this.#d.tokenExpires=new Date(l.expires).getTime(),this.#d.state="done",this.#d._invisibleElapsed=this.#d._t0?n(this.#d._t0):"?",this.#d.notify()}catch{if(!this.#d)return;this.#d.state="done",this.#d.notify()}}get#T(){return this.getAttribute("data-cap-hidden-field-name")||"cap-token"}#L(e){const t=this.querySelector(`input[name='${this.#T}']`);t&&(t.value=e)}getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||this.#v?.[e]||t}#I(){s.debug(r("solve"),`served from speculative cache (saved ${this.#d._invisibleElapsed||"?"})`),this.dispatchEvent("progress",{progress:100}),this.#L(this.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const e=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),e),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#m&&navigator.vibrate([10,50,20,30,40]),this.#E(),this.#w(),this.#l=!1,{success:!0,token:this.token}}#P(){this.#v=function(e){const t=e?[e]:navigator.languages||[navigator.language||""];for(let e of t){if(!e)continue;if(e=e.toLowerCase(),"en"===e||e.startsWith("en-"))return null;const t=h[e]?e:h[e.split("-")[0]]?e.split("-")[0]:null;if(!t)return null;const i=h[t].split("/"),s={};return c.forEach((e,t)=>{s[e]=i[t]}),s}return null}(window.CAP_LANG||this.getAttribute("data-cap-lang"))}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state","required"]}constructor(){super(),this.#c&&this.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this);try{this.#h=this.attachInternals()}catch{}}#M(){this.#h?.setValidity&&(this.hasAttribute("required")&&!this.token?this.#h.setValidity({valueMissing:!0},this.getI18nText("required-label","Please verify you're human"),this.#s||this):this.#h.setValidity({}))}initialize(){m(),this.#b=new AbortController,this.#d||(this.#d=this.#f()),this.#p||(this.#p=new f(1),this.#p._spawn())}attributeChangedCallback(e,t,i){if(e.startsWith("on")){const t=e.slice(2),s=this.#c.get(e);if(s&&this.removeEventListener(t,s),i){const i=t=>{const i=this.getAttribute(e);"function"==typeof window[i]&&window[i].call(this,t)};this.#c.set(e,i),this.addEventListener(t,i)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(i,10)),"data-cap-i18n-initial-state"===e&&this.#s&&this.#s?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human")),"required"===e&&this.#M()}async connectedCallback(){this.#o=this,this.shadowRoot?this.#i=this.shadowRoot:this.#i=this.attachShadow({mode:"open"}),this.#s||(this.#s=document.createElement("div")),this.#P(),this.createUI(),this.addEventListeners(),this.initialize(),this.#r.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8),this.#o.innerHTML=`<input type="hidden" name="${this.#T}">`,s.debug(r(),`widget ready (workers=${this.#t}, haptics=${this.#m})`),this.#y(),this.#M(),this.addEventListener("invalid",this.#H)}#H=()=>{if(this.#s){try{this.scrollIntoView({behavior:"smooth",block:"center"})}catch{this.scrollIntoView()}this.#s.classList.remove("invalid"),this.#s.offsetWidth,this.#s.classList.add("invalid"),setTimeout(()=>this.#s?.classList.remove("invalid"),1500)}};async solve(){if(this.#l)return;this.#$();const e=performance.now(),t=this.#b?.signal;s.debug(r("solve"),"starting");try{this.#l=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#r.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let i,l,c=this.getAttribute("data-cap-api-endpoint");if(!c&&window?.CAP_CUSTOM_FETCH)c="/";else if(!c)throw a("missing_endpoint","Missing API endpoint. Either custom fetch or an API endpoint must be provided.");if(c.endsWith("/")||(c+="/"),"done"===this.#d.state&&this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires)return this.#I();if("done"===this.#d.state)i=this.#d.results,l=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100});else if("solving"===this.#d.state||"redeeming"===this.#d.state||"fetching"===this.#d.state||"waiting"===this.#d.state){"waiting"===this.#d.state&&(this.#u&&(clearTimeout(this.#u),this.#u=null),this.#d.state="waiting",this.#C()),this.#d.pendingPromotion=this.#t,this.#d.promoteFn&&this.#d.promoteFn(this.#t);const e=setInterval(()=>{if(t?.aborted||!this.#d)return void clearInterval(e);const i=this.#d.state;if("done"===i||"error"===i)return void clearInterval(e);const s=this.#d.challenges?this.#d.challenges.length:1,r=this.#d.completedCount,n="redeeming"===i?99:"fetching"===i||"waiting"===i?0:Math.min(98,Math.round(r/s*100));this.dispatchEvent("progress",{progress:n})},150);if(await new Promise(e=>this.#d.onSettled(e)),clearInterval(e),t?.aborted||!this.#d)return;if("idle"===this.#d.state&&2===this.#d.challengeResp?.format&&Array.isArray(this.#d.challengeResp.challenges))l=this.#d.challengeResp,this.#d.challengeResp=null,i=await this.solveChallengesV2(l.challenges,t);else{if("done"!==this.#d.state)throw a("solve_failed","Unable to solve challenge, self-hosted instance likely down. This is not an issue with Cap.");if(this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires)return this.#I();i=this.#d.results,l=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100})}}else{const e=this.#d.challengeResp;if(2===e?.format&&Array.isArray(e.challenges))l=e,this.#d.challengeResp=null;else{const e=await o(`${c}challenge`,{method:"POST",signal:t});try{l=await e.json()}catch{throw a("challenge_parse_error","Failed to parse challenge response from server")}if(l.error)throw a("network_error",l.error)}if(2===l.format&&Array.isArray(l.challenges))i=await this.solveChallengesV2(l.challenges,t);else{const{challenge:e,token:s}=l;let r=e;if(!Array.isArray(r)){let t=0;r=Array.from({length:e.c},()=>(t++,[d(`${s}${t}`,e.s),d(`${s}${t}d`,e.d)]))}i=await this.solveChallenges(r,t)}}const h=l.instrumentation?p(l.instrumentation):Promise.resolve(null),u=await h;if(t?.aborted||!this.#d)return;if(u?.__timeout||u?.__blocked){o(`${c}redeem`,{method:"POST",body:JSON.stringify({token:l.token,solutions:i,...u.__blocked&&{instr_blocked:!0},...u.__timeout&&{instr_timeout:!0}}),headers:{"Content-Type":"application/json"},signal:t}).catch(()=>{}),this.updateUIBlocked(this.getI18nText("error-label","Error"),u?.__blocked),this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again"));const e=u?.__blocked?"instr_blocked":"instr_timeout",n=u?.__blocked?`Instrumentation blocked (${u.blockReason||"automated_browser"})`:"Instrumentation timed out";this.removeEventListener("error",this.boundHandleError);const a=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,code:e,message:n}});return super.dispatchEvent(a),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",a),s.error(r("instr"),`[${e}] ${n}`),void(this.#l=!1)}const{token:g}=l,v=await o(`${c}redeem`,{method:"POST",body:JSON.stringify({token:g,solutions:i,...u&&{instr:u}}),headers:{"Content-Type":"application/json"},signal:t});let b;try{b=await v.json()}catch{throw a("redeem_failed","Failed to parse server response")}if(t?.aborted||!this.#d)return;if(this.dispatchEvent("progress",{progress:100}),!b.success)throw a("invalid_solution",b.error||"Invalid solution");this.#L(b.token),this.dispatchEvent("solve",{token:b.token}),this.token=b.token,this.#w(),this.#e&&clearTimeout(this.#e);const m=new Date(b.expires).getTime()-Date.now();return m>0&&m<864e5?this.#e=setTimeout(()=>this.reset(),m):this.error("Invalid expiration time","invalid_expires"),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#m&&navigator.vibrate([10,50,20,30,40]),s.debug(r("solve"),`verified in ${n(e)}`),this.#E(),{success:!0,token:this.token}}catch(e){if(t?.aborted||!this.#d)return;throw this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message,e.code||"solve_failed"),e}}finally{this.#l=!1}}async solveChallengesV2(e,t){const i=e.length;let n=0;const o=new Array(e.length);for(let t=0;t<e.length;t++){const i=e[t];if(!i||"object"!=typeof i||"sha256-pow"!==i.protocol&&"rsw"!==i.protocol&&"instrumentation"!==i.protocol)throw a("challenge_unsupported",`unsupported format-2 protocol '${i?.protocol}'`)}let c=null;if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate)try{c=await v()}catch(e){s.warn(r("wasm"),"unavailable, falling back to JS solver:",e.message||e)}const h=Math.max(1,Math.min(this.#t,e.length)),d=new f(h);d.setWasm(c),d._ensureSize(h);const u=(e,t)=>Promise.race([e,new Promise((e,i)=>setTimeout(()=>i(new Error(`[cap] ${t} timed out after 60000ms`)),6e4))]),g=new Array(e.length).fill(0),b=()=>{const e=g.reduce((e,t)=>e+t,0)+n,t=Math.min(99,Math.round(e/i*100));this.dispatchEvent("progress",{progress:t})};try{await l(Promise.all(e.map((e,t)=>"sha256-pow"===e.protocol?u(d.run(e.payload.salt,e.payload.target),`sha256-pow worker #${t}`).then(e=>{o[t]={nonce:Number(e)},g[t]=0,n++,b()}):"rsw"===e.protocol?u(d.runMsg({kind:"rsw",N:e.payload.N,x:e.payload.x,t:0|e.payload.t},e=>{g[t]=e,b()}),`rsw worker #${t}`).then(e=>{o[t]={y:e.y},g[t]=0,n++,b()}):p(e.payload.blob).then(e=>{o[t]=e?.__timeout?{timeout:!0}:e?.__blocked?{blocked:!0}:{instr:e},g[t]=0,n++,b()}))),t)}finally{d.terminate()}return o}async solveChallenges(e,t){const i=e.length;let n=0;let a=null;const o="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(o)try{a=await v()}catch(e){s.warn(r("wasm"),"unavailable, falling back to JS solver:",e.message||e)}if(!o&&(s.warn(r("wasm"),"WebAssembly disabled in this browser, solver will be ~10x slower"),!this.#i.querySelector(".warning"))){const e=document.createElement("div");e.className="warning",e.style.cssText="width:var(--cap-widget-width,230px);background:rgb(237,56,46);color:white;padding:4px 6px;padding-bottom:calc(var(--cap-border-radius,14px) + 5px);font-size:10px;box-sizing:border-box;font-family:system-ui;border-top-left-radius:8px;border-top-right-radius:8px;text-align:center;user-select:none;margin-bottom:-35.5px;opacity:0;transition:margin-bottom .3s,opacity .3s;",e.innerText=this.getI18nText("wasm-disabled","Enable WASM for significantly faster solving"),this.#i.insertBefore(e,this.#i.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}const c=new f(this.#t);c.setWasm(a),c._ensureSize(this.#t);const h=[];try{for(let s=0;s<e.length;s+=this.#t){const r=e.slice(s,Math.min(s+this.#t,e.length)),a=await l(Promise.all(r.map(([e,t])=>c.run(e,t).then(e=>{n++;const t=Math.min(99,Math.round((0+n)/i*100));return this.dispatchEvent("progress",{progress:t}),e}))),t);h.push(...a)}}finally{c.terminate()}return h}setWorkersCount(e){const t=parseInt(e,10),i=Math.min(navigator.hardwareConcurrency||8,16);this.#t=!Number.isNaN(t)&&t>0&&t<=i?t:navigator.hardwareConcurrency||8}createUI(){this.#s.classList.add("captcha"),this.#s.setAttribute("role","group"),this.#s.setAttribute("aria-label",this.getI18nText("group-aria-label","Cap verification")),this.#r=document.createElement("div"),this.#r.className="captcha-trigger",this.#r.setAttribute("part","trigger"),this.#r.setAttribute("role","button"),this.#r.setAttribute("tabindex","0"),this.#r.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#r.setAttribute("aria-live","polite"),this.#r.setAttribute("disabled","true"),this.#r.innerHTML=`<div class="checkbox" part="checkbox" aria-hidden="true"><svg class="progress-ring" viewBox="0 0 32 32" aria-hidden="true"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p>`,this.#s.appendChild(this.#r),this.#a=document.createElement("a"),this.#a.className="cap-troubleshoot-link",this.#a.setAttribute("part","troubleshoot"),this.#a.setAttribute("target","_blank"),this.#a.setAttribute("rel","noopener"),this.#a.hidden=!0,this.#s.appendChild(this.#a),this.#n=document.createElement("a"),this.#n.className="credits",this.#n.setAttribute("aria-label","Secured by Cap"),this.#n.setAttribute("href","https://trycap.dev"),this.#n.setAttribute("target","_blank"),this.#n.setAttribute("title","Secured by Cap: The self-hosted CAPTCHA for the modern web."),this.#n.textContent="Cap",this.#s.appendChild(this.#n),this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>:host{display:inline-block}@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{width:var(--cap-widget-width,260px);height:var(--cap-widget-height,58px);background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif);transition:filter .2s,transform .2s,height .2s;display:inline-block;position:relative;overflow:hidden}.captcha.has-troubleshoot{height:calc(var(--cap-widget-height,58px) + 26px)}.captcha.has-troubleshoot .captcha-trigger{inset-block-end:26px}.captcha:not([data-state]):active{transform:scale(.97)}.captcha:hover{filter:brightness(98%)}.captcha-trigger{padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);border-radius:inherit;transition:inset .2s;display:flex;position:absolute;inset:0}.captcha-trigger:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:-3px}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);flex-shrink:0;transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;inset-inline-start:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha.invalid{border-color:var(--cap-invalid-border-color,#f55b50);box-shadow:0 0 0 3px var(--cap-invalid-ring-color,#f55b5033);animation:.5s cubic-bezier(.36,.07,.19,.97) cap-shake}@keyframes cap-shake{10%,90%{transform:translate(-1px)}20%,80%{transform:translate(2px)}30%,50%,70%{transform:translate(-4px)}40%,60%{transform:translate(4px)}}@media (prefers-reduced-motion:reduce){.captcha.invalid{animation:none!important}}.captcha-trigger[disabled]{cursor:not-allowed}.captcha[data-state=verifying] .captcha-trigger{cursor:progress}.captcha[data-state=done] .captcha-trigger{cursor:default}.credits{color:var(--cap-color,#212121);text-underline-offset:.18em;z-index:2;border-radius:4px;justify-content:flex-end;align-items:center;min-width:24px;min-height:24px;padding:4px 6px;text-decoration:underline;text-decoration-thickness:.08em;position:absolute;inset-block-end:4px;inset-inline-end:4px;opacity:.8!important;font-size:12px!important;display:inline-flex!important}.captcha .credits{visibility:visible!important;pointer-events:all!important;display:inline-flex!important}.credits:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px;opacity:1!important}.cap-troubleshoot-link{min-width:24px;min-height:24px;color:var(--cap-troubleshoot-color,#0050a0);text-underline-offset:.15em;cursor:pointer;z-index:2;border-radius:4px;align-items:center;padding:4px 6px;font-size:13px;font-weight:500;text-decoration:underline;text-decoration-thickness:.08em;display:inline-flex;position:absolute;inset-block-end:4px;inset-inline-start:14px}.cap-troubleshoot-link[hidden]{display:none}.cap-troubleshoot-link:hover{opacity:.85}.cap-troubleshoot-link:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px}@media (forced-colors:active){.captcha,.checkbox{border-color:canvastext}:is(.captcha-trigger:focus-visible,.credits:focus-visible,.cap-troubleshoot-link:focus-visible){outline-color:highlight}.progress-ring-bg{stroke:graytext}.progress-ring-circle{stroke:canvastext}.captcha.invalid{box-shadow:none;border-color:mark}.credits{opacity:1!important}}</style>`,this.#i.appendChild(this.#s),this.#$(),setTimeout(()=>this.#$(),100)}addEventListeners(){this.#r&&(this.#n.addEventListener("click",e=>{e.preventDefault(),window.open(`https://trycap.dev/?${new URLSearchParams({utm_source:"cap_widget",utm_medium:"referral",utm_campaign:"widget",utm_content:window.CAP_DISABLE_WIDGET_REF?"":location.hostname,ref:window.CAP_DISABLE_WIDGET_REF?"":location.href||"",sub:window.CAP_DISABLE_WIDGET_REF?"":document.referrer||""}).toString()}`,"_blank")}),this.#r.addEventListener("click",()=>{this.#r.hasAttribute("disabled")||this.solve()}),this.#r.addEventListener("mousedown",()=>{!this.#r.hasAttribute("disabled")&&this.#m&&navigator.vibrate(5)}),this.#r.addEventListener("keydown",e=>{e.target===this.#r&&("Enter"!==e.key&&" "!==e.key||this.#r.hasAttribute("disabled")||(e.preventDefault(),e.stopPropagation(),this.solve()))}),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}#N(){if(!this.#o)return!1;const e=this.#o.getBoundingClientRect();if(0===e.width&&0===e.height)return!0;const t=window.getComputedStyle(this.#o);return"none"===t.display||"hidden"===t.visibility}#$(){this.#n&&this.#s&&!this.#N()&&(this.#n.isConnected&&this.#n.parentNode===this.#s||this.#s.appendChild(this.#n),this.#n.textContent&&this.#n.textContent.trim()||(this.#n.textContent="Cap"),"https://trycap.dev"!==this.#n.getAttribute("href")&&this.#n.setAttribute("href","https://trycap.dev"),this.#n.style.cssText=["display: inline-flex !important","visibility: visible !important","opacity: 0.8 !important","pointer-events: all !important","font-size: 12px !important","transform: none !important","clip-path: none !important","filter: none !important","position: absolute !important"].join("; "))}animateLabel(e){if(!this.#r)return;const t=this.#r.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const i=t.querySelector(".label.active");if(i)i.textContent=e;else{const i=document.createElement("span");i.className="label active",i.textContent=e,t.appendChild(i)}return}const i=t.querySelector(".label.active"),s=document.createElement("span");s.className="label",s.textContent=e,t.appendChild(s),s.offsetWidth,s.classList.add("active"),i&&(i.classList.remove("active"),i.classList.add("exit"),i.addEventListener("transitionend",()=>i.remove(),{once:!0}))}updateUI(e,t,i=!1){this.#s&&this.#r&&(this.#s.setAttribute("data-state",e),this.#s.classList.remove("has-troubleshoot"),this.animateLabel(t),this.#a&&(this.#a.hidden=!0),i?this.#r.setAttribute("disabled","true"):this.#r.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(this.#s&&this.#r&&(this.#s.setAttribute("data-state","error"),this.#r.removeAttribute("disabled"),this.animateLabel(e),this.#a))if(t){const e=this.getAttribute("data-cap-troubleshooting-url")||"https://trycap.dev/guide/troubleshooting/instrumentation.html";this.#a.setAttribute("href",e),this.#a.textContent=this.getI18nText("troubleshooting-label","Troubleshoot"),this.#a.hidden=!1,this.#s.classList.add("has-troubleshoot")}else this.#a.hidden=!0,this.#s.classList.remove("has-troubleshoot")}handleProgress(e){if(!this.#r)return;const t=this.#r.querySelector(".progress-ring-circle");if(t){const i=2*Math.PI*14,s=i-e.detail.progress/100*i;t.style.strokeDashoffset=s}const i=this.#r.querySelector(".label-wrapper");if(i){const e=i.querySelector(".label.active");e&&(e.textContent=`${this.getI18nText("verifying-label","Verifying...")}`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e),this.#h?.setValidity?.({}),this.#s?.classList.remove("invalid")}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e),this.#m&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e),this.#M()}executeAttributeCode(e,t){const i=this.getAttribute(e);i&&(s.warn(r(),"inline `onxxx='…'` handlers are deprecated. use `addEventListener` callbacks instead."),new Function("event",i).call(this,t))}error(e="Unknown error",t="unknown"){s.error(r("solve"),`[${t}] ${e}`),this.dispatchEvent("error",{isCap:!0,code:t,message:e})}dispatchEvent(e,t={}){const i=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(i)}reset(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.token=null,this.dispatchEvent("reset"),this.#L("")}get tokenValue(){return this.token}disconnectedCallback(){this.#b?.abort(),this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#k(),this.#u&&(clearTimeout(this.#u),this.#u=null),this.#p&&(this.#p.terminate(),this.#p=null),this.#d&&(this.#d.state="error",this.#d.notify(),this.#d=null)}}class y{constructor(e={},t){const i=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{i.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw i.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&i.setAttribute("data-cap-api-endpoint",e.apiEndpoint),t||i.hasAttribute("data-cap-disable-haptics")||i.setAttribute("data-cap-disable-haptics",""),this.widget=i,this.solve=this.widget.solve.bind(this.widget),this.reset=this.widget.reset.bind(this.widget),this.addEventListener=this.widget.addEventListener.bind(this.widget),Object.defineProperty(this,"token",{get:()=>i.token,configurable:!0,enumerable:!0}),t||(i.style.display="none",document.documentElement.appendChild(i))}}window.Cap=y,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?customElements.get("cap-widget")&&s.warn(r(),"cap-widget custom element already defined, skipping re-define. set window.CAP_DONT_SKIP_REDEFINE = true to override"):customElements.define("cap-widget",w),"object"==typeof exports&&"undefined"!=typeof module?module.exports=y:"function"==typeof define&&define.amd&&define([],()=>y),"undefined"!=typeof exports&&(exports.default=y)})();
1
+ (()=>{const e="0.0.8",t="vibrate"in navigator&&!window.matchMedia("(prefers-reduced-motion: reduce)").matches;if("undefined"==typeof window)return;const i=["#f5c2e7","#cba6f7","#f38ba8","#fab387","#f9e2af","#a6e3a1","#94e2d5","#89dceb","#b4befe"],s=(e,t,s)=>{const r=0===t?"9999px":"0",n=t===s-1?"9999px":"0";return`color:#1e1e2e;background:${(e=>{if("cap"===e)return"#89b4fa";let t=0;for(let i=0;i<e.length;i++)t=e.charCodeAt(i)+((t<<5)-t);return i[Math.abs(t)%i.length]})(e)};margin-left:${t?"-6px":0};padding:0 6px;border-radius:${r} ${n} ${n} ${r};font-weight:600`},r={};for(const e of["debug","info","warn","error"])r[e]=(t,...i)=>{if(window.CAP_SILENT||"debug"===e&&!window.CAP_DEBUG)return;const r=t.map(e=>`%c${e}`).join(" "),n=t.map((e,i)=>s(e,i,t.length));console["debug"===e?"log":e](r,...n,...i)};const n=e=>e?["cap",e]:["cap"],a=e=>`${Math.round(performance.now()-e)}ms`,o=(e,t)=>Object.assign(new Error(t),{code:e}),l=(e,t={})=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(e,t):fetch(e,t),c=(e,t)=>t?t.aborted?Promise.reject(o("aborted","aborted")):Promise.race([e,new Promise((e,i)=>t.addEventListener("abort",()=>i(o("aborted","aborted")),{once:!0}))]):e,h="initial-state,verifying-label,solved-label,error-label,required-label,troubleshooting-label,group-aria-label,verify-aria-label,verifying-aria-label,verified-aria-label,error-aria-label".split(","),d={"zh-cn":"确认你是真人/正在验证.../你是真人/出错了。请重试。/请确认你是真人/故障排除/Cap 验证/点击以确认你是真人/正在验证你是真人,请稍候/我们已确认你是真人,你可以继续了/发生错误,请重试",es:"Verifica que eres humano/Verificando.../Eres humano/Error. Inténtalo de nuevo./Verifica que eres humano/Solucionar problemas/Verificación de Cap/Haz clic para verificar que eres humano/Verificando que eres humano, espera/Hemos verificado que eres humano, ya puedes continuar/Se produjo un error, inténtalo de nuevo",hi:"पुष्टि करें कि आप मानव हैं/सत्यापित हो रहा है.../आप मानव हैं/त्रुटि। पुनः प्रयास करें।/कृपया पुष्टि करें कि आप मानव हैं/समस्या निवारण/Cap सत्यापन/पुष्टि करने के लिए क्लिक करें कि आप मानव हैं/सत्यापित किया जा रहा है कि आप मानव हैं, प्रतीक्षा करें/हमने पुष्टि कर दी है कि आप मानव हैं, अब आप जारी रख सकते हैं/एक त्रुटि हुई, पुनः प्रयास करें",ar:"تحقق من أنك إنسان/جارٍ التحقق.../أنت إنسان/خطأ. حاول مرة أخرى./يرجى التحقق من أنك إنسان/استكشاف الأخطاء وإصلاحها/تحقق Cap/انقر للتحقق من أنك إنسان/جارٍ التحقق من أنك إنسان، يرجى الانتظار/لقد تحققنا من أنك إنسان، يمكنك المتابعة الآن/حدث خطأ، يرجى المحاولة مرة أخرى",pt:"Sou humano/Verificando.../Você é humano/Erro. Tente novamente./Verifique que você é humano/Solução de problemas/Verificação Cap/Clique para verificar que você é humano/Verificando que você é humano, aguarde/Verificamos que você é humano, você pode continuar/Ocorreu um erro, tente novamente",ru:"Я человек/Проверка.../Вы человек/Ошибка. Повторите./Подтвердите, что вы человек/Устранение неполадок/Проверка Cap/Нажмите, чтобы подтвердить, что вы человек/Проверяем, что вы человек, подождите/Мы подтвердили, что вы человек, можете продолжить/Произошла ошибка, повторите попытку",ja:"私は人間です/確認中.../あなたは人間です/エラー。再試行を。/あなたが人間であることを確認してください/トラブルシューティング/Cap 認証/クリックして人間であることを確認/あなたが人間であることを確認しています。お待ちください/あなたが人間であることを確認しました。続行できます/エラーが発生しました。もう一度お試しください",de:"Ich bin ein Mensch/Wird überprüft.../Sie sind ein Mensch/Fehler. Erneut versuchen./Bitte bestätigen Sie, dass Sie ein Mensch sind/Fehlerbehebung/Cap-Verifizierung/Klicken, um zu bestätigen, dass Sie ein Mensch sind/Überprüfung, ob Sie ein Mensch sind, bitte warten/Wir haben bestätigt, dass Sie ein Mensch sind, Sie können fortfahren/Ein Fehler ist aufgetreten, bitte erneut versuchen",fr:"Je suis humain/Vérification.../Vous êtes humain/Erreur. Réessayez./Veuillez vérifier que vous êtes humain/Dépannage/Vérification Cap/Cliquez pour vérifier que vous êtes humain/Vérification que vous êtes humain, veuillez patienter/Nous avons vérifié que vous êtes humain, vous pouvez continuer/Une erreur s'est produite, veuillez réessayer"};function u(e,t){let i=function(e){let t=2166136261;for(let i=0;i<e.length;i++)t^=e.charCodeAt(i),t+=(t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24);return t>>>0}(e),s="";function r(){return i^=i<<13,i^=i>>>17,i^=i<<5,i>>>0}for(;s.length<t;){s+=r().toString(16).padStart(8,"0")}return s.substring(0,t)}let p=null;async function g(e){const t=(e=>{const t=atob(e),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);return i})(e),i=(new TextDecoder).decode(await async function(e){if("undefined"!=typeof DecompressionStream)try{const t=new DecompressionStream("deflate-raw"),i=t.writable.getWriter(),s=t.readable.getReader();i.write(e).then(()=>i.close()).catch(()=>{});const r=[];for(;;){const{done:e,value:t}=await s.read();if(e)break;r.push(t)}let n=0;for(const e of r)n+=e.length;const a=new Uint8Array(n);let o=0;for(const e of r)a.set(e,o),o+=e.length;return a}catch{}return p||(p=new Promise((e,t)=>{const i=window.CAP_PAKO_URL||"https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js";r.debug(n("instr"),"DecompressionStream unavailable, loading pako from",i);const s=document.createElement("script");s.src=i;const a=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE;a&&s.setAttribute("nonce",a),s.onload=()=>{window.pako?.inflateRaw?e(window.pako):t(new Error("pako loaded but inflateRaw is missing"))},s.onerror=()=>{p=null,t(new Error(`failed to load pako fallback from ${i}`))},document.head.appendChild(s)})),(await p).inflateRaw(e)}(t));return new Promise(e=>{var t=setTimeout(()=>{n(),e({__timeout:!0})},2e4),s=document.createElement("iframe");s.setAttribute("sandbox","allow-scripts"),s.setAttribute("aria-hidden","true"),s.style.cssText="position:absolute;width:1px;height:1px;top:-9999px;left:-9999px;border:none;opacity:0;pointer-events:none;";var r=!1;function n(){r||(r=!0,clearTimeout(t),window.removeEventListener("message",a),s.parentNode&&s.parentNode.removeChild(s))}function a(t){if(s.contentWindow&&t.source===s.contentWindow){var i=t.data;i&&"object"==typeof i&&("cap:instr"===i.type?(n(),i.blocked?e({__blocked:!0,blockReason:i.blockReason||"automated_browser"}):i.result?e({...i.result,vp:[window.innerWidth,window.innerHeight]}):e({__timeout:!0})):"cap:error"===i.type&&(n(),e({__timeout:!0})))}}window.addEventListener("message",a);const o=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE,l=o?` nonce="${String(o).replace(/"/g,"&quot;")}"`:"";s.srcdoc='<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body><script'+l+">"+i+"\n<\/script></body></html>",document.body.appendChild(s)})}let m=null;const v=()=>{if(m)return m;const t=window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm_bg.wasm`,i=performance.now();return r.debug(n("wasm"),"fetching",t),m=fetch(t).then(e=>{if(!e.ok)throw new Error(`Failed to fetch wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).then(e=>(r.debug(n("wasm"),`ready in ${a(i)}`),e)).catch(e=>{throw m=null,r.warn(n("wasm"),`load failed (${a(i)}):`,e.message||e),e}),m};let f=null;const b=()=>{if(f)return f;const t=window.CAP_CUSTOM_HASHWX_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/hashwx.wasm`,i=performance.now();return r.debug(n("wasm"),"fetching",t),f=fetch(t).then(e=>{if(!e.ok)throw new Error(`Failed to fetch hashwx wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).then(e=>(r.debug(n("wasm"),`hashwx ready in ${a(i)}`),e)).catch(e=>{throw f=null,r.warn(n("wasm"),`hashwx load failed (${a(i)}):`,e.message||e),e}),f};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&v().catch(()=>{});let w=null;function y(){return w||(w=URL.createObjectURL(new Blob(['(()=>{const e=(e,...t)=>{self.CAP_SILENT||console[e]("[cap worker]",...t)};let t=!1;const n="function"==typeof MessageChannel?new MessageChannel:null,s=()=>new Promise(e=>{if(!n)return setTimeout(e,0);n.port1.onmessage=()=>e(),n.port2.postMessage(0)}),r=async({salt:t,target:n})=>{let s=0;const r=new TextEncoder,o=4*n.length,a=Math.floor(o/8),l=o%8,i=n.length%2==0?n:`${n}0`,c=i.length/2,f=new Uint8Array(c);for(let e=0;e<c;e++)f[e]=parseInt(i.substring(2*e,2*e+2),16);const w=l>0?255<<8-l&255:0;for(;;)try{for(let e=0;e<5e4;e++){const e=t+s,n=r.encode(e),o=await crypto.subtle.digest("SHA-256",n),i=new Uint8Array(o);let c=!0;for(let e=0;e<a;e++)if(i[e]!==f[e]){c=!1;break}if(c&&l>0&&(i[a]&w)!==(f[a]&w)&&(c=!1),c)return void self.postMessage({nonce:s,found:!0});s++}}catch(t){return e("error","fallback solver crashed:",t.message||t),void self.postMessage({found:!1,error:t.message})}},o=e=>{const t=BigInt(`0x${e.N}`);let n=BigInt(`0x${e.x}`);const s=0|e.t,r=Math.max(64,Math.floor(s/50)),o=performance.now();for(let e=0;e<s;e+=r){const o=Math.min(s,e+r);for(let s=e;s<o;s++)n=n*n%t;o<s&&self.postMessage({progress:o/s})}self.postMessage({found:!0,y:n.toString(16),durationMs:(performance.now()-o).toFixed(2)})},a=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298]),l=(e,t)=>{const n=e.length,s=1+(n+8>>6),r=new Uint8Array(s<<6);r.set(e),r[n]=128;const o=8*n,l=new DataView(r.buffer);l.setUint32(r.length-4,o>>>0,!1),l.setUint32(r.length-8,Math.floor(o/4294967296),!1);let i=1779033703,c=3144134277,f=1013904242,w=2773480762,g=1359893119,d=2600822924,h=528734635,u=1541459225;const m=new Uint32Array(64);for(let e=0;e<s;e++){const t=e<<6;for(let e=0;e<16;e++)m[e]=l.getUint32(t+(e<<2),!1);for(let e=16;e<64;e++){const t=m[e-15],n=m[e-2],s=(t>>>7|t<<25)^(t>>>18|t<<14)^t>>>3,r=(n>>>17|n<<15)^(n>>>19|n<<13)^n>>>10;m[e]=m[e-16]+s+m[e-7]+r>>>0}let n=i,s=c,r=f,o=w,b=g,p=d,_=h,y=u;for(let e=0;e<64;e++){const t=y+((b>>>6|b<<26)^(b>>>11|b<<21)^(b>>>25|b<<7))+(b&p^~b&_)+a[e]+m[e]>>>0,l=n&s^n&r^s&r;y=_,_=p,p=b,b=o+t>>>0,o=r,r=s,s=n,n=t+(((n>>>2|n<<30)^(n>>>13|n<<19)^(n>>>22|n<<10))+l>>>0)>>>0}i=i+n>>>0,c=c+s>>>0,f=f+r>>>0,w=w+o>>>0,g=g+b>>>0,d=d+p>>>0,h=h+_>>>0,u=u+y>>>0}const b=new DataView(t.buffer,t.byteOffset,32);b.setUint32(0,i,!1),b.setUint32(4,c,!1),b.setUint32(8,f,!1),b.setUint32(12,w,!1),b.setUint32(16,g,!1),b.setUint32(20,d,!1),b.setUint32(24,h,!1),b.setUint32(28,u,!1)};let i=null;const c=async e=>{const{c:n,d:r,n:o,workerIndex:a,workerCount:c,hashwxModule:f}=e;if(!Number.isInteger(o)||o<1||!Number.isInteger(r)||r<1)throw new Error("malformed hashwx challenge payload");const w=o>>>0,g=((1n<<64n)-1n)/BigInt(r);t=!1;const{w:d,ctx:h,compiled:u,seedPtr:m,regPtr:b,memPtr:p}=(e=>{if(i)return i;const t=new WebAssembly.Instance(e,{}).exports;t._initialize&&t._initialize();let n=!0,s=t.hashwx_alloc(1);if(-1!==s&&0!==s||(n=!1,s=t.hashwx_alloc(0)),s<=0)throw new Error("hashwx_alloc failed");return i={w:t,ctx:s,compiled:n,seedPtr:t.hashwx_seed(s),regPtr:n?t.hashwx_registers(s):0,memPtr:n?t.hashwx_memory(s):0},i})(f),_=new Uint8Array(32);for(let e=0;e<32;e++)_[e]=parseInt(n.slice(2*e,2*e+2),16);const y=new Uint8Array(40);y.set(_,0);const x=new Uint8Array(32),M=performance.now();let U=0,A=M,I=M;try{for(let e=a;;e+=c){let n=e;for(let e=0;e<8;e++)y[32+e]=255&n,n=Math.floor(n/256);l(y,x),new Uint8Array(d.memory.buffer,m,32).set(x),d.hashwx_make(h,m);let r=null;if(u){const e=d.hashwx_module(h),t=d.hashwx_module_size(h),n=new Uint8Array(d.memory.buffer,e,t).slice();r=new WebAssembly.Instance(new WebAssembly.Module(n),{env:{memory:d.memory}}).exports.exec}const o=BigInt(e)*BigInt(w);for(let e=0;e<w;e++){const n=o+BigInt(e);let a;if(u?(d.hashwx_exec_begin(h,n),r(b,p),a=BigInt.asUintN(64,d.hashwx_exec_final(h))):a=BigInt.asUintN(64,d.hashwx_exec(h,n)),U++,a<=g)return void self.postMessage({found:!0,nonce:n.toString(),hashes:U,compiled:u,durationMs:(performance.now()-M).toFixed(2)});if(!(255&U)){const e=performance.now();if(e-A>=16&&(e-I>=150&&(I=e,self.postMessage({progress:U})),await s(),A=performance.now(),t))return self.postMessage({found:!1,error:"stopped"})}}}}catch(e){throw i=null,e}};if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return e("warn","WebAssembly unavailable, using JS fallback solver (significantly slower)"),void(self.onmessage=async({data:e})=>{if("stop"!==e?.kind)return"rsw"===e?.kind?o(e):"hashwx"===e?.kind?self.postMessage({found:!1,error:"hashwx requires WebAssembly"}):r({salt:e.salt,target:e.target});t=!0});let f=null;self.onmessage=async({data:n})=>{if("stop"===n?.kind)return void(t=!0);if("rsw"===n?.kind)return o(n);if("hashwx"===n?.kind)try{return await c(n)}catch(t){return e("error","hashwx solver threw:",t.message||t),self.postMessage({found:!1,error:t.message||String(t)})}const{salt:s,target:a,wasmModule:l}=n;if(l instanceof WebAssembly.Module&&null===f){const t=(t=>{try{let e,n=0,s=null;const r=()=>(null!==s&&0!==s.byteLength||(s=new Uint8Array(e.memory.buffer)),s),o=new TextEncoder,a=(e,t,s)=>{if(void 0===s){const s=o.encode(e),a=t(s.length,1)>>>0;return r().subarray(a,a+s.length).set(s),n=s.length,a}let a=e.length,l=t(a,1)>>>0;const i=r();let c=0;for(;c<a;c++){const t=e.charCodeAt(c);if(t>127)break;i[l+c]=t}if(c!==a){0!==c&&(e=e.slice(c));const t=c+3*e.length;l=s(l,a,t,1)>>>0,a=t;const n=r().subarray(l+c,l+a),{written:i}=o.encodeInto(e,n);c+=i,l=s(l,a,c,1)>>>0}return n=c,l},l={wbg:{}};l.wbg.__wbindgen_init_externref_table=()=>{const t=e.__wbindgen_export_0,n=t.grow(4);t.set(0,void 0),t.set(n+0,void 0),t.set(n+1,null),t.set(n+2,!0),t.set(n+3,!1)};const i=new WebAssembly.Instance(t,l);return e=i.exports,e.__wbindgen_start&&e.__wbindgen_start(),f=(t,s)=>{const r=a(t,e.__wbindgen_malloc,e.__wbindgen_realloc),o=n,l=a(s,e.__wbindgen_malloc,e.__wbindgen_realloc),i=n;return BigInt.asUintN(64,e.solve_pow(r,o,l,i))},!0}catch(t){return e("error","wasm init failed:",t.message||t),!1}})(l);if(!t)return e("warn","wasm init failed, falling back to JS solver"),r({salt:s,target:a})}if(null===f)return e("warn","no wasm module provided, falling back to JS solver"),r({salt:s,target:a});try{const e=performance.now(),t=f(s,a),n=performance.now();self.postMessage({nonce:Number(t),found:!0,durationMs:(n-e).toFixed(2)})}catch(t){e("error","solve_pow threw:",t.message||t),self.postMessage({found:!1,error:t.message||String(t)})}},self.onerror=e=>{self.postMessage({found:!1,error:e})}})();'],{type:"application/javascript"})),w)}class k{constructor(e){this._size=e,this._workers=[],this._idle=[],this._queue=[],this._wasmModule=null,this._spawnFailures=0}setWasm(e){this._wasmModule=e}_spawn(){const e=y(),t=new Worker(e);return t._busy=!1,this._workers.push(t),this._idle.push(t),t}_replaceWorker(e){const t=this._workers.indexOf(e);-1!==t&&this._workers.splice(t,1);const i=this._idle.indexOf(e);-1!==i&&this._idle.splice(i,1);try{e.terminate()}catch{}return this._spawnFailures++,r.warn(n("pool"),`worker died, replacing (attempt ${this._spawnFailures}/3)`),this._spawnFailures>3?(r.error(n("pool"),"worker spawn failed repeatedly, giving up"),null):this._spawn()}_ensureSize(e){for(;this._workers.length<e;)this._spawn()}run(e,t){return new Promise((i,s)=>{this._queue.push({salt:e,target:t,resolve:i,reject:s}),this._dispatch()})}runMsg(e,t){return new Promise((i,s)=>{this._queue.push({msg:e,onProgress:t,resolve:i,reject:s}),this._dispatch()})}_dispatch(){for(;this._idle.length>0&&this._queue.length>0;){const e=this._idle.shift(),t=this._queue.shift(),{resolve:i,reject:s}=t,r=void 0!==t.msg;let n=!1;const a=({data:l})=>{if(!n)if(!l||"number"!=typeof l.progress||l.found)n=!0,e.removeEventListener("message",a),e.removeEventListener("error",o),this._spawnFailures=0,this._idle.push(e),l.found?i(r?l:l.nonce):s(new Error(l.error||"worker failed")),this._dispatch();else if(t.onProgress)try{t.onProgress(l.progress)}catch{}},o=t=>{if(n)return;n=!0,e.removeEventListener("message",a),e.removeEventListener("error",o);const i=this._replaceWorker(e);s(t),i&&this._dispatch()};e.addEventListener("message",a),e.addEventListener("error",o),r?e.postMessage(t.msg):this._wasmModule?e.postMessage({salt:t.salt,target:t.target,wasmModule:this._wasmModule},[]):e.postMessage({salt:t.salt,target:t.target})}}stopAll(){for(const e of this._workers)try{e.postMessage({kind:"stop"})}catch{}}terminate(){const e=this._workers;this._workers=[],this._idle=[],this._queue=[];for(const t of e)try{t.postMessage({kind:"stop"})}catch{}setTimeout(()=>{for(const t of e)try{t.terminate()}catch{}},250)}}class x extends HTMLElement{static formAssociated=!0;#e=null;#t=navigator.hardwareConcurrency||8;token=null;#i;#s;#r;#n;#a;#o;#l=!1;#c;#h;#d=null;#u=null;#p=null;#g=null;#m=null;#v=null;get#f(){return t&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#b(){return{state:"idle",challengeResp:null,challenges:null,results:[],completedCount:0,solvePromise:null,promoteFn:null,_listeners:[],pendingPromotion:null,token:null,tokenExpires:null,notify(){for(const e of this._listeners)e();this._listeners=[]},onSettled(e){"done"===this.state||"error"===this.state?e():this._listeners.push(e)}}}#w(){this.#d=this.#b(),this.#y()}#k(){this.#g&&(window.removeEventListener("mousemove",this.#g),window.removeEventListener("touchstart",this.#g),window.removeEventListener("keydown",this.#g),this.#g=null)}#y(){this.#k();const e=()=>{this.#k(),this.#x()};this.#g=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#_(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#A(){this.#_()||r.info(n("challenges"),"solved invisible challenge")}#x(){"idle"===this.#d.state&&this.#_()&&(this.#d.state="waiting",this.#u=setTimeout(()=>{this.#E()},2500))}async#E(){if("waiting"!==this.#d.state)return;this.#d.state="fetching",this.#d._t0=performance.now();let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH&&(e="/"),e){e.endsWith("/")||(e+="/");try{const t=await l(`${e}challenge`,{method:"POST",signal:this.#v?.signal});let i;try{i=await t.json()}catch{throw new Error("Failed to parse speculative challenge response")}if(i.error)throw new Error(i.error);if(!this.#d)return;if(i._apiEndpoint=e,this.#d.challengeResp=i,2===i.format&&Array.isArray(i.challenges))return i.challenges.some(e=>"hashwx"===e?.protocol)&&b().catch(()=>{}),this.#d.state="idle",void this.#d.notify();const{challenge:s,token:r}=i;let n=s;if(!Array.isArray(n)){let e=0;n=Array.from({length:s.c},()=>(e++,[u(`${r}${e}`,s.s),u(`${r}${e}d`,s.d)]))}this.#d.challenges=n,this.#d.state="solving",this.#d.solvePromise=this.#C(n).catch(()=>{this.#d&&(this.#d.state="error",this.#d.notify())})}catch{if(!this.#d)return;this.#d.state="error",this.#d.notify()}}else this.#d.state="idle"}async#C(e){y();let t=null;try{t=await v()}catch{}if(!this.#d)return[];this.#p||(this.#p=new k(1),this.#p._spawn()),this.#p.setWasm(t);const i=e.length,s=new Array(i);let r=1,n=!1;this.#d.promoteFn=e=>{!n&&this.#p&&(n=!0,r=e,this.#p._size=e,this.#p._ensureSize(e))},null!==this.#d.pendingPromotion&&(this.#d.promoteFn(this.#d.pendingPromotion),this.#d.pendingPromotion=null);let a=0;for(;a<i;){if(!this.#d||!this.#p)return s;const t=r,o=[],l=[];for(let s=0;s<t&&a<i;s++)l.push(a),o.push(e[a]),a++;this.#p._ensureSize(Math.max(r,t));const c=await Promise.all(o.map(e=>this.#p.run(e[0],e[1]).then(e=>(this.#d&&this.#d.completedCount++,e))));for(let e=0;e<l.length;e++)s[l[e]]=c[e];!n&&a<i&&await new Promise(e=>setTimeout(e,120))}return this.#d?(this.#d.results=s,this.#d.state="redeeming",this.#S(s),s):s}async#S(e){try{if(!this.#d)return;const t=this.#d.challengeResp,i=t._apiEndpoint;if(!i)throw o("missing_endpoint","speculative redeem: missing apiEndpoint");let s=null;if(t.instrumentation){if(s=await g(t.instrumentation),!this.#d)return;if(s?.__timeout||s?.__blocked)return this.#d.state="done",void this.#d.notify()}const r=await l(`${i}redeem`,{method:"POST",body:JSON.stringify({token:t.token,solutions:e,...s&&{instr:s}}),headers:{"Content-Type":"application/json"},signal:this.#v?.signal});let n;try{n=await r.json()}catch{throw new Error("Failed to parse speculative redeem response")}if(!this.#d)return;if(!n.success)throw new Error(n.error||"Speculative redeem failed");this.#d.token=n.token,this.#d.tokenExpires=new Date(n.expires).getTime(),this.#d.state="done",this.#d._invisibleElapsed=this.#d._t0?a(this.#d._t0):"?",this.#d.notify()}catch{if(!this.#d)return;this.#d.state="done",this.#d.notify()}}get#T(){return this.getAttribute("data-cap-hidden-field-name")||"cap-token"}#I(e){const t=this.querySelector(`input[name='${this.#T}']`);t&&(t.value=e)}getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||this.#m?.[e]||t}#P(){r.debug(n("solve"),`served from speculative cache (saved ${this.#d._invisibleElapsed||"?"})`),this.dispatchEvent("progress",{progress:100}),this.#I(this.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const e=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),e),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#f&&navigator.vibrate([10,50,20,30,40]),this.#A(),this.#w(),this.#l=!1,{success:!0,token:this.token}}#L(){this.#m=function(e){const t=e?[e]:navigator.languages||[navigator.language||""];for(let e of t){if(!e)continue;if(e=e.toLowerCase(),"en"===e||e.startsWith("en-"))return null;const t=d[e]?e:d[e.split("-")[0]]?e.split("-")[0]:null;if(!t)return null;const i=d[t].split("/"),s={};return h.forEach((e,t)=>{s[e]=i[t]}),s}return null}(window.CAP_LANG||this.getAttribute("data-cap-lang"))}static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","data-cap-worker-count","data-cap-i18n-initial-state","required"]}constructor(){super(),this.#c&&this.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c=new Map,this.boundHandleProgress=this.handleProgress.bind(this),this.boundHandleSolve=this.handleSolve.bind(this),this.boundHandleError=this.handleError.bind(this),this.boundHandleReset=this.handleReset.bind(this);try{this.#h=this.attachInternals()}catch{}}#M(){this.#h?.setValidity&&(this.hasAttribute("required")&&!this.token?this.#h.setValidity({valueMissing:!0},this.getI18nText("required-label","Please verify you're human"),this.#s||this):this.#h.setValidity({}))}initialize(){y(),this.#v=new AbortController,this.#d||(this.#d=this.#b()),this.#p||(this.#p=new k(1),this.#p._spawn())}attributeChangedCallback(e,t,i){if(e.startsWith("on")){const t=e.slice(2),s=this.#c.get(e);if(s&&this.removeEventListener(t,s),i){const i=t=>{const i=this.getAttribute(e);"function"==typeof window[i]&&window[i].call(this,t)};this.#c.set(e,i),this.addEventListener(t,i)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(i,10)),"data-cap-i18n-initial-state"===e&&this.#s&&this.#s?.querySelector(".label.active")&&this.animateLabel(this.getI18nText("initial-state","Verify you're human")),"required"===e&&this.#M()}async connectedCallback(){this.#o=this,this.shadowRoot?this.#i=this.shadowRoot:this.#i=this.attachShadow({mode:"open"}),this.#s||(this.#s=document.createElement("div")),this.#L(),this.createUI(),this.addEventListeners(),this.initialize(),this.#r.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8),this.#o.innerHTML=`<input type="hidden" name="${this.#T}">`,r.debug(n(),`widget ready (workers=${this.#t}, haptics=${this.#f})`),this.#y(),this.#M(),this.addEventListener("invalid",this.#$)}#$=()=>{if(this.#s){try{this.scrollIntoView({behavior:"smooth",block:"center"})}catch{this.scrollIntoView()}this.#s.classList.remove("invalid"),this.#s.offsetWidth,this.#s.classList.add("invalid"),setTimeout(()=>this.#s?.classList.remove("invalid"),1500)}};async solve(){if(this.#l)return;this.#U();const e=performance.now(),t=this.#v?.signal;r.debug(n("solve"),"starting");try{this.#l=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.#r.setAttribute("aria-label",this.getI18nText("verifying-aria-label","Verifying you're a human, please wait")),this.dispatchEvent("progress",{progress:0});try{let i,s,c=this.getAttribute("data-cap-api-endpoint");if(!c&&window?.CAP_CUSTOM_FETCH)c="/";else if(!c)throw o("missing_endpoint","Missing API endpoint. Either custom fetch or an API endpoint must be provided.");if(c.endsWith("/")||(c+="/"),"done"===this.#d.state&&this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires)return this.#P();if("done"===this.#d.state)i=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100});else if("solving"===this.#d.state||"redeeming"===this.#d.state||"fetching"===this.#d.state||"waiting"===this.#d.state){"waiting"===this.#d.state&&(this.#u&&(clearTimeout(this.#u),this.#u=null),this.#d.state="waiting",this.#E()),this.#d.pendingPromotion=this.#t,this.#d.promoteFn&&this.#d.promoteFn(this.#t);const e=setInterval(()=>{if(t?.aborted||!this.#d)return void clearInterval(e);const i=this.#d.state;if("done"===i||"error"===i)return void clearInterval(e);const s=this.#d.challenges?this.#d.challenges.length:1,r=this.#d.completedCount,n="redeeming"===i?99:"fetching"===i||"waiting"===i?0:Math.min(98,Math.round(r/s*100));this.dispatchEvent("progress",{progress:n})},150);if(await new Promise(e=>this.#d.onSettled(e)),clearInterval(e),t?.aborted||!this.#d)return;if("idle"===this.#d.state&&2===this.#d.challengeResp?.format&&Array.isArray(this.#d.challengeResp.challenges))s=this.#d.challengeResp,this.#d.challengeResp=null,i=await this.solveChallengesV2(s.challenges,t);else{if("done"!==this.#d.state)throw o("solve_failed","Unable to solve challenge, self-hosted instance likely down. This is not an issue with Cap.");if(this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires)return this.#P();i=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100})}}else{const e=this.#d.challengeResp;if(2===e?.format&&Array.isArray(e.challenges))s=e,this.#d.challengeResp=null;else{const e=await l(`${c}challenge`,{method:"POST",signal:t});try{s=await e.json()}catch{throw o("challenge_parse_error","Failed to parse challenge response from server")}if(s.error)throw o("network_error",s.error)}if(2===s.format&&Array.isArray(s.challenges))i=await this.solveChallengesV2(s.challenges,t);else{const{challenge:e,token:r}=s;let n=e;if(!Array.isArray(n)){let t=0;n=Array.from({length:e.c},()=>(t++,[u(`${r}${t}`,e.s),u(`${r}${t}d`,e.d)]))}i=await this.solveChallenges(n,t)}}const h=s.instrumentation?g(s.instrumentation):Promise.resolve(null),d=await h;if(t?.aborted||!this.#d)return;if(d?.__timeout||d?.__blocked){l(`${c}redeem`,{method:"POST",body:JSON.stringify({token:s.token,solutions:i,...d.__blocked&&{instr_blocked:!0},...d.__timeout&&{instr_timeout:!0}}),headers:{"Content-Type":"application/json"},signal:t}).catch(()=>{}),this.updateUIBlocked(this.getI18nText("error-label","Error"),d?.__blocked),this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again"));const e=d?.__blocked?"instr_blocked":"instr_timeout",a=d?.__blocked?`Instrumentation blocked (${d.blockReason||"automated_browser"})`:"Instrumentation timed out";this.removeEventListener("error",this.boundHandleError);const o=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,code:e,message:a}});return super.dispatchEvent(o),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",o),r.error(n("instr"),`[${e}] ${a}`),void(this.#l=!1)}const{token:p}=s,m=await l(`${c}redeem`,{method:"POST",body:JSON.stringify({token:p,solutions:i,...d&&{instr:d}}),headers:{"Content-Type":"application/json"},signal:t});let v;try{v=await m.json()}catch{throw o("redeem_failed","Failed to parse server response")}if(t?.aborted||!this.#d)return;if(this.dispatchEvent("progress",{progress:100}),!v.success)throw o("invalid_solution",v.error||"Invalid solution");this.#I(v.token),this.dispatchEvent("solve",{token:v.token}),this.token=v.token,this.#w(),this.#e&&clearTimeout(this.#e);const f=new Date(v.expires).getTime()-Date.now();return f>0&&f<864e5?this.#e=setTimeout(()=>this.reset(),f):this.error("Invalid expiration time","invalid_expires"),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#f&&navigator.vibrate([10,50,20,30,40]),r.debug(n("solve"),`verified in ${a(e)}`),this.#A(),{success:!0,token:this.token}}catch(e){if(t?.aborted||!this.#d)return;throw this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message,e.code||"solve_failed"),e}}finally{this.#l=!1}}async solveChallengesV2(e,t){const i=e.length;let s=0;const a=new Array(e.length);for(let t=0;t<e.length;t++){const i=e[t];if(!i||"object"!=typeof i||"sha256-pow"!==i.protocol&&"rsw"!==i.protocol&&"hashwx"!==i.protocol&&"instrumentation"!==i.protocol)throw o("challenge_unsupported",`unsupported format-2 protocol '${i?.protocol}'`)}let l=!1,h=0;for(const t of e)if("sha256-pow"===t.protocol)l=!0;else if("hashwx"===t.protocol){h++;const{c:e,d:i,n:s}=t.payload??{};if("string"!=typeof e||!/^[0-9a-f]{64}$/i.test(e)||!Number.isInteger(i)||i<1||i>1e9||!Number.isInteger(s)||s<1||s>1048576)throw o("challenge_unsupported","malformed hashwx challenge payload")}const d=h>0,u="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;let p=null;if(u&&l)try{p=await v()}catch(e){r.warn(n("wasm"),"unavailable, falling back to JS solver:",e.message||e)}let m=null;if(d){if(!u)throw o("challenge_unsupported","hashwx requires WebAssembly");m=await b()}const f=Math.max(1,Math.min(this.#t,e.length)),w=new k(f);w.setWasm(p),w._ensureSize(f);const y=Math.max(1,this.#t),x=h>y,_=x?1:y;let A=d?new k(y):null;A&&A._ensureSize(y);const E=async e=>{let t;A.stopAll();const i=await Promise.race([Promise.allSettled(e).then(()=>!0),new Promise(e=>{t=setTimeout(()=>e(!1),1e3)})]);clearTimeout(t),i||(r.warn(n("solve"),"hashwx workers did not stop in time, replacing the pool"),A.terminate(),A=new k(y),A._ensureSize(y))},C=(e,t)=>{let i;return Promise.race([e,new Promise((e,s)=>{i=setTimeout(()=>s(new Error(`[cap] ${t} timed out after 60000ms`)),6e4)})]).finally(()=>clearTimeout(i))},S=new Array(e.length).fill(0),T=()=>{const e=S.reduce((e,t)=>e+t,0)+s,t=Math.min(99,Math.round(e/i*100));this.dispatchEvent("progress",{progress:t})},I=async(e,t)=>{const{c:i,d:l,n:c}=e.payload,h=Math.max(1,l),d=new Array(_).fill(0),u=[];try{for(let e=0;e<_;e++){const s=e,r=A.runMsg({kind:"hashwx",hashwxModule:m,c:i,d:l,n:c,workerIndex:s,workerCount:_},e=>{d[s]=e;let i=0;for(const e of d)i+=e;S[t]=Math.min(.99,i/h),T()});r.catch(()=>{}),u.push(r)}const e=await C((p=u,new Promise((e,t)=>{let i=p.length,s=null;for(const r of p)r.then(e,e=>{s||(s=e),i--,0===i&&t(s)})})).catch(e=>{throw o("solve_failed",e?.message||"every hashwx worker failed")}),`hashwx worker #${t}`);a[t]={nonce:e.nonce},r.debug(n("solve"),`hashwx #${t}: ${e.hashes} hashes in ${e.durationMs}ms`,`compiled=${e.compiled}`),S[t]=0,s++,T()}catch(e){throw await E(u),e}var p;return u},P=[];e.forEach((e,t)=>{"hashwx"===e.protocol&&P.push({ch:e,idx:t})});const L=P.length?x?Promise.all(P.map(e=>I(e.ch,e.idx))):(async()=>{for(let e=0;e<P.length;e++){const t=await I(P[e].ch,P[e].idx);e<P.length-1&&await E(t)}})():null;try{await c(Promise.all([L,...e.map((e,t)=>"sha256-pow"===e.protocol?C(w.run(e.payload.salt,e.payload.target),`sha256-pow worker #${t}`).then(e=>{a[t]={nonce:Number(e)},S[t]=0,s++,T()}):"rsw"===e.protocol?C(w.runMsg({kind:"rsw",N:e.payload.N,x:e.payload.x,t:0|e.payload.t},e=>{S[t]=e,T()}),`rsw worker #${t}`).then(e=>{a[t]={y:e.y},S[t]=0,s++,T()}):"hashwx"===e.protocol?null:g(e.payload.blob).then(e=>{a[t]=e?.__timeout?{timeout:!0}:e?.__blocked?{blocked:!0}:{instr:e},S[t]=0,s++,T()}))]),t)}finally{w.terminate(),A&&A.terminate()}return a}async solveChallenges(e,t){const i=e.length;let s=0;let a=null;const o="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(o)try{a=await v()}catch(e){r.warn(n("wasm"),"unavailable, falling back to JS solver:",e.message||e)}if(!o&&(r.warn(n("wasm"),"WebAssembly disabled in this browser, solver will be ~10x slower"),!this.#i.querySelector(".warning"))){const e=document.createElement("div");e.className="warning",e.style.cssText="width:var(--cap-widget-width,230px);background:rgb(237,56,46);color:white;padding:4px 6px;padding-bottom:calc(var(--cap-border-radius,14px) + 5px);font-size:10px;box-sizing:border-box;font-family:system-ui;border-top-left-radius:8px;border-top-right-radius:8px;text-align:center;user-select:none;margin-bottom:-35.5px;opacity:0;transition:margin-bottom .3s,opacity .3s;",e.innerText=this.getI18nText("wasm-disabled","Enable WASM for significantly faster solving"),this.#i.insertBefore(e,this.#i.firstChild),setTimeout(()=>{e.style.marginBottom="calc(-1 * var(--cap-border-radius, 14px))",e.style.opacity=1},10)}const l=new k(this.#t);l.setWasm(a),l._ensureSize(this.#t);const h=[];try{for(let r=0;r<e.length;r+=this.#t){const n=e.slice(r,Math.min(r+this.#t,e.length)),a=await c(Promise.all(n.map(([e,t])=>l.run(e,t).then(e=>{s++;const t=Math.min(99,Math.round((0+s)/i*100));return this.dispatchEvent("progress",{progress:t}),e}))),t);h.push(...a)}}finally{l.terminate()}return h}setWorkersCount(e){const t=parseInt(e,10),i=Math.min(navigator.hardwareConcurrency||8,16);this.#t=!Number.isNaN(t)&&t>0&&t<=i?t:navigator.hardwareConcurrency||8}createUI(){this.#s.classList.add("captcha"),this.#s.setAttribute("role","group"),this.#s.setAttribute("aria-label",this.getI18nText("group-aria-label","Cap verification")),this.#r=document.createElement("div"),this.#r.className="captcha-trigger",this.#r.setAttribute("part","trigger"),this.#r.setAttribute("role","button"),this.#r.setAttribute("tabindex","0"),this.#r.setAttribute("aria-label",this.getI18nText("verify-aria-label","Click to verify you're a human")),this.#r.setAttribute("aria-live","polite"),this.#r.setAttribute("disabled","true"),this.#r.innerHTML=`<div class="checkbox" part="checkbox" aria-hidden="true"><svg class="progress-ring" viewBox="0 0 32 32" aria-hidden="true"><circle class="progress-ring-bg" cx="16" cy="16" r="14"></circle><circle class="progress-ring-circle" cx="16" cy="16" r="14"></circle></svg></div><p part="label" class="label-wrapper"><span class="label active">${this.getI18nText("initial-state","Verify you're human")}</span></p>`,this.#s.appendChild(this.#r),this.#a=document.createElement("a"),this.#a.className="cap-troubleshoot-link",this.#a.setAttribute("part","troubleshoot"),this.#a.setAttribute("target","_blank"),this.#a.setAttribute("rel","noopener"),this.#a.hidden=!0,this.#s.appendChild(this.#a),this.#n=document.createElement("a"),this.#n.className="credits",this.#n.setAttribute("aria-label","Secured by Cap"),this.#n.setAttribute("href","https://trycap.dev"),this.#n.setAttribute("target","_blank"),this.#n.setAttribute("title","Secured by Cap: The self-hosted CAPTCHA for the modern web."),this.#n.textContent="Cap",this.#s.appendChild(this.#n),this.#i.innerHTML=`<style${window.CAP_CSS_NONCE?` nonce=${window.CAP_CSS_NONCE}`:""}>:host{display:inline-block}@media (prefers-reduced-motion:reduce){.captcha,.captcha *{transition:none!important;animation:none!important}.label{filter:none!important;opacity:1!important;transition:none!important;transform:none!important}.label:not(.active),.label.exit{display:none!important}}.captcha,.captcha *{box-sizing:border-box}.captcha{width:var(--cap-widget-width,260px);height:var(--cap-widget-height,58px);background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);-webkit-user-select:none;user-select:none;cursor:pointer;-webkit-tap-highlight-color:#fff0;color:var(--cap-color,#212121);font-family:var(--cap-font,system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif);transition:filter .2s,transform .2s,height .2s;display:inline-block;position:relative;overflow:hidden}.captcha.has-troubleshoot{height:calc(var(--cap-widget-height,58px) + 26px)}.captcha.has-troubleshoot .captcha-trigger{inset-block-end:26px}.captcha:not([data-state]):active{transform:scale(.97)}.captcha:hover{filter:brightness(98%)}.captcha-trigger{padding:var(--cap-widget-padding,14px);align-items:center;gap:var(--cap-gap,15px);border-radius:inherit;transition:inset .2s;display:flex;position:absolute;inset:0}.captcha-trigger:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:-3px}.checkbox{width:var(--cap-checkbox-size,25px);height:var(--cap-checkbox-size,25px);border:var(--cap-checkbox-border,1px solid #aaaaaad1);border-radius:var(--cap-checkbox-border-radius,6px);background-color:var(--cap-checkbox-background,#fafafa91);margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px);flex-shrink:0;transition:opacity .2s}.captcha p{-webkit-user-select:none;user-select:none;margin:0;font-size:15px;font-weight:500}.label-wrapper{flex:1;align-items:center;min-width:0;height:2em;display:flex;position:relative;overflow:hidden}.label-wrapper:before,.label-wrapper:after{content:"";pointer-events:none;z-index:1;width:100%;height:.6em;position:absolute;inset-inline-start:0}.label-wrapper:before{background:linear-gradient(to bottom, var(--cap-background,#fdfdfd), transparent);top:0}.label-wrapper:after{background:linear-gradient(to top, var(--cap-background,#fdfdfd), transparent);bottom:0}.label{white-space:nowrap;opacity:0;filter:blur(2px);transition:transform .5s cubic-bezier(.25,1,.5,1),opacity .5s,filter .5s;position:absolute;transform:translateY(100%)}.label.active{opacity:1;filter:none;transform:translateY(0)}.label.exit{opacity:0;filter:blur(2px);transform:translateY(-100%)}.checkbox .progress-ring{width:100%;height:100%;display:none;transform:rotate(-90deg)}.checkbox .progress-ring-bg{fill:none;stroke:var(--cap-spinner-background-color,#eee);stroke-width:var(--cap-spinner-thickness,3)}.checkbox .progress-ring-circle{fill:none;stroke:var(--cap-spinner-color,#000);stroke-width:var(--cap-spinner-thickness,3);stroke-linecap:round;stroke-dasharray:87.96;stroke-dashoffset:87.96px;transition:stroke-dashoffset .3s}.captcha[data-state=verifying] .checkbox{background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;display:flex;transform:scale(1.1)}.captcha[data-state=verifying] .checkbox .progress-ring{display:block}.captcha[data-state=done] .checkbox{background-image:var(--cap-checkmark,url(data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cstyle%3E%40keyframes%20anim%7B0%25%7Bstroke-dashoffset%3A23.21320343017578px%7Dto%7Bstroke-dashoffset%3A0%7D%7D%3C%2Fstyle%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%2300a67d%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m5%2012%205%205L20%207%22%20style%3D%22stroke-dashoffset%3A0%3Bstroke-dasharray%3A23.21320343017578px%3Banimation%3Aanim%20.5s%20ease%22%2F%3E%3C%2Fsvg%3E));background-size:cover;border:1px solid #0000}.captcha[data-state=done] .checkbox .progress-ring{display:none}.captcha[data-state=error] .checkbox{background-image:var(--cap-error-cross,url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96' viewBox='0 0 24 24'%3E%3Cpath fill='%23f55b50' d='M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8'/%3E%3C%2Fsvg%3E"));background-size:cover;border:1px solid #0000}.captcha[data-state=error] .checkbox .progress-ring{display:none}.captcha.invalid{border-color:var(--cap-invalid-border-color,#f55b50);box-shadow:0 0 0 3px var(--cap-invalid-ring-color,#f55b5033);animation:.5s cubic-bezier(.36,.07,.19,.97) cap-shake}@keyframes cap-shake{10%,90%{transform:translate(-1px)}20%,80%{transform:translate(2px)}30%,50%,70%{transform:translate(-4px)}40%,60%{transform:translate(4px)}}@media (prefers-reduced-motion:reduce){.captcha.invalid{animation:none!important}}.captcha-trigger[disabled]{cursor:not-allowed}.captcha[data-state=verifying] .captcha-trigger{cursor:progress}.captcha[data-state=done] .captcha-trigger{cursor:default}.credits{color:var(--cap-color,#212121);text-underline-offset:.18em;z-index:2;border-radius:4px;justify-content:flex-end;align-items:center;min-width:24px;min-height:24px;padding:4px 6px;text-decoration:underline;text-decoration-thickness:.08em;position:absolute;inset-block-end:4px;inset-inline-end:4px;opacity:.8!important;font-size:12px!important;display:inline-flex!important}.captcha .credits{visibility:visible!important;pointer-events:all!important;display:inline-flex!important}.credits:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px;opacity:1!important}.cap-troubleshoot-link{min-width:24px;min-height:24px;color:var(--cap-troubleshoot-color,#0050a0);text-underline-offset:.15em;cursor:pointer;z-index:2;border-radius:4px;align-items:center;padding:4px 6px;font-size:13px;font-weight:500;text-decoration:underline;text-decoration-thickness:.08em;display:inline-flex;position:absolute;inset-block-end:4px;inset-inline-start:14px}.cap-troubleshoot-link[hidden]{display:none}.cap-troubleshoot-link:hover{opacity:.85}.cap-troubleshoot-link:focus-visible{outline:2px solid var(--cap-focus-ring,#06c);outline-offset:2px}@media (forced-colors:active){.captcha,.checkbox{border-color:canvastext}:is(.captcha-trigger:focus-visible,.credits:focus-visible,.cap-troubleshoot-link:focus-visible){outline-color:highlight}.progress-ring-bg{stroke:graytext}.progress-ring-circle{stroke:canvastext}.captcha.invalid{box-shadow:none;border-color:mark}.credits{opacity:1!important}}</style>`,this.#i.appendChild(this.#s),this.#U(),setTimeout(()=>this.#U(),100)}addEventListeners(){this.#r&&(this.#n.addEventListener("click",e=>{e.preventDefault(),window.open(`https://trycap.dev/?${new URLSearchParams({utm_source:"cap_widget",utm_medium:"referral",utm_campaign:"widget",utm_content:window.CAP_DISABLE_WIDGET_REF?"":location.hostname,ref:window.CAP_DISABLE_WIDGET_REF?"":location.href||"",sub:window.CAP_DISABLE_WIDGET_REF?"":document.referrer||""}).toString()}`,"_blank")}),this.#r.addEventListener("click",()=>{this.#r.hasAttribute("disabled")||this.solve()}),this.#r.addEventListener("mousedown",()=>{!this.#r.hasAttribute("disabled")&&this.#f&&navigator.vibrate(5)}),this.#r.addEventListener("keydown",e=>{e.target===this.#r&&("Enter"!==e.key&&" "!==e.key||this.#r.hasAttribute("disabled")||(e.preventDefault(),e.stopPropagation(),this.solve()))}),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}#H(){if(!this.#o)return!1;const e=this.#o.getBoundingClientRect();if(0===e.width&&0===e.height)return!0;const t=window.getComputedStyle(this.#o);return"none"===t.display||"hidden"===t.visibility}#U(){this.#n&&this.#s&&!this.#H()&&(this.#n.isConnected&&this.#n.parentNode===this.#s||this.#s.appendChild(this.#n),this.#n.textContent&&this.#n.textContent.trim()||(this.#n.textContent="Cap"),"https://trycap.dev"!==this.#n.getAttribute("href")&&this.#n.setAttribute("href","https://trycap.dev"),this.#n.style.cssText=["display: inline-flex !important","visibility: visible !important","opacity: 0.8 !important","pointer-events: all !important","font-size: 12px !important","transform: none !important","clip-path: none !important","filter: none !important","position: absolute !important"].join("; "))}animateLabel(e){if(!this.#r)return;const t=this.#r.querySelector(".label-wrapper");if(!t)return;if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches){const i=t.querySelector(".label.active");if(i)i.textContent=e;else{const i=document.createElement("span");i.className="label active",i.textContent=e,t.appendChild(i)}return}const i=t.querySelector(".label.active"),s=document.createElement("span");s.className="label",s.textContent=e,t.appendChild(s),s.offsetWidth,s.classList.add("active"),i&&(i.classList.remove("active"),i.classList.add("exit"),i.addEventListener("transitionend",()=>i.remove(),{once:!0}))}updateUI(e,t,i=!1){this.#s&&this.#r&&(this.#s.setAttribute("data-state",e),this.#s.classList.remove("has-troubleshoot"),this.animateLabel(t),this.#a&&(this.#a.hidden=!0),i?this.#r.setAttribute("disabled","true"):this.#r.removeAttribute("disabled"))}updateUIBlocked(e,t=!1){if(this.#s&&this.#r&&(this.#s.setAttribute("data-state","error"),this.#r.removeAttribute("disabled"),this.animateLabel(e),this.#a))if(t){const e=this.getAttribute("data-cap-troubleshooting-url")||"https://trycap.dev/guide/troubleshooting/instrumentation.html";this.#a.setAttribute("href",e),this.#a.textContent=this.getI18nText("troubleshooting-label","Troubleshoot"),this.#a.hidden=!1,this.#s.classList.add("has-troubleshoot")}else this.#a.hidden=!0,this.#s.classList.remove("has-troubleshoot")}handleProgress(e){if(!this.#r)return;const t=this.#r.querySelector(".progress-ring-circle");if(t){const i=2*Math.PI*14,s=i-e.detail.progress/100*i;t.style.strokeDashoffset=s}const i=this.#r.querySelector(".label-wrapper");if(i){const e=i.querySelector(".label.active");e&&(e.textContent=`${this.getI18nText("verifying-label","Verifying...")}`)}this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e),this.#h?.setValidity?.({}),this.#s?.classList.remove("invalid")}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e),this.#f&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e),this.#M()}executeAttributeCode(e,t){const i=this.getAttribute(e);i&&(r.warn(n(),"inline `onxxx='…'` handlers are deprecated. use `addEventListener` callbacks instead."),new Function("event",i).call(this,t))}error(e="Unknown error",t="unknown"){r.error(n("solve"),`[${t}] ${e}`),this.dispatchEvent("error",{isCap:!0,code:t,message:e})}dispatchEvent(e,t={}){const i=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(i)}reset(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#u&&(clearTimeout(this.#u),this.#u=null),this.#d&&this.#w(),this.token=null,this.dispatchEvent("reset"),this.#I("")}get tokenValue(){return this.token}disconnectedCallback(){this.#v?.abort(),this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),this.#c.forEach((e,t)=>{this.removeEventListener(t.slice(2),e)}),this.#c.clear(),this.#i&&(this.#i.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#e&&(clearTimeout(this.#e),this.#e=null),this.#k(),this.#u&&(clearTimeout(this.#u),this.#u=null),this.#p&&(this.#p.terminate(),this.#p=null),this.#d&&(this.#d.state="error",this.#d.notify(),this.#d=null)}}class _{constructor(e={},t){const i=t||document.createElement("cap-widget");if(Object.entries(e).forEach(([e,t])=>{i.setAttribute(e,t)}),!e.apiEndpoint&&!window?.CAP_CUSTOM_FETCH)throw i.remove(),new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");e.apiEndpoint&&i.setAttribute("data-cap-api-endpoint",e.apiEndpoint),t||i.hasAttribute("data-cap-disable-haptics")||i.setAttribute("data-cap-disable-haptics",""),this.widget=i,this.solve=this.widget.solve.bind(this.widget),this.reset=this.widget.reset.bind(this.widget),this.addEventListener=this.widget.addEventListener.bind(this.widget),Object.defineProperty(this,"token",{get:()=>i.token,configurable:!0,enumerable:!0}),t||(i.style.display="none",document.documentElement.appendChild(i))}}window.Cap=_,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?customElements.get("cap-widget")&&r.warn(n(),"cap-widget custom element already defined, skipping re-define. set window.CAP_DONT_SKIP_REDEFINE = true to override"):customElements.define("cap-widget",x),"object"==typeof exports&&"undefined"!=typeof module?module.exports=_:"function"==typeof define&&define.amd&&define([],()=>_),"undefined"!=typeof exports&&(exports.default=_)})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap.js/widget",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "description": "The self-hosted CAPTCHA for the modern web.",
5
5
  "keywords": [
6
6
  "algorithm",
package/src/cap.js CHANGED
@@ -1,5 +1,5 @@
1
1
  (() => {
2
- const WASM_VERSION = "0.0.7";
2
+ const WASM_VERSION = "0.0.8";
3
3
  const _browserHasHaptics =
4
4
  "vibrate" in navigator &&
5
5
  !window.matchMedia("(prefers-reduced-motion: reduce)").matches;
@@ -209,7 +209,7 @@
209
209
  blockReason: d.blockReason || "automated_browser",
210
210
  });
211
211
  } else if (d.result) {
212
- resolve(d.result);
212
+ resolve({ ...d.result, vp: [window.innerWidth, window.innerHeight] });
213
213
  } else {
214
214
  resolve({ __timeout: true });
215
215
  }
@@ -267,6 +267,52 @@
267
267
  return wasmModulePromise;
268
268
  };
269
269
 
270
+ const firstFulfilled = (tasks) =>
271
+ new Promise((resolve, reject) => {
272
+ let pending = tasks.length;
273
+ let firstError = null;
274
+ for (const task of tasks) {
275
+ task.then(resolve, (err) => {
276
+ if (!firstError) firstError = err;
277
+ pending--;
278
+ if (pending === 0) reject(firstError);
279
+ });
280
+ }
281
+ });
282
+
283
+ const MAX_HASHWX_DIFFICULTY = 1_000_000_000;
284
+ const MAX_HASHWX_NONCES_PER_HASH = 1_048_576;
285
+
286
+ let hashwxModulePromise = null;
287
+
288
+ const getHashwxModule = () => {
289
+ if (hashwxModulePromise) return hashwxModulePromise;
290
+
291
+ const wasmUrl =
292
+ window.CAP_CUSTOM_HASHWX_URL ||
293
+ `https://cdn.jsdelivr.net/npm/@cap.js/wasm@${WASM_VERSION}/browser/hashwx.wasm`;
294
+
295
+ const t0 = performance.now();
296
+ log.debug(T("wasm"), "fetching", wasmUrl);
297
+ hashwxModulePromise = fetch(wasmUrl)
298
+ .then((r) => {
299
+ if (!r.ok) throw new Error(`Failed to fetch hashwx wasm: ${r.status}`);
300
+ return r.arrayBuffer();
301
+ })
302
+ .then((buf) => WebAssembly.compile(buf))
303
+ .then((mod) => {
304
+ log.debug(T("wasm"), `hashwx ready in ${since(t0)}`);
305
+ return mod;
306
+ })
307
+ .catch((e) => {
308
+ hashwxModulePromise = null;
309
+ log.warn(T("wasm"), `hashwx load failed (${since(t0)}):`, e.message || e);
310
+ throw e;
311
+ });
312
+
313
+ return hashwxModulePromise;
314
+ };
315
+
270
316
  if (
271
317
  typeof WebAssembly === "object" &&
272
318
  typeof WebAssembly.compile === "function"
@@ -292,6 +338,9 @@
292
338
  return _sharedWorkerUrl;
293
339
  }
294
340
 
341
+ const WORKER_STOP_GRACE_MS = 250;
342
+ const HASHWX_SETTLE_MS = 1000;
343
+
295
344
  class WorkerPool {
296
345
  constructor(size) {
297
346
  this._size = size;
@@ -412,15 +461,31 @@
412
461
  }
413
462
  }
414
463
 
415
- terminate() {
464
+ stopAll() {
416
465
  for (const w of this._workers) {
417
466
  try {
418
- w.terminate();
467
+ w.postMessage({ kind: "stop" });
419
468
  } catch {}
420
469
  }
470
+ }
471
+
472
+ terminate() {
473
+ const workers = this._workers;
421
474
  this._workers = [];
422
475
  this._idle = [];
423
476
  this._queue = [];
477
+ for (const w of workers) {
478
+ try {
479
+ w.postMessage({ kind: "stop" });
480
+ } catch {}
481
+ }
482
+ setTimeout(() => {
483
+ for (const w of workers) {
484
+ try {
485
+ w.terminate();
486
+ } catch {}
487
+ }
488
+ }, WORKER_STOP_GRACE_MS);
424
489
  }
425
490
  }
426
491
 
@@ -565,6 +630,9 @@
565
630
  this.#speculative.challengeResp = resp;
566
631
 
567
632
  if (resp.format === 2 && Array.isArray(resp.challenges)) {
633
+ if (resp.challenges.some((ch) => ch?.protocol === "hashwx")) {
634
+ getHashwxModule().catch(() => {});
635
+ }
568
636
  this.#speculative.state = "idle";
569
637
  this.#speculative.notify();
570
638
  return;
@@ -585,7 +653,13 @@
585
653
  this.#speculative.challenges = challenges;
586
654
  this.#speculative.state = "solving";
587
655
 
588
- this.#speculative.solvePromise = this.#speculativeSolveAll(challenges);
656
+ this.#speculative.solvePromise = this.#speculativeSolveAll(
657
+ challenges,
658
+ ).catch(() => {
659
+ if (!this.#speculative) return;
660
+ this.#speculative.state = "error";
661
+ this.#speculative.notify();
662
+ });
589
663
  } catch {
590
664
  if (!this.#speculative) return;
591
665
  this.#speculative.state = "error";
@@ -601,6 +675,7 @@
601
675
  wasmModule = await getWasmModule();
602
676
  } catch {}
603
677
 
678
+ if (!this.#speculative) return [];
604
679
  if (!this.#speculativePool) {
605
680
  this.#speculativePool = new WorkerPool(1);
606
681
  this.#speculativePool._spawn();
@@ -614,7 +689,7 @@
614
689
  let promoted = false;
615
690
 
616
691
  this.#speculative.promoteFn = (fullCount) => {
617
- if (promoted) return;
692
+ if (promoted || !this.#speculativePool) return;
618
693
  promoted = true;
619
694
  concurrency = fullCount;
620
695
  this.#speculativePool._size = fullCount;
@@ -629,6 +704,7 @@
629
704
  let nextIndex = 0;
630
705
 
631
706
  while (nextIndex < total) {
707
+ if (!this.#speculative || !this.#speculativePool) return results;
632
708
  const batchSize = concurrency;
633
709
  const batch = [];
634
710
  const batchIndices = [];
@@ -1217,37 +1293,96 @@
1217
1293
  !(
1218
1294
  ch.protocol === "sha256-pow" ||
1219
1295
  ch.protocol === "rsw" ||
1296
+ ch.protocol === "hashwx" ||
1220
1297
  ch.protocol === "instrumentation"
1221
1298
  )
1222
1299
  ) {
1223
- // Unknown protocol = older widget on a newer server. Fall back to
1224
- // erroring out -- the host can detect this and serve format-1.
1225
1300
  throw _err("challenge_unsupported", `unsupported format-2 protocol '${ch?.protocol}'`);
1226
1301
  }
1227
1302
  }
1228
1303
 
1229
- let wasmModule = null;
1304
+ let needsCapWasm = false;
1305
+ let hashwxCount = 0;
1306
+ for (const ch of challenges) {
1307
+ if (ch.protocol === "sha256-pow") needsCapWasm = true;
1308
+ else if (ch.protocol === "hashwx") {
1309
+ hashwxCount++;
1310
+ const { c, d, n } = ch.payload ?? {};
1311
+ if (
1312
+ typeof c !== "string" ||
1313
+ !/^[0-9a-f]{64}$/i.test(c) ||
1314
+ !Number.isInteger(d) ||
1315
+ d < 1 ||
1316
+ d > MAX_HASHWX_DIFFICULTY ||
1317
+ !Number.isInteger(n) ||
1318
+ n < 1 ||
1319
+ n > MAX_HASHWX_NONCES_PER_HASH
1320
+ ) {
1321
+ throw _err("challenge_unsupported", "malformed hashwx challenge payload");
1322
+ }
1323
+ }
1324
+ }
1325
+ const needsHashwx = hashwxCount > 0;
1326
+
1230
1327
  const wasmSupported =
1231
1328
  typeof WebAssembly === "object" &&
1232
1329
  typeof WebAssembly.instantiate === "function";
1233
- if (wasmSupported) {
1330
+
1331
+ let wasmModule = null;
1332
+ if (wasmSupported && needsCapWasm) {
1234
1333
  try { wasmModule = await getWasmModule(); }
1235
1334
  catch (e) { log.warn(T("wasm"), "unavailable, falling back to JS solver:", e.message || e); }
1236
1335
  }
1237
1336
 
1337
+ let hashwxModule = null;
1338
+ if (needsHashwx) {
1339
+ if (!wasmSupported) {
1340
+ throw _err("challenge_unsupported", "hashwx requires WebAssembly");
1341
+ }
1342
+ hashwxModule = await getHashwxModule();
1343
+ }
1344
+
1238
1345
  const poolSize = Math.max(1, Math.min(this.#workersCount, challenges.length));
1239
1346
  const pool = new WorkerPool(poolSize);
1240
1347
  pool.setWasm(wasmModule);
1241
1348
  pool._ensureSize(poolSize);
1242
1349
 
1350
+ const hashwxWorkers = Math.max(1, this.#workersCount);
1351
+ const hashwxQueued = hashwxCount > hashwxWorkers;
1352
+ const hashwxStripes = hashwxQueued ? 1 : hashwxWorkers;
1353
+ let hashwxPool = needsHashwx ? new WorkerPool(hashwxWorkers) : null;
1354
+ if (hashwxPool) hashwxPool._ensureSize(hashwxWorkers);
1355
+
1356
+ const reclaimStripes = async (stripes) => {
1357
+ hashwxPool.stopAll();
1358
+ let timer;
1359
+ const settled = await Promise.race([
1360
+ Promise.allSettled(stripes).then(() => true),
1361
+ new Promise((resolve) => {
1362
+ timer = setTimeout(() => resolve(false), HASHWX_SETTLE_MS);
1363
+ }),
1364
+ ]);
1365
+ clearTimeout(timer);
1366
+ if (settled) return;
1367
+ log.warn(T("solve"), "hashwx workers did not stop in time, replacing the pool");
1368
+ hashwxPool.terminate();
1369
+ hashwxPool = new WorkerPool(hashwxWorkers);
1370
+ hashwxPool._ensureSize(hashwxWorkers);
1371
+ };
1372
+
1243
1373
  const TASK_TIMEOUT_MS = 60_000;
1244
- const withTimeout = (promise, label) => Promise.race([
1245
- promise,
1246
- new Promise((_, rej) => setTimeout(
1247
- () => rej(new Error(`[cap] ${label} timed out after ${TASK_TIMEOUT_MS}ms`)),
1248
- TASK_TIMEOUT_MS,
1249
- )),
1250
- ]);
1374
+ const withTimeout = (promise, label) => {
1375
+ let timer;
1376
+ return Promise.race([
1377
+ promise,
1378
+ new Promise((_, rej) => {
1379
+ timer = setTimeout(
1380
+ () => rej(new Error(`[cap] ${label} timed out after ${TASK_TIMEOUT_MS}ms`)),
1381
+ TASK_TIMEOUT_MS,
1382
+ );
1383
+ }),
1384
+ ]).finally(() => clearTimeout(timer));
1385
+ };
1251
1386
 
1252
1387
  const inFlight = new Array(challenges.length).fill(0);
1253
1388
  const emit = () => {
@@ -1256,9 +1391,82 @@
1256
1391
  this.dispatchEvent("progress", { progress: visual });
1257
1392
  };
1258
1393
 
1394
+ const solveHashwx = async (ch, idx) => {
1395
+ const { c, d, n } = ch.payload;
1396
+ const expected = Math.max(1, d);
1397
+ const striped = new Array(hashwxStripes).fill(0);
1398
+ const stripes = [];
1399
+ try {
1400
+ for (let s = 0; s < hashwxStripes; s++) {
1401
+ const stripe = s;
1402
+ const task = hashwxPool.runMsg(
1403
+ {
1404
+ kind: "hashwx",
1405
+ hashwxModule,
1406
+ c,
1407
+ d,
1408
+ n,
1409
+ workerIndex: stripe,
1410
+ workerCount: hashwxStripes,
1411
+ },
1412
+ (hashes) => {
1413
+ striped[stripe] = hashes;
1414
+ let sum = 0;
1415
+ for (const v of striped) sum += v;
1416
+ inFlight[idx] = Math.min(0.99, sum / expected);
1417
+ emit();
1418
+ },
1419
+ );
1420
+ task.catch(() => {});
1421
+ stripes.push(task);
1422
+ }
1423
+ const data = await withTimeout(
1424
+ firstFulfilled(stripes).catch((err) => {
1425
+ throw _err(
1426
+ "solve_failed",
1427
+ err?.message || "every hashwx worker failed",
1428
+ );
1429
+ }),
1430
+ `hashwx worker #${idx}`,
1431
+ );
1432
+ solutions[idx] = { nonce: data.nonce };
1433
+ log.debug(
1434
+ T("solve"),
1435
+ `hashwx #${idx}: ${data.hashes} hashes in ${data.durationMs}ms`,
1436
+ `compiled=${data.compiled}`,
1437
+ );
1438
+ inFlight[idx] = 0;
1439
+ completed++;
1440
+ emit();
1441
+ } catch (err) {
1442
+ await reclaimStripes(stripes);
1443
+ throw err;
1444
+ }
1445
+ return stripes;
1446
+ };
1447
+
1448
+ const hashwxJobs = [];
1449
+ challenges.forEach((ch, idx) => {
1450
+ if (ch.protocol === "hashwx") hashwxJobs.push({ ch, idx });
1451
+ });
1452
+ const hashwxSequence = hashwxJobs.length
1453
+ ? hashwxQueued
1454
+ ? Promise.all(hashwxJobs.map((job) => solveHashwx(job.ch, job.idx)))
1455
+ : (async () => {
1456
+ for (let i = 0; i < hashwxJobs.length; i++) {
1457
+ const stripes = await solveHashwx(
1458
+ hashwxJobs[i].ch,
1459
+ hashwxJobs[i].idx,
1460
+ );
1461
+ if (i < hashwxJobs.length - 1) await reclaimStripes(stripes);
1462
+ }
1463
+ })()
1464
+ : null;
1465
+
1259
1466
  try {
1260
- await raceAbort(Promise.all(
1261
- challenges.map((ch, idx) => {
1467
+ await raceAbort(Promise.all([
1468
+ hashwxSequence,
1469
+ ...challenges.map((ch, idx) => {
1262
1470
  if (ch.protocol === "sha256-pow") {
1263
1471
  return withTimeout(
1264
1472
  pool.run(ch.payload.salt, ch.payload.target),
@@ -1285,7 +1493,9 @@
1285
1493
  emit();
1286
1494
  });
1287
1495
  }
1288
-
1496
+
1497
+ if (ch.protocol === "hashwx") return null;
1498
+
1289
1499
  return runInstrumentationChallenge(ch.payload.blob).then((out) => {
1290
1500
  if (out?.__timeout) solutions[idx] = { timeout: true };
1291
1501
  else if (out?.__blocked) solutions[idx] = { blocked: true };
@@ -1295,9 +1505,10 @@
1295
1505
  emit();
1296
1506
  });
1297
1507
  }),
1298
- ), signal);
1508
+ ]), signal);
1299
1509
  } finally {
1300
1510
  pool.terminate();
1511
+ if (hashwxPool) hashwxPool.terminate();
1301
1512
  }
1302
1513
 
1303
1514
  return solutions;
@@ -1687,6 +1898,11 @@
1687
1898
  clearTimeout(this.#resetTimer);
1688
1899
  this.#resetTimer = null;
1689
1900
  }
1901
+ if (this.#speculativeTimer) {
1902
+ clearTimeout(this.#speculativeTimer);
1903
+ this.#speculativeTimer = null;
1904
+ }
1905
+ if (this.#speculative) this.#resetSpeculativeState();
1690
1906
  this.token = null;
1691
1907
  this.dispatchEvent("reset");
1692
1908
  this.#setToken("");
package/src/worker.js CHANGED
@@ -3,6 +3,18 @@
3
3
  if (!self.CAP_SILENT) console[method]("[cap worker]", ...args);
4
4
  };
5
5
 
6
+ const HASHWX_CLOCK_EVERY = 256;
7
+ const HASHWX_YIELD_MS = 16;
8
+ const HASHWX_PROGRESS_MS = 150;
9
+ let stopRequested = false;
10
+ const yieldChannel = typeof MessageChannel === "function" ? new MessageChannel() : null;
11
+ const macrotask = () =>
12
+ new Promise((resolve) => {
13
+ if (!yieldChannel) return setTimeout(resolve, 0);
14
+ yieldChannel.port1.onmessage = () => resolve();
15
+ yieldChannel.port2.postMessage(0);
16
+ });
17
+
6
18
  const solveFallback = async ({ salt, target }) => {
7
19
  let nonce = 0;
8
20
  const batchSize = 50000;
@@ -65,8 +77,8 @@
65
77
  };
66
78
 
67
79
  const solveRsw = (data) => {
68
- const N = BigInt("0x" + data.N);
69
- let y = BigInt("0x" + data.x);
80
+ const N = BigInt(`0x${data.N}`);
81
+ let y = BigInt(`0x${data.x}`);
70
82
  const t = data.t | 0;
71
83
  const chunk = Math.max(64, Math.floor(t / 50));
72
84
  const t0 = performance.now();
@@ -82,11 +94,191 @@
82
94
  });
83
95
  };
84
96
 
97
+ const SHA_K = new Uint32Array([
98
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
99
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
100
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
101
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
102
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
103
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
104
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
105
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
106
+ ]);
107
+
108
+ const sha256Into = (bytes, out) => {
109
+ const len = bytes.length;
110
+ const blocks = ((len + 8) >> 6) + 1;
111
+ const padded = new Uint8Array(blocks << 6);
112
+ padded.set(bytes);
113
+ padded[len] = 0x80;
114
+ const bitLen = len * 8;
115
+ const dv = new DataView(padded.buffer);
116
+ dv.setUint32(padded.length - 4, bitLen >>> 0, false);
117
+ dv.setUint32(padded.length - 8, Math.floor(bitLen / 0x100000000), false);
118
+
119
+ let h0 = 0x6a09e667, h1 = 0xbb67ae85, h2 = 0x3c6ef372, h3 = 0xa54ff53a;
120
+ let h4 = 0x510e527f, h5 = 0x9b05688c, h6 = 0x1f83d9ab, h7 = 0x5be0cd19;
121
+ const w = new Uint32Array(64);
122
+
123
+ for (let b = 0; b < blocks; b++) {
124
+ const off = b << 6;
125
+ for (let i = 0; i < 16; i++) w[i] = dv.getUint32(off + (i << 2), false);
126
+ for (let i = 16; i < 64; i++) {
127
+ const x = w[i - 15], y = w[i - 2];
128
+ const s0 = ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
129
+ const s1 = ((y >>> 17) | (y << 15)) ^ ((y >>> 19) | (y << 13)) ^ (y >>> 10);
130
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
131
+ }
132
+ let a = h0, bb = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
133
+ for (let i = 0; i < 64; i++) {
134
+ const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
135
+ const ch = (e & f) ^ (~e & g);
136
+ const t1 = (h + S1 + ch + SHA_K[i] + w[i]) >>> 0;
137
+ const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
138
+ const maj = (a & bb) ^ (a & c) ^ (bb & c);
139
+ const t2 = (S0 + maj) >>> 0;
140
+ h = g; g = f; f = e; e = (d + t1) >>> 0;
141
+ d = c; c = bb; bb = a; a = (t1 + t2) >>> 0;
142
+ }
143
+ h0 = (h0 + a) >>> 0; h1 = (h1 + bb) >>> 0; h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0;
144
+ h4 = (h4 + e) >>> 0; h5 = (h5 + f) >>> 0; h6 = (h6 + g) >>> 0; h7 = (h7 + h) >>> 0;
145
+ }
146
+
147
+ const odv = new DataView(out.buffer, out.byteOffset, 32);
148
+ odv.setUint32(0, h0, false); odv.setUint32(4, h1, false);
149
+ odv.setUint32(8, h2, false); odv.setUint32(12, h3, false);
150
+ odv.setUint32(16, h4, false); odv.setUint32(20, h5, false);
151
+ odv.setUint32(24, h6, false); odv.setUint32(28, h7, false);
152
+ };
153
+
154
+ let hashwxRuntime = null;
155
+
156
+ const getHashwxRuntime = (hashwxModule) => {
157
+ if (hashwxRuntime) return hashwxRuntime;
158
+
159
+ const instance = new WebAssembly.Instance(hashwxModule, {});
160
+ const w = instance.exports;
161
+ if (w._initialize) w._initialize();
162
+
163
+ let compiled = true;
164
+ let ctx = w.hashwx_alloc(1);
165
+ if (ctx === -1 || ctx === 0) {
166
+ compiled = false;
167
+ ctx = w.hashwx_alloc(0);
168
+ }
169
+ if (ctx <= 0) throw new Error("hashwx_alloc failed");
170
+
171
+ hashwxRuntime = {
172
+ w,
173
+ ctx,
174
+ compiled,
175
+ seedPtr: w.hashwx_seed(ctx),
176
+ regPtr: compiled ? w.hashwx_registers(ctx) : 0,
177
+ memPtr: compiled ? w.hashwx_memory(ctx) : 0,
178
+ };
179
+ return hashwxRuntime;
180
+ };
181
+
182
+ const solveHashwx = async (data) => {
183
+ const { c, d, n, workerIndex, workerCount, hashwxModule } = data;
184
+ if (!Number.isInteger(n) || n < 1 || !Number.isInteger(d) || d < 1) {
185
+ throw new Error("malformed hashwx challenge payload");
186
+ }
187
+ const noncesPerHash = n >>> 0;
188
+ const target = ((1n << 64n) - 1n) / BigInt(d);
189
+ stopRequested = false;
190
+
191
+ const { w, ctx, compiled, seedPtr, regPtr, memPtr } =
192
+ getHashwxRuntime(hashwxModule);
193
+
194
+ const challenge = new Uint8Array(32);
195
+ for (let i = 0; i < 32; i++) challenge[i] = parseInt(c.slice(i * 2, i * 2 + 2), 16);
196
+ const seedInput = new Uint8Array(40);
197
+ seedInput.set(challenge, 0);
198
+ const seedOut = new Uint8Array(32);
199
+
200
+ const t0 = performance.now();
201
+ let hashes = 0;
202
+ let lastYield = t0;
203
+ let lastReport = t0;
204
+
205
+ try {
206
+ for (let block = workerIndex; ; block += workerCount) {
207
+ let rest = block;
208
+ for (let i = 0; i < 8; i++) { seedInput[32 + i] = rest & 0xff; rest = Math.floor(rest / 256); }
209
+ sha256Into(seedInput, seedOut);
210
+ new Uint8Array(w.memory.buffer, seedPtr, 32).set(seedOut);
211
+ w.hashwx_make(ctx, seedPtr);
212
+
213
+ let side = null;
214
+ if (compiled) {
215
+ const codePtr = w.hashwx_module(ctx);
216
+ const codeSize = w.hashwx_module_size(ctx);
217
+ const bytes = new Uint8Array(w.memory.buffer, codePtr, codeSize).slice();
218
+ side = new WebAssembly.Instance(new WebAssembly.Module(bytes), {
219
+ env: { memory: w.memory },
220
+ }).exports.exec;
221
+ }
222
+
223
+ const base = BigInt(block) * BigInt(noncesPerHash);
224
+ for (let k = 0; k < noncesPerHash; k++) {
225
+ const nonce = base + BigInt(k);
226
+ let hash;
227
+ if (compiled) {
228
+ w.hashwx_exec_begin(ctx, nonce);
229
+ side(regPtr, memPtr);
230
+ hash = BigInt.asUintN(64, w.hashwx_exec_final(ctx));
231
+ } else {
232
+ hash = BigInt.asUintN(64, w.hashwx_exec(ctx, nonce));
233
+ }
234
+ hashes++;
235
+ if (hash <= target) {
236
+ self.postMessage({
237
+ found: true,
238
+ nonce: nonce.toString(),
239
+ hashes,
240
+ compiled,
241
+ durationMs: (performance.now() - t0).toFixed(2),
242
+ });
243
+ return;
244
+ }
245
+ if ((hashes & (HASHWX_CLOCK_EVERY - 1)) === 0) {
246
+ const now = performance.now();
247
+ if (now - lastYield >= HASHWX_YIELD_MS) {
248
+ if (now - lastReport >= HASHWX_PROGRESS_MS) {
249
+ lastReport = now;
250
+ self.postMessage({ progress: hashes });
251
+ }
252
+ await macrotask();
253
+ lastYield = performance.now();
254
+ if (stopRequested) {
255
+ return self.postMessage({ found: false, error: "stopped" });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ }
261
+ } catch (error) {
262
+ hashwxRuntime = null;
263
+ throw error;
264
+ }
265
+ };
266
+
85
267
  if (typeof WebAssembly !== "object" || typeof WebAssembly?.instantiate !== "function") {
86
268
  wlog("warn","WebAssembly unavailable, using JS fallback solver (significantly slower)");
87
269
 
88
270
  self.onmessage = async ({ data }) => {
271
+ if (data?.kind === "stop") {
272
+ stopRequested = true;
273
+ return;
274
+ }
89
275
  if (data?.kind === "rsw") return solveRsw(data);
276
+ if (data?.kind === "hashwx") {
277
+ return self.postMessage({
278
+ found: false,
279
+ error: "hashwx requires WebAssembly",
280
+ });
281
+ }
90
282
  return solveFallback({ salt: data.salt, target: data.target });
91
283
  };
92
284
 
@@ -179,7 +371,19 @@
179
371
  };
180
372
 
181
373
  self.onmessage = async ({ data }) => {
374
+ if (data?.kind === "stop") {
375
+ stopRequested = true;
376
+ return;
377
+ }
182
378
  if (data?.kind === "rsw") return solveRsw(data);
379
+ if (data?.kind === "hashwx") {
380
+ try {
381
+ return await solveHashwx(data);
382
+ } catch (error) {
383
+ wlog("error", "hashwx solver threw:", error.message || error);
384
+ return self.postMessage({ found: false, error: error.message || String(error) });
385
+ }
386
+ }
183
387
  const { salt, target, wasmModule } = data;
184
388
  if (wasmModule instanceof WebAssembly.Module && solve_pow_function === null) {
185
389
  const ok = initFromModule(wasmModule);