cap-widget 0.1.53 → 0.1.56

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,9 +2,12 @@ declare global {
2
2
  interface Window {
3
3
  CAP_CUSTOM_FETCH?: typeof fetch;
4
4
  CAP_CUSTOM_WASM_URL?: string;
5
+ CAP_PAKO_URL?: string;
5
6
  CAP_CSS_NONCE?: string;
6
7
  CAP_SCRIPT_NONCE?: string;
7
8
  CAP_DONT_SKIP_REDEFINE?: boolean;
9
+ CAP_DEBUG?: boolean;
10
+ CAP_SILENT?: boolean;
8
11
  Cap: typeof Cap;
9
12
  }
10
13
  }
@@ -17,8 +20,24 @@ interface CapSolveEventDetail {
17
20
  token: string;
18
21
  }
19
22
 
23
+ type CapErrorCode =
24
+ | "missing_endpoint"
25
+ | "network_error"
26
+ | "challenge_parse_error"
27
+ | "challenge_unsupported"
28
+ | "solve_failed"
29
+ | "instr_timeout"
30
+ | "instr_blocked"
31
+ | "redeem_failed"
32
+ | "invalid_solution"
33
+ | "invalid_expires"
34
+ | "wasm_load_failed"
35
+ | "worker_spawn_failed"
36
+ | "unknown";
37
+
20
38
  interface CapErrorEventDetail {
21
39
  isCap: boolean;
40
+ code: CapErrorCode;
22
41
  message: string;
23
42
  }
24
43
 
@@ -116,6 +135,7 @@ export {
116
135
  type CapProgressEvent,
117
136
  type CapSolveEvent,
118
137
  type CapErrorEvent,
138
+ type CapErrorCode,
119
139
  type CapResetEvent,
120
140
  type SolveResult,
121
141
  };
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=(e,t={})=>window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(e,t):fetch(e,t);function i(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 s=null;async function r(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 s||(s=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=document.createElement("script");r.src=i;const n=window.CAP_SCRIPT_NONCE||window.CAP_CSS_NONCE;n&&r.setAttribute("nonce",n),r.onload=()=>{window.pako?.inflateRaw?e(window.pako):t(new Error("[cap] pako loaded but inflateRaw is missing"))},r.onerror=()=>{s=null,t(new Error(`[cap] failed to load pako fallback from ${i}`))},document.head.appendChild(r)})),(await s).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 n=null;const a=()=>{if(n)return n;const e=window.CAP_CUSTOM_WASM_URL||"https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.7/browser/cap_wasm_bg.wasm";return n=fetch(e).then(e=>{if(!e.ok)throw new Error(`Failed to fetch wasm: ${e.status}`);return e.arrayBuffer()}).then(e=>WebAssembly.compile(e)).catch(e=>{throw n=null,e}),n};"object"==typeof WebAssembly&&"function"==typeof WebAssembly.compile&&a().catch(()=>{});let o=null;function l(){return o||(o=URL.createObjectURL(new Blob(['(()=>{const e=async({salt:e,target:t})=>{let n=0;const o=new TextEncoder,r=4*t.length,s=Math.floor(r/8),a=r%8,l=t.length%2==0?t:`${t}0`,i=l.length/2,c=new Uint8Array(i);for(let e=0;e<i;e++)c[e]=parseInt(l.substring(2*e,2*e+2),16);const f=a>0?255<<8-a&255:0;for(;;)try{for(let t=0;t<5e4;t++){const t=e+n,r=o.encode(t),l=await crypto.subtle.digest("SHA-256",r),i=new Uint8Array(l);let g=!0;for(let e=0;e<s;e++)if(i[e]!==c[e]){g=!1;break}if(g&&a>0&&(i[s]&f)!==(c[s]&f)&&(g=!1),g)return void self.postMessage({nonce:n,found:!0});n++}}catch(e){return console.error("[cap worker]",e),void self.postMessage({found:!1,error:e.message})}},t=e=>{const t=BigInt("0x"+e.N);let n=BigInt("0x"+e.x);const o=0|e.t,r=Math.max(64,Math.floor(o/50)),s=performance.now();for(let e=0;e<o;e+=r){const s=Math.min(o,e+r);for(let o=e;o<s;o++)n=n*n%t;s<o&&self.postMessage({progress:s/o})}self.postMessage({found:!0,y:n.toString(16),durationMs:(performance.now()-s).toFixed(2)})};if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return console.warn("[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower."),void(self.onmessage=async({data:n})=>"rsw"===n?.kind?t(n):e({salt:n.salt,target:n.target}));let n=null;self.onmessage=async({data:o})=>{if("rsw"===o?.kind)return t(o);const{salt:r,target:s,wasmModule:a}=o;if(a instanceof WebAssembly.Module&&null===n){const t=(e=>{try{let t,o=0,r=null;const s=()=>(null!==r&&0!==r.byteLength||(r=new Uint8Array(t.memory.buffer)),r),a=new TextEncoder,l=(e,t,n)=>{if(void 0===n){const n=a.encode(e),r=t(n.length,1)>>>0;return s().subarray(r,r+n.length).set(n),o=n.length,r}let r=e.length,l=t(r,1)>>>0;const i=s();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=n(l,r,t,1)>>>0,r=t;const o=s().subarray(l+c,l+r),{written:i}=a.encodeInto(e,o);c+=i,l=n(l,r,c,1)>>>0}return o=c,l},i={wbg:{}};i.wbg.__wbindgen_init_externref_table=()=>{const e=t.__wbindgen_export_0,n=e.grow(4);e.set(0,void 0),e.set(n+0,void 0),e.set(n+1,null),e.set(n+2,!0),e.set(n+3,!1)};const c=new WebAssembly.Instance(e,i);return t=c.exports,t.__wbindgen_start&&t.__wbindgen_start(),n=(e,n)=>{const r=l(e,t.__wbindgen_malloc,t.__wbindgen_realloc),s=o,a=l(n,t.__wbindgen_malloc,t.__wbindgen_realloc),i=o;return BigInt.asUintN(64,t.solve_pow(r,s,a,i))},!0}catch(e){return console.error("[cap worker] failed to init wasm from module:",e),!1}})(a);if(!t)return console.warn("[cap worker] wasm init failed, falling back to JS solver."),e({salt:r,target:s})}if(null===n)return console.warn("[cap worker] no wasm module provided, falling back to JS solver."),e({salt:r,target:s});try{const e=performance.now(),t=n(r,s),o=performance.now();self.postMessage({nonce:Number(t),found:!0,durationMs:(o-e).toFixed(2)})}catch(e){console.error("[cap worker]",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:e})}})();'],{type:"application/javascript"})),o)}class c{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=l(),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++,this._spawnFailures>3?(console.error("[cap] worker spawn failed repeatedly, not retrying"),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 h 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;#p=null;#u=null;#g=null;get#v(){return e&&!window.CAP_DISABLE_HAPTICS&&!this.hasAttribute("data-cap-disable-haptics")}#m(){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)}}}#b(){this.#d=this.#m(),this.#f()}#w(){this.#g&&(window.removeEventListener("mousemove",this.#g),window.removeEventListener("touchstart",this.#g),window.removeEventListener("keydown",this.#g),this.#g=null)}#f(){this.#w();const e=()=>{this.#w(),this.#y()};this.#g=e,window.addEventListener("mousemove",e,{passive:!0}),window.addEventListener("touchstart",e,{passive:!0}),window.addEventListener("keydown",e,{passive:!0})}#k(){return"function"==typeof this.checkVisibility?this.checkVisibility({checkOpacity:!0,checkVisibilityCSS:!0}):!!(this.offsetParent||this.getClientRects().length>0)}#y(){"idle"===this.#d.state&&this.#k()&&(this.#d.state="waiting",this.#p=setTimeout(()=>{this.#x()},2500))}async#x(){if("waiting"!==this.#d.state)return;this.#d.state="fetching";let e=this.getAttribute("data-cap-api-endpoint");if(!e&&window?.CAP_CUSTOM_FETCH&&(e="/"),e){e.endsWith("/")||(e+="/");try{const s=await t(`${e}challenge`,{method:"POST"});let r;try{r=await s.json()}catch{throw new Error("Failed to parse speculative challenge response")}if(r.error)throw new Error(r.error);if(r._apiEndpoint=e,this.#d.challengeResp=r,2===r.format&&Array.isArray(r.challenges))return this.#d.state="idle",void this.#d.notify();const{challenge:n,token:a}=r;let o=n;if(!Array.isArray(o)){let e=0;o=Array.from({length:n.c},()=>(e++,[i(`${a}${e}`,n.s),i(`${a}${e}d`,n.d)]))}this.#d.challenges=o,this.#d.state="solving",this.#d.solvePromise=this.#_(o)}catch(e){console.warn("[cap] speculative challenge fetch failed:",e),this.#d.state="error",this.#d.notify()}}else this.#d.state="idle"}async#_(e){l();let t=null;try{t=await a()}catch{}this.#u||(this.#u=new c(1),this.#u._spawn()),this.#u.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.#u._size=e,this.#u._ensureSize(e))},null!==this.#d.pendingPromotion&&(this.#d.promoteFn(this.#d.pendingPromotion),this.#d.pendingPromotion=null);let o=0;for(;o<i;){const t=r,a=[],l=[];for(let s=0;s<t&&o<i;s++)l.push(o),a.push(e[o]),o++;this.#u._ensureSize(Math.max(r,t));const c=await Promise.all(a.map(e=>this.#u.run(e[0],e[1]).then(e=>(this.#d.completedCount++,e))));for(let e=0;e<l.length;e++)s[l[e]]=c[e];!n&&o<i&&await new Promise(e=>setTimeout(e,120))}return this.#d.results=s,this.#d.state="redeeming",this.#E(s),s}async#E(e){try{const i=this.#d.challengeResp,s=i._apiEndpoint;if(!s)throw new Error("[cap] speculative redeem: missing apiEndpoint");let n=null;if(i.instrumentation&&(n=await r(i.instrumentation),n?.__timeout||n?.__blocked))return this.#d.state="done",void this.#d.notify();const a=await t(`${s}redeem`,{method:"POST",body:JSON.stringify({token:i.token,solutions:e,...n&&{instr:n}}),headers:{"Content-Type":"application/json"}});let o;try{o=await a.json()}catch{throw new Error("Failed to parse speculative redeem response")}if(!o.success)throw new Error(o.error||"Speculative redeem failed");this.#d.token=o.token,this.#d.tokenExpires=new Date(o.expires).getTime(),this.#d.state="done",this.#d.notify()}catch(e){console.warn("[cap] speculative redeem failed (will redo on click):",e),this.#d.state="done",this.#d.notify()}}getI18nText(e,t){return this.getAttribute(`data-cap-i18n-${e}`)||t}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{}}#A(){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(){l(),this.#d||(this.#d=this.#m()),this.#u||(this.#u=new c(1),this.#u._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.#A()}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.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);const i=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#o.innerHTML=`<input type="hidden" name="${i}">`,this.#f(),this.#A(),this.addEventListener("invalid",this.#C)}#C=()=>{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){this.#S();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 e,s,n=this.getAttribute("data-cap-api-endpoint");if(!n&&window?.CAP_CUSTOM_FETCH)n="/";else if(!n)throw new Error("Missing API endpoint. Either custom fetch or an API endpoint must be provided.");if(n.endsWith("/")||(n+="/"),"done"===this.#d.state&&this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const t=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),this.#b(),this.#l=!1,{success:!0,token:this.token}}if("done"===this.#d.state)e=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.#p&&(clearTimeout(this.#p),this.#p=null),this.#d.state="waiting",this.#x()),this.#d.pendingPromotion=this.#t,this.#d.promoteFn&&this.#d.promoteFn(this.#t);const t=setInterval(()=>{const e=this.#d.state;if("done"===e||"error"===e)return void clearInterval(t);const i=this.#d.challenges?this.#d.challenges.length:1,s=this.#d.completedCount,r="redeeming"===e?99:"fetching"===e||"waiting"===e?0:Math.min(98,Math.round(s/i*100));this.dispatchEvent("progress",{progress:r})},150);if(await new Promise(e=>this.#d.onSettled(e)),clearInterval(t),"idle"===this.#d.state&&2===this.#d.challengeResp?.format&&Array.isArray(this.#d.challengeResp.challenges))s=this.#d.challengeResp,this.#d.challengeResp=null,e=await this.solveChallengesV2(s.challenges);else{if("done"!==this.#d.state)throw new Error("Speculative solve failed – please try again");if(this.#d.token&&this.#d.tokenExpires&&Date.now()<this.#d.tokenExpires){this.dispatchEvent("progress",{progress:100});const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value=this.#d.token),this.dispatchEvent("solve",{token:this.#d.token}),this.token=this.#d.token;const t=this.#d.tokenExpires-Date.now();return this.#e&&clearTimeout(this.#e),this.#e=setTimeout(()=>this.reset(),t),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),this.#b(),this.#l=!1,{success:!0,token:this.token}}e=this.#d.results,s=this.#d.challengeResp,this.dispatchEvent("progress",{progress:100})}}else{const r=this.#d.challengeResp;if(2===r?.format&&Array.isArray(r.challenges))s=r,this.#d.challengeResp=null;else{const e=await t(`${n}challenge`,{method:"POST"});try{s=await e.json()}catch{throw new Error("Failed to parse challenge response from server")}if(s.error)throw new Error(s.error)}if(2===s.format&&Array.isArray(s.challenges))e=await this.solveChallengesV2(s.challenges);else{const{challenge:t,token:r}=s;let n=t;if(!Array.isArray(n)){let e=0;n=Array.from({length:t.c},()=>(e++,[i(`${r}${e}`,t.s),i(`${r}${e}d`,t.d)]))}e=await this.solveChallenges(n)}}const a=s.instrumentation?r(s.instrumentation):Promise.resolve(null),o=await a;if(o?.__timeout||o?.__blocked){t(`${n}redeem`,{method:"POST",body:JSON.stringify({token:s.token,solutions:e,...o.__blocked&&{instr_blocked:!0},...o.__timeout&&{instr_timeout:!0}}),headers:{"Content-Type":"application/json"}}).catch(()=>{}),this.updateUIBlocked(this.getI18nText("error-label","Error"),o?.__blocked),this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.removeEventListener("error",this.boundHandleError);const i=new CustomEvent("error",{bubbles:!0,composed:!0,detail:{isCap:!0,message:"Instrumentation failed"}});return super.dispatchEvent(i),this.addEventListener("error",this.boundHandleError),this.executeAttributeCode("onerror",i),console.error("[cap]","Instrumentation failed"),void(this.#l=!1)}const{token:l}=s,c=await t(`${n}redeem`,{method:"POST",body:JSON.stringify({token:l,solutions:e,...o&&{instr:o}}),headers:{"Content-Type":"application/json"}});let h;try{h=await c.json()}catch{throw new Error("Failed to parse server response")}if(this.dispatchEvent("progress",{progress:100}),!h.success)throw new Error(h.error||"Invalid solution");const d=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${d}']`)&&(this.querySelector(`input[name='${d}']`).value=h.token),this.dispatchEvent("solve",{token:h.token}),this.token=h.token,this.#b(),this.#e&&clearTimeout(this.#e);const p=new Date(h.expires).getTime()-Date.now();return p>0&&p<864e5?this.#e=setTimeout(()=>this.reset(),p):this.error("Invalid expiration time"),this.#r.setAttribute("aria-label",this.getI18nText("verified-aria-label","We have verified you're a human, you may now continue")),this.#v&&navigator.vibrate([10,50,20,30,40]),{success:!0,token:this.token}}catch(e){throw this.#r.setAttribute("aria-label",this.getI18nText("error-aria-label","An error occurred, please try again")),this.error(e.message),e}}finally{this.#l=!1}}}async solveChallengesV2(e){const t=e.length;let i=0;const s=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 new Error(`[cap] unsupported format-2 protocol '${i?.protocol}'`)}let n=null;if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate)try{n=await a()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}const o=Math.max(1,Math.min(this.#t,e.length)),l=new c(o);l.setWasm(n),l._ensureSize(o);const h=(e,t)=>Promise.race([e,new Promise((e,i)=>setTimeout(()=>i(new Error(`[cap] ${t} timed out after 60000ms`)),6e4))]),d=new Array(e.length).fill(0),p=()=>{const e=d.reduce((e,t)=>e+t,0)+i,s=Math.min(99,Math.round(e/t*100));this.dispatchEvent("progress",{progress:s})};try{await Promise.all(e.map((e,t)=>"sha256-pow"===e.protocol?h(l.run(e.payload.salt,e.payload.target),`sha256-pow worker #${t}`).then(e=>{s[t]={nonce:Number(e)},d[t]=0,i++,p()}):"rsw"===e.protocol?h(l.runMsg({kind:"rsw",N:e.payload.N,x:e.payload.x,t:0|e.payload.t},e=>{d[t]=e,p()}),`rsw worker #${t}`).then(e=>{s[t]={y:e.y},d[t]=0,i++,p()}):r(e.payload.blob).then(e=>{s[t]=e?.__timeout?{timeout:!0}:e?.__blocked?{blocked:!0}:{instr:e},d[t]=0,i++,p()})))}finally{l.terminate()}return s}async solveChallenges(e){const t=e.length;let i=0;let s=null;const r="object"==typeof WebAssembly&&"function"==typeof WebAssembly.instantiate;if(r)try{s=await a()}catch(e){console.warn("[cap] wasm unavailable, falling back to JS solver:",e)}if(!r&&!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 n=new c(this.#t);n.setWasm(s),n._ensureSize(this.#t);const o=[];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 Promise.all(r.map(([e,s])=>n.run(e,s).then(e=>{i++;const s=Math.min(99,Math.round((0+i)/t*100));return this.dispatchEvent("progress",{progress:s}),e})));o.push(...a)}}finally{n.terminate()}return o}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.#S(),setTimeout(()=>this.#S(),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.#v&&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))}#T(){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}#S(){this.#n&&this.#s&&!this.#T()&&(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.#v&&navigator.vibrate([10,40,10])}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e),this.#A()}executeAttributeCode(e,t){const i=this.getAttribute(e);i&&(console.error("[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead."),new Function("event",i).call(this,t))}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,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");const e=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${e}']`)&&(this.querySelector(`input[name='${e}']`).value="")}get tokenValue(){return this.token}disconnectedCallback(){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.#w(),this.#p&&(clearTimeout(this.#p),this.#p=null),this.#u&&(this.#u.terminate(),this.#u=null),this.#d&&(this.#d.state="error",this.#d.notify(),this.#d=null)}}class d{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=d,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?customElements.get("cap-widget")&&console.warn("[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true"):customElements.define("cap-widget",h),"object"==typeof exports&&"undefined"!=typeof module?module.exports=d:"function"==typeof define&&define.amd&&define([],()=>d),"undefined"!=typeof exports&&(exports.default=d)})();
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)})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cap-widget",
3
- "version": "0.1.53",
3
+ "version": "0.1.56",
4
4
  "description": "The self-hosted CAPTCHA for the modern web.",
5
5
  "keywords": [
6
6
  "algorithm",
package/src/cap.js CHANGED
@@ -8,6 +8,31 @@
8
8
  return;
9
9
  }
10
10
 
11
+ const _ctp = ["#f5c2e7","#cba6f7","#f38ba8","#fab387","#f9e2af","#a6e3a1","#94e2d5","#89dceb","#b4befe"];
12
+ const _bg = (s) => {
13
+ if (s === "cap") return "#89b4fa";
14
+ let h = 0;
15
+ for (let i = 0; i < s.length; i++) h = s.charCodeAt(i) + ((h << 5) - h);
16
+ return _ctp[Math.abs(h) % _ctp.length];
17
+ };
18
+ const _style = (t, i, n) => {
19
+ const l = i === 0 ? "9999px" : "0";
20
+ const r = i === n - 1 ? "9999px" : "0";
21
+ return `color:#1e1e2e;background:${_bg(t)};margin-left:${i ? "-6px" : 0};padding:0 6px;border-radius:${l} ${r} ${r} ${l};font-weight:600`;
22
+ };
23
+ const log = {};
24
+ for (const lvl of ["debug", "info", "warn", "error"]) {
25
+ log[lvl] = (tags, ...args) => {
26
+ if (window.CAP_SILENT || (lvl === "debug" && !window.CAP_DEBUG)) return;
27
+ const fmt = tags.map((t) => `%c${t}`).join(" ");
28
+ const styles = tags.map((t, i) => _style(t, i, tags.length));
29
+ console[lvl === "debug" ? "log" : lvl](fmt, ...styles, ...args);
30
+ };
31
+ }
32
+ const T = (sub) => (sub ? ["cap", sub] : ["cap"]);
33
+ const since = (t) => `${Math.round(performance.now() - t)}ms`;
34
+ const _err = (code, message) => Object.assign(new Error(message), { code });
35
+
11
36
  const capFetch = (u, conf = {}) => {
12
37
  if (window?.CAP_CUSTOM_FETCH) {
13
38
  return window.CAP_CUSTOM_FETCH(u, conf);
@@ -16,6 +41,48 @@
16
41
  return fetch(u, conf);
17
42
  };
18
43
 
44
+ const raceAbort = (promise, signal) => {
45
+ if (!signal) return promise;
46
+ if (signal.aborted) return Promise.reject(_err("aborted", "aborted"));
47
+ return Promise.race([
48
+ promise,
49
+ new Promise((_, reject) =>
50
+ signal.addEventListener(
51
+ "abort",
52
+ () => reject(_err("aborted", "aborted")),
53
+ { once: true },
54
+ ),
55
+ ),
56
+ ]);
57
+ };
58
+
59
+ const I18N_KEYS = "%%i18nKeys%%".split(",");
60
+ const I18N_ROWS = %%i18nData%%;
61
+
62
+ function _resolveI18nMap(forced) {
63
+ const prefs = forced
64
+ ? [forced]
65
+ : navigator.languages || [navigator.language || ""];
66
+ for (let pref of prefs) {
67
+ if (!pref) continue;
68
+ pref = pref.toLowerCase();
69
+ if (pref === "en" || pref.startsWith("en-")) return null;
70
+ const code = I18N_ROWS[pref]
71
+ ? pref
72
+ : I18N_ROWS[pref.split("-")[0]]
73
+ ? pref.split("-")[0]
74
+ : null;
75
+ if (!code) return null;
76
+ const parts = I18N_ROWS[code].split("/");
77
+ const map = {};
78
+ I18N_KEYS.forEach((k, i) => {
79
+ map[k] = parts[i];
80
+ });
81
+ return map;
82
+ }
83
+ return null;
84
+ }
85
+
19
86
  function prng(seed, length) {
20
87
  function fnv1a(str) {
21
88
  let hash = 2166136261;
@@ -78,17 +145,18 @@
78
145
  const url =
79
146
  window.CAP_PAKO_URL ||
80
147
  "https://cdn.jsdelivr.net/npm/pako@2.1.0/dist/pako_inflate.min.js";
148
+ log.debug(T("instr"), "DecompressionStream unavailable, loading pako from", url);
81
149
  const script = document.createElement("script");
82
150
  script.src = url;
83
151
  const pakoNonce = window.CAP_SCRIPT_NONCE || window.CAP_CSS_NONCE;
84
152
  if (pakoNonce) script.setAttribute("nonce", pakoNonce);
85
153
  script.onload = () => {
86
154
  if (window.pako?.inflateRaw) resolve(window.pako);
87
- else reject(new Error("[cap] pako loaded but inflateRaw is missing"));
155
+ else reject(new Error("pako loaded but inflateRaw is missing"));
88
156
  };
89
157
  script.onerror = () => {
90
158
  _pakoPromise = null;
91
- reject(new Error(`[cap] failed to load pako fallback from ${url}`));
159
+ reject(new Error(`failed to load pako fallback from ${url}`));
92
160
  };
93
161
  document.head.appendChild(script);
94
162
  });
@@ -178,14 +246,21 @@
178
246
  window.CAP_CUSTOM_WASM_URL ||
179
247
  `https://cdn.jsdelivr.net/npm/@cap.js/wasm@${WASM_VERSION}/browser/cap_wasm_bg.wasm`;
180
248
 
249
+ const t0 = performance.now();
250
+ log.debug(T("wasm"), "fetching", wasmUrl);
181
251
  wasmModulePromise = fetch(wasmUrl)
182
252
  .then((r) => {
183
253
  if (!r.ok) throw new Error(`Failed to fetch wasm: ${r.status}`);
184
254
  return r.arrayBuffer();
185
255
  })
186
256
  .then((buf) => WebAssembly.compile(buf))
257
+ .then((mod) => {
258
+ log.debug(T("wasm"), `ready in ${since(t0)}`);
259
+ return mod;
260
+ })
187
261
  .catch((e) => {
188
262
  wasmModulePromise = null;
263
+ log.warn(T("wasm"), `load failed (${since(t0)}):`, e.message || e);
189
264
  throw e;
190
265
  });
191
266
 
@@ -251,8 +326,9 @@
251
326
  } catch {}
252
327
 
253
328
  this._spawnFailures++;
329
+ log.warn(T("pool"), `worker died, replacing (attempt ${this._spawnFailures}/3)`);
254
330
  if (this._spawnFailures > 3) {
255
- console.error("[cap] worker spawn failed repeatedly, not retrying");
331
+ log.error(T("pool"), "worker spawn failed repeatedly, giving up");
256
332
  return null;
257
333
  }
258
334
 
@@ -367,6 +443,8 @@
367
443
  #speculativeTimer = null;
368
444
  #speculativePool = null;
369
445
  #interactionHandler = null;
446
+ #i18n = null;
447
+ #abort = null;
370
448
 
371
449
  get #hasHaptics() {
372
450
  return (
@@ -438,10 +516,13 @@
438
516
  checkVisibilityCSS: true,
439
517
  });
440
518
  }
441
- // Fallback: offsetParent is null for display:none; also check the style directly
442
519
  return !!(this.offsetParent || this.getClientRects().length > 0);
443
520
  }
444
521
 
522
+ #logInvisible() {
523
+ if (!this.#isVisible()) log.info(T("challenges"), "solved invisible challenge");
524
+ }
525
+
445
526
  #onFirstInteraction() {
446
527
  if (this.#speculative.state !== "idle") return;
447
528
  if (!this.#isVisible()) return;
@@ -454,6 +535,7 @@
454
535
  async #beginSpeculativeSolve() {
455
536
  if (this.#speculative.state !== "waiting") return;
456
537
  this.#speculative.state = "fetching";
538
+ this.#speculative._t0 = performance.now();
457
539
 
458
540
  let apiEndpoint = this.getAttribute("data-cap-api-endpoint");
459
541
  if (!apiEndpoint && window?.CAP_CUSTOM_FETCH) {
@@ -468,6 +550,7 @@
468
550
  try {
469
551
  const raw = await capFetch(`${apiEndpoint}challenge`, {
470
552
  method: "POST",
553
+ signal: this.#abort?.signal,
471
554
  });
472
555
  let resp;
473
556
  try {
@@ -476,6 +559,7 @@
476
559
  throw new Error("Failed to parse speculative challenge response");
477
560
  }
478
561
  if (resp.error) throw new Error(resp.error);
562
+ if (!this.#speculative) return;
479
563
 
480
564
  resp._apiEndpoint = apiEndpoint;
481
565
  this.#speculative.challengeResp = resp;
@@ -502,8 +586,8 @@
502
586
  this.#speculative.state = "solving";
503
587
 
504
588
  this.#speculative.solvePromise = this.#speculativeSolveAll(challenges);
505
- } catch (e) {
506
- console.warn("[cap] speculative challenge fetch failed:", e);
589
+ } catch {
590
+ if (!this.#speculative) return;
507
591
  this.#speculative.state = "error";
508
592
  this.#speculative.notify();
509
593
  }
@@ -562,7 +646,7 @@
562
646
  this.#speculativePool
563
647
  .run(challenge[0], challenge[1])
564
648
  .then((nonce) => {
565
- this.#speculative.completedCount++;
649
+ if (this.#speculative) this.#speculative.completedCount++;
566
650
  return nonce;
567
651
  }),
568
652
  ),
@@ -579,6 +663,7 @@
579
663
  }
580
664
  }
581
665
 
666
+ if (!this.#speculative) return results;
582
667
  this.#speculative.results = results;
583
668
  this.#speculative.state = "redeeming";
584
669
  this.#speculativeRedeem(results);
@@ -587,16 +672,18 @@
587
672
 
588
673
  async #speculativeRedeem(solutions) {
589
674
  try {
675
+ if (!this.#speculative) return;
590
676
  const challengeResp = this.#speculative.challengeResp;
591
677
  const apiEndpoint = challengeResp._apiEndpoint;
592
678
  if (!apiEndpoint)
593
- throw new Error("[cap] speculative redeem: missing apiEndpoint");
679
+ throw _err("missing_endpoint", "speculative redeem: missing apiEndpoint");
594
680
 
595
681
  let instrOut = null;
596
682
  if (challengeResp.instrumentation) {
597
683
  instrOut = await runInstrumentationChallenge(
598
684
  challengeResp.instrumentation,
599
685
  );
686
+ if (!this.#speculative) return;
600
687
  if (instrOut?.__timeout || instrOut?.__blocked) {
601
688
  this.#speculative.state = "done";
602
689
  this.#speculative.notify();
@@ -612,6 +699,7 @@
612
699
  ...(instrOut && { instr: instrOut }),
613
700
  }),
614
701
  headers: { "Content-Type": "application/json" },
702
+ signal: this.#abort?.signal,
615
703
  });
616
704
 
617
705
  let resp;
@@ -621,25 +709,71 @@
621
709
  throw new Error("Failed to parse speculative redeem response");
622
710
  }
623
711
 
712
+ if (!this.#speculative) return;
624
713
  if (!resp.success)
625
714
  throw new Error(resp.error || "Speculative redeem failed");
626
715
 
627
716
  this.#speculative.token = resp.token;
628
717
  this.#speculative.tokenExpires = new Date(resp.expires).getTime();
629
718
  this.#speculative.state = "done";
719
+ this.#speculative._invisibleElapsed = this.#speculative._t0 ? since(this.#speculative._t0) : "?";
630
720
  this.#speculative.notify();
631
- } catch (e) {
632
- console.warn(
633
- "[cap] speculative redeem failed (will redo on click):",
634
- e,
635
- );
721
+ } catch {
722
+ if (!this.#speculative) return;
636
723
  this.#speculative.state = "done";
637
724
  this.#speculative.notify();
638
725
  }
639
726
  }
640
727
 
728
+ get #fieldName() {
729
+ return this.getAttribute("data-cap-hidden-field-name") || "cap-token";
730
+ }
731
+
732
+ #setToken(value) {
733
+ const input = this.querySelector(`input[name='${this.#fieldName}']`);
734
+ if (input) input.value = value;
735
+ }
736
+
641
737
  getI18nText(key, defaultValue) {
642
- return this.getAttribute(`data-cap-i18n-${key}`) || defaultValue;
738
+ return (
739
+ this.getAttribute(`data-cap-i18n-${key}`) ||
740
+ this.#i18n?.[key] ||
741
+ defaultValue
742
+ );
743
+ }
744
+
745
+ #commitSpeculativeToken() {
746
+ log.debug(T("solve"), `served from speculative cache (saved ${this.#speculative._invisibleElapsed || "?"})`);
747
+ this.dispatchEvent("progress", { progress: 100 });
748
+
749
+ this.#setToken(this.#speculative.token);
750
+
751
+ this.dispatchEvent("solve", { token: this.#speculative.token });
752
+ this.token = this.#speculative.token;
753
+
754
+ const expiresIn = this.#speculative.tokenExpires - Date.now();
755
+ if (this.#resetTimer) clearTimeout(this.#resetTimer);
756
+ this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
757
+
758
+ this.#trigger.setAttribute(
759
+ "aria-label",
760
+ this.getI18nText(
761
+ "verified-aria-label",
762
+ "We have verified you're a human, you may now continue",
763
+ ),
764
+ );
765
+ if (this.#hasHaptics) navigator.vibrate([10, 50, 20, 30, 40]);
766
+
767
+ this.#logInvisible();
768
+ this.#resetSpeculativeState();
769
+ this.#solving = false;
770
+ return { success: true, token: this.token };
771
+ }
772
+
773
+ #resolveI18n() {
774
+ this.#i18n = _resolveI18nMap(
775
+ window.CAP_LANG || this.getAttribute("data-cap-lang"),
776
+ );
643
777
  }
644
778
 
645
779
  static get observedAttributes() {
@@ -688,6 +822,7 @@
688
822
 
689
823
  initialize() {
690
824
  _getSharedWorkerUrl();
825
+ this.#abort = new AbortController();
691
826
  if (!this.#speculative) {
692
827
  this.#speculative = this.#makeSpeculativeState();
693
828
  }
@@ -746,6 +881,7 @@
746
881
  }
747
882
 
748
883
  if (!this.#div) this.#div = document.createElement("div");
884
+ this.#resolveI18n();
749
885
  this.createUI();
750
886
  this.addEventListeners();
751
887
  this.initialize();
@@ -754,10 +890,9 @@
754
890
  const workers = this.getAttribute("data-cap-worker-count");
755
891
  const parsedWorkers = workers ? parseInt(workers, 10) : null;
756
892
  this.setWorkersCount(parsedWorkers || navigator.hardwareConcurrency || 8);
757
- const fieldName =
758
- this.getAttribute("data-cap-hidden-field-name") || "cap-token";
759
- this.#host.innerHTML = `<input type="hidden" name="${fieldName}">`;
893
+ this.#host.innerHTML = `<input type="hidden" name="${this.#fieldName}">`;
760
894
 
895
+ log.debug(T(), `widget ready (workers=${this.#workersCount}, haptics=${this.#hasHaptics})`);
761
896
  this.#attachInteractionListeners();
762
897
  this.#updateValidity();
763
898
 
@@ -783,6 +918,9 @@
783
918
  }
784
919
 
785
920
  this.#enforceCredits();
921
+ const _solveT0 = performance.now();
922
+ const signal = this.#abort?.signal;
923
+ log.debug(T("solve"), "starting");
786
924
 
787
925
  try {
788
926
  this.#solving = true;
@@ -805,7 +943,8 @@
805
943
  if (!apiEndpoint && window?.CAP_CUSTOM_FETCH) {
806
944
  apiEndpoint = "/";
807
945
  } else if (!apiEndpoint) {
808
- throw new Error(
946
+ throw _err(
947
+ "missing_endpoint",
809
948
  "Missing API endpoint. Either custom fetch or an API endpoint must be provided.",
810
949
  );
811
950
  }
@@ -820,33 +959,7 @@
820
959
  this.#speculative.tokenExpires &&
821
960
  Date.now() < this.#speculative.tokenExpires
822
961
  ) {
823
- this.dispatchEvent("progress", { progress: 100 });
824
-
825
- const fieldName =
826
- this.getAttribute("data-cap-hidden-field-name") || "cap-token";
827
- if (this.querySelector(`input[name='${fieldName}']`)) {
828
- this.querySelector(`input[name='${fieldName}']`).value =
829
- this.#speculative.token;
830
- }
831
- this.dispatchEvent("solve", { token: this.#speculative.token });
832
- this.token = this.#speculative.token;
833
-
834
- const expiresIn = this.#speculative.tokenExpires - Date.now();
835
- if (this.#resetTimer) clearTimeout(this.#resetTimer);
836
- this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
837
-
838
- this.#trigger.setAttribute(
839
- "aria-label",
840
- this.getI18nText(
841
- "verified-aria-label",
842
- "We have verified you're a human, you may now continue",
843
- ),
844
- );
845
- if (this.#hasHaptics) navigator.vibrate([10, 50, 20, 30, 40]);
846
-
847
- this.#resetSpeculativeState();
848
- this.#solving = false;
849
- return { success: true, token: this.token };
962
+ return this.#commitSpeculativeToken();
850
963
  }
851
964
 
852
965
  if (this.#speculative.state === "done") {
@@ -874,6 +987,10 @@
874
987
  }
875
988
 
876
989
  const progressInterval = setInterval(() => {
990
+ if (signal?.aborted || !this.#speculative) {
991
+ clearInterval(progressInterval);
992
+ return;
993
+ }
877
994
  const st = this.#speculative.state;
878
995
  if (st === "done" || st === "error") {
879
996
  clearInterval(progressInterval);
@@ -896,6 +1013,7 @@
896
1013
  this.#speculative.onSettled(resolve),
897
1014
  );
898
1015
  clearInterval(progressInterval);
1016
+ if (signal?.aborted || !this.#speculative) return;
899
1017
 
900
1018
  if (
901
1019
  this.#speculative.state === "idle" &&
@@ -904,10 +1022,13 @@
904
1022
  ) {
905
1023
  challengeResp = this.#speculative.challengeResp;
906
1024
  this.#speculative.challengeResp = null;
907
- solutions = await this.solveChallengesV2(challengeResp.challenges);
1025
+ solutions = await this.solveChallengesV2(
1026
+ challengeResp.challenges,
1027
+ signal,
1028
+ );
908
1029
  } else {
909
1030
  if (this.#speculative.state !== "done") {
910
- throw new Error("Speculative solve failed – please try again");
1031
+ throw _err("solve_failed", "Unable to solve challenge, self-hosted instance likely down. This is not an issue with Cap.");
911
1032
  }
912
1033
 
913
1034
  if (
@@ -915,33 +1036,7 @@
915
1036
  this.#speculative.tokenExpires &&
916
1037
  Date.now() < this.#speculative.tokenExpires
917
1038
  ) {
918
- this.dispatchEvent("progress", { progress: 100 });
919
-
920
- const fieldName =
921
- this.getAttribute("data-cap-hidden-field-name") || "cap-token";
922
- if (this.querySelector(`input[name='${fieldName}']`)) {
923
- this.querySelector(`input[name='${fieldName}']`).value =
924
- this.#speculative.token;
925
- }
926
- this.dispatchEvent("solve", { token: this.#speculative.token });
927
- this.token = this.#speculative.token;
928
-
929
- const expiresIn = this.#speculative.tokenExpires - Date.now();
930
- if (this.#resetTimer) clearTimeout(this.#resetTimer);
931
- this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
932
-
933
- this.#trigger.setAttribute(
934
- "aria-label",
935
- this.getI18nText(
936
- "verified-aria-label",
937
- "We have verified you're a human, you may now continue",
938
- ),
939
- );
940
- if (this.#hasHaptics) navigator.vibrate([10, 50, 20, 30, 40]);
941
-
942
- this.#resetSpeculativeState();
943
- this.#solving = false;
944
- return { success: true, token: this.token };
1039
+ return this.#commitSpeculativeToken();
945
1040
  }
946
1041
 
947
1042
  solutions = this.#speculative.results;
@@ -956,20 +1051,24 @@
956
1051
  } else {
957
1052
  const challengeRaw = await capFetch(`${apiEndpoint}challenge`, {
958
1053
  method: "POST",
1054
+ signal,
959
1055
  });
960
1056
  try {
961
1057
  challengeResp = await challengeRaw.json();
962
1058
  } catch {
963
- throw new Error("Failed to parse challenge response from server");
1059
+ throw _err("challenge_parse_error", "Failed to parse challenge response from server");
964
1060
  }
965
- if (challengeResp.error) throw new Error(challengeResp.error);
1061
+ if (challengeResp.error) throw _err("network_error", challengeResp.error);
966
1062
  }
967
1063
 
968
1064
  if (
969
1065
  challengeResp.format === 2 &&
970
1066
  Array.isArray(challengeResp.challenges)
971
1067
  ) {
972
- solutions = await this.solveChallengesV2(challengeResp.challenges);
1068
+ solutions = await this.solveChallengesV2(
1069
+ challengeResp.challenges,
1070
+ signal,
1071
+ );
973
1072
  } else {
974
1073
  const { challenge, token } = challengeResp;
975
1074
  let challenges = challenge;
@@ -983,7 +1082,7 @@
983
1082
  ];
984
1083
  });
985
1084
  }
986
- solutions = await this.solveChallenges(challenges);
1085
+ solutions = await this.solveChallenges(challenges, signal);
987
1086
  }
988
1087
  }
989
1088
 
@@ -992,6 +1091,7 @@
992
1091
  : Promise.resolve(null);
993
1092
 
994
1093
  const instrOut = await instrPromise;
1094
+ if (signal?.aborted || !this.#speculative) return;
995
1095
 
996
1096
  if (instrOut?.__timeout || instrOut?.__blocked) {
997
1097
  capFetch(`${apiEndpoint}redeem`, {
@@ -1003,6 +1103,7 @@
1003
1103
  ...(instrOut.__timeout && { instr_timeout: true }),
1004
1104
  }),
1005
1105
  headers: { "Content-Type": "application/json" },
1106
+ signal,
1006
1107
  }).catch(() => {});
1007
1108
 
1008
1109
  this.updateUIBlocked(
@@ -1016,16 +1117,20 @@
1016
1117
  "An error occurred, please try again",
1017
1118
  ),
1018
1119
  );
1120
+ const instrCode = instrOut?.__blocked ? "instr_blocked" : "instr_timeout";
1121
+ const instrMsg = instrOut?.__blocked
1122
+ ? `Instrumentation blocked (${instrOut.blockReason || "automated_browser"})`
1123
+ : "Instrumentation timed out";
1019
1124
  this.removeEventListener("error", this.boundHandleError);
1020
1125
  const errEvent = new CustomEvent("error", {
1021
1126
  bubbles: true,
1022
1127
  composed: true,
1023
- detail: { isCap: true, message: "Instrumentation failed" },
1128
+ detail: { isCap: true, code: instrCode, message: instrMsg },
1024
1129
  });
1025
1130
  super.dispatchEvent(errEvent);
1026
1131
  this.addEventListener("error", this.boundHandleError);
1027
1132
  this.executeAttributeCode("onerror", errEvent);
1028
- console.error("[cap]", "Instrumentation failed");
1133
+ log.error(T("instr"), `[${instrCode}] ${instrMsg}`);
1029
1134
  this.#solving = false;
1030
1135
  return;
1031
1136
  }
@@ -1040,23 +1145,22 @@
1040
1145
  ...(instrOut && { instr: instrOut }),
1041
1146
  }),
1042
1147
  headers: { "Content-Type": "application/json" },
1148
+ signal,
1043
1149
  });
1044
1150
 
1045
1151
  let resp;
1046
1152
  try {
1047
1153
  resp = await redeemResponse.json();
1048
1154
  } catch {
1049
- throw new Error("Failed to parse server response");
1155
+ throw _err("redeem_failed", "Failed to parse server response");
1050
1156
  }
1051
1157
 
1158
+ if (signal?.aborted || !this.#speculative) return;
1159
+
1052
1160
  this.dispatchEvent("progress", { progress: 100 });
1053
- if (!resp.success) throw new Error(resp.error || "Invalid solution");
1161
+ if (!resp.success) throw _err("invalid_solution", resp.error || "Invalid solution");
1054
1162
 
1055
- const fieldName =
1056
- this.getAttribute("data-cap-hidden-field-name") || "cap-token";
1057
- if (this.querySelector(`input[name='${fieldName}']`)) {
1058
- this.querySelector(`input[name='${fieldName}']`).value = resp.token;
1059
- }
1163
+ this.#setToken(resp.token);
1060
1164
 
1061
1165
  this.dispatchEvent("solve", { token: resp.token });
1062
1166
  this.token = resp.token;
@@ -1068,7 +1172,7 @@
1068
1172
  if (expiresIn > 0 && expiresIn < 24 * 60 * 60 * 1000) {
1069
1173
  this.#resetTimer = setTimeout(() => this.reset(), expiresIn);
1070
1174
  } else {
1071
- this.error("Invalid expiration time");
1175
+ this.error("Invalid expiration time", "invalid_expires");
1072
1176
  }
1073
1177
 
1074
1178
  this.#trigger.setAttribute(
@@ -1080,8 +1184,11 @@
1080
1184
  );
1081
1185
  if (this.#hasHaptics) navigator.vibrate([10, 50, 20, 30, 40]);
1082
1186
 
1187
+ log.debug(T("solve"), `verified in ${since(_solveT0)}`);
1188
+ this.#logInvisible();
1083
1189
  return { success: true, token: this.token };
1084
1190
  } catch (err) {
1191
+ if (signal?.aborted || !this.#speculative) return;
1085
1192
  this.#trigger.setAttribute(
1086
1193
  "aria-label",
1087
1194
  this.getI18nText(
@@ -1089,7 +1196,7 @@
1089
1196
  "An error occurred, please try again",
1090
1197
  ),
1091
1198
  );
1092
- this.error(err.message);
1199
+ this.error(err.message, err.code || "solve_failed");
1093
1200
  throw err;
1094
1201
  }
1095
1202
  } finally {
@@ -1097,7 +1204,7 @@
1097
1204
  }
1098
1205
  }
1099
1206
 
1100
- async solveChallengesV2(challenges) {
1207
+ async solveChallengesV2(challenges, signal) {
1101
1208
  const total = challenges.length;
1102
1209
  let completed = 0;
1103
1210
 
@@ -1115,7 +1222,7 @@
1115
1222
  ) {
1116
1223
  // Unknown protocol = older widget on a newer server. Fall back to
1117
1224
  // erroring out -- the host can detect this and serve format-1.
1118
- throw new Error(`[cap] unsupported format-2 protocol '${ch?.protocol}'`);
1225
+ throw _err("challenge_unsupported", `unsupported format-2 protocol '${ch?.protocol}'`);
1119
1226
  }
1120
1227
  }
1121
1228
 
@@ -1125,7 +1232,7 @@
1125
1232
  typeof WebAssembly.instantiate === "function";
1126
1233
  if (wasmSupported) {
1127
1234
  try { wasmModule = await getWasmModule(); }
1128
- catch (e) { console.warn("[cap] wasm unavailable, falling back to JS solver:", e); }
1235
+ catch (e) { log.warn(T("wasm"), "unavailable, falling back to JS solver:", e.message || e); }
1129
1236
  }
1130
1237
 
1131
1238
  const poolSize = Math.max(1, Math.min(this.#workersCount, challenges.length));
@@ -1150,7 +1257,7 @@
1150
1257
  };
1151
1258
 
1152
1259
  try {
1153
- await Promise.all(
1260
+ await raceAbort(Promise.all(
1154
1261
  challenges.map((ch, idx) => {
1155
1262
  if (ch.protocol === "sha256-pow") {
1156
1263
  return withTimeout(
@@ -1188,7 +1295,7 @@
1188
1295
  emit();
1189
1296
  });
1190
1297
  }),
1191
- );
1298
+ ), signal);
1192
1299
  } finally {
1193
1300
  pool.terminate();
1194
1301
  }
@@ -1196,7 +1303,7 @@
1196
1303
  return solutions;
1197
1304
  }
1198
1305
 
1199
- async solveChallenges(challenges) {
1306
+ async solveChallenges(challenges, signal) {
1200
1307
  const total = challenges.length;
1201
1308
  let completed = 0;
1202
1309
 
@@ -1211,11 +1318,12 @@
1211
1318
  try {
1212
1319
  wasmModule = await getWasmModule();
1213
1320
  } catch (e) {
1214
- console.warn("[cap] wasm unavailable, falling back to JS solver:", e);
1321
+ log.warn(T("wasm"), "unavailable, falling back to JS solver:", e.message || e);
1215
1322
  }
1216
1323
  }
1217
1324
 
1218
1325
  if (!wasmSupported) {
1326
+ log.warn(T("wasm"), "WebAssembly disabled in this browser, solver will be ~10x slower");
1219
1327
  if (!this.#shadow.querySelector(".warning")) {
1220
1328
  const warningEl = document.createElement("div");
1221
1329
  warningEl.className = "warning";
@@ -1243,7 +1351,7 @@
1243
1351
  i,
1244
1352
  Math.min(i + this.#workersCount, challenges.length),
1245
1353
  );
1246
- const chunkResults = await Promise.all(
1354
+ const chunkResults = await raceAbort(Promise.all(
1247
1355
  chunk.map(([salt, target]) =>
1248
1356
  pool.run(salt, target).then((nonce) => {
1249
1357
  completed++;
@@ -1255,7 +1363,7 @@
1255
1363
  return nonce;
1256
1364
  }),
1257
1365
  ),
1258
- );
1366
+ ), signal);
1259
1367
  results.push(...chunkResults);
1260
1368
  }
1261
1369
  } finally {
@@ -1552,16 +1660,17 @@
1552
1660
  return;
1553
1661
  }
1554
1662
 
1555
- console.error(
1556
- "[cap] using `onxxx='…'` is strongly discouraged and will be deprecated soon. please use `addEventListener` callbacks instead.",
1663
+ log.warn(
1664
+ T(),
1665
+ "inline `onxxx='…'` handlers are deprecated. use `addEventListener` callbacks instead.",
1557
1666
  );
1558
1667
 
1559
1668
  new Function("event", code).call(this, event);
1560
1669
  }
1561
1670
 
1562
- error(message = "Unknown error") {
1563
- console.error("[cap]", message);
1564
- this.dispatchEvent("error", { isCap: true, message });
1671
+ error(message = "Unknown error", code = "unknown") {
1672
+ log.error(T("solve"), `[${code}] ${message}`);
1673
+ this.dispatchEvent("error", { isCap: true, code, message });
1565
1674
  }
1566
1675
 
1567
1676
  dispatchEvent(eventName, detail = {}) {
@@ -1580,11 +1689,7 @@
1580
1689
  }
1581
1690
  this.token = null;
1582
1691
  this.dispatchEvent("reset");
1583
- const fieldName =
1584
- this.getAttribute("data-cap-hidden-field-name") || "cap-token";
1585
- if (this.querySelector(`input[name='${fieldName}']`)) {
1586
- this.querySelector(`input[name='${fieldName}']`).value = "";
1587
- }
1692
+ this.#setToken("");
1588
1693
  }
1589
1694
 
1590
1695
  get tokenValue() {
@@ -1592,6 +1697,7 @@
1592
1697
  }
1593
1698
 
1594
1699
  disconnectedCallback() {
1700
+ this.#abort?.abort();
1595
1701
  this.removeEventListener("progress", this.boundHandleProgress);
1596
1702
  this.removeEventListener("solve", this.boundHandleSolve);
1597
1703
  this.removeEventListener("error", this.boundHandleError);
@@ -1679,8 +1785,9 @@
1679
1785
  if (!customElements.get("cap-widget") && !window?.CAP_DONT_SKIP_REDEFINE) {
1680
1786
  customElements.define("cap-widget", CapWidget);
1681
1787
  } else if (customElements.get("cap-widget")) {
1682
- console.warn(
1683
- "[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true",
1788
+ log.warn(
1789
+ T(),
1790
+ "cap-widget custom element already defined, skipping re-define. set window.CAP_DONT_SKIP_REDEFINE = true to override",
1684
1791
  );
1685
1792
  }
1686
1793
 
@@ -0,0 +1,87 @@
1
+ export const shipped = [
2
+ "zh-cn", "es", "hi", "ar", "pt", "ru", "ja", "de", "fr",
3
+ ];
4
+
5
+ export const shippedKeys = [
6
+ "initial-state", "verifying-label", "solved-label", "error-label",
7
+ "required-label", "troubleshooting-label", "group-aria-label",
8
+ "verify-aria-label", "verifying-aria-label", "verified-aria-label",
9
+ "error-aria-label",
10
+ ];
11
+
12
+ export const keys = [
13
+ "initial-state", // Verify you're human
14
+ "verifying-label", // Verifying...
15
+ "solved-label", // You're a human
16
+ "error-label", // Error. Try again.
17
+ "required-label", // Please verify you're human
18
+ "troubleshooting-label", // Troubleshoot
19
+ "group-aria-label", // Cap verification
20
+ "verify-aria-label", // Click to verify you're a human
21
+ "verifying-aria-label", // Verifying you're a human, please wait
22
+ "verified-aria-label", // We have verified you're a human, you may now continue
23
+ "error-aria-label", // An error occurred, please try again
24
+ "wasm-disabled", // Enable WASM for significantly faster solving
25
+ ];
26
+
27
+ export const translations = {
28
+ 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","Activa WASM para una resolución mucho más rápida"],
29
+ 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","Ative o WASM para resolver muito mais rápido"],
30
+ 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","Activez WASM pour une résolution bien plus rapide"],
31
+ 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","Aktivieren Sie WASM für deutlich schnelleres Lösen"],
32
+ it: ["Verifica di essere umano","Verifica in corso...","Sei umano","Errore. Riprova.","Verifica di essere umano","Risoluzione dei problemi","Verifica Cap","Clicca per verificare di essere umano","Verifica che sei umano, attendi","Abbiamo verificato che sei umano, puoi continuare","Si è verificato un errore, riprova","Abilita WASM per una risoluzione molto più veloce"],
33
+ nl: ["Ik ben een mens","Verifiëren...","Je bent een mens","Fout. Probeer opnieuw.","Verifieer dat je een mens bent","Problemen oplossen","Cap-verificatie","Klik om te verifiëren dat je een mens bent","Verifiëren dat je een mens bent, even geduld","We hebben geverifieerd dat je een mens bent, je kunt doorgaan","Er is een fout opgetreden, probeer opnieuw","Schakel WASM in voor veel sneller oplossen"],
34
+ sv: ["Jag är människa","Verifierar...","Du är människa","Fel. Försök igen.","Verifiera att du är människa","Felsökning","Cap-verifiering","Klicka för att verifiera att du är människa","Verifierar att du är människa, vänta","Vi har verifierat att du är människa, du kan fortsätta","Ett fel uppstod, försök igen","Aktivera WASM för betydligt snabbare lösning"],
35
+ da: ["Jeg er et menneske","Bekræfter...","Du er et menneske","Fejl. Prøv igen.","Bekræft, at du er et menneske","Fejlfinding","Cap-bekræftelse","Klik for at bekræfte, at du er et menneske","Bekræfter, at du er et menneske, vent","Vi har bekræftet, at du er et menneske, du kan fortsætte","Der opstod en fejl, prøv igen","Aktivér WASM for betydeligt hurtigere løsning"],
36
+ nb: ["Jeg er et menneske","Bekrefter...","Du er et menneske","Feil. Prøv igjen.","Bekreft at du er et menneske","Feilsøking","Cap-bekreftelse","Klikk for å bekrefte at du er et menneske","Bekrefter at du er et menneske, vent","Vi har bekreftet at du er et menneske, du kan fortsette","Det oppstod en feil, prøv igjen","Aktiver WASM for betydelig raskere løsning"],
37
+ fi: ["Vahvista, että olet ihminen","Vahvistetaan...","Olet ihminen","Virhe. Yritä uudelleen.","Vahvista, että olet ihminen","Vianmääritys","Cap-vahvistus","Napsauta vahvistaaksesi, että olet ihminen","Vahvistetaan, että olet ihminen, odota","Olemme vahvistaneet, että olet ihminen, voit jatkaa","Tapahtui virhe, yritä uudelleen","Ota WASM käyttöön huomattavasti nopeampaa ratkaisua varten"],
38
+ pl: ["Jestem człowiekiem","Weryfikacja...","Jesteś człowiekiem","Błąd. Spróbuj ponownie.","Potwierdź, że jesteś człowiekiem","Rozwiązywanie problemów","Weryfikacja Cap","Kliknij, aby potwierdzić, że jesteś człowiekiem","Weryfikacja, że jesteś człowiekiem, czekaj","Potwierdziliśmy, że jesteś człowiekiem, możesz kontynuować","Wystąpił błąd, spróbuj ponownie","Włącz WASM, aby rozwiązywać znacznie szybciej"],
39
+ cs: ["Ověřte, že jste člověk","Ověřování...","Jste člověk","Chyba. Zkuste to znovu.","Ověřte, že jste člověk","Řešení potíží","Ověření Cap","Kliknutím ověřte, že jste člověk","Ověřujeme, že jste člověk, čekejte","Ověřili jsme, že jste člověk, můžete pokračovat","Došlo k chybě, zkuste to znovu","Povolte WASM pro výrazně rychlejší řešení"],
40
+ sk: ["Overte, že ste človek","Overovanie...","Ste človek","Chyba. Skúste znova.","Overte, že ste človek","Riešenie problémov","Overenie Cap","Kliknutím overte, že ste človek","Overujeme, že ste človek, čakajte","Overili sme, že ste človek, môžete pokračovať","Vyskytla sa chyba, skúste znova","Povoľte WASM pre výrazne rýchlejšie riešenie"],
41
+ ru: ["Я человек","Проверка...","Вы человек","Ошибка. Повторите.","Подтвердите, что вы человек","Устранение неполадок","Проверка Cap","Нажмите, чтобы подтвердить, что вы человек","Проверяем, что вы человек, подождите","Мы подтвердили, что вы человек, можете продолжить","Произошла ошибка, повторите попытку","Включите WASM для значительно более быстрого решения"],
42
+ uk: ["Я людина","Перевірка...","Ви людина","Помилка. Спробуйте.","Підтвердьте, що ви людина","Усунення несправностей","Перевірка Cap","Натисніть, щоб підтвердити, що ви людина","Перевіряємо, що ви людина, зачекайте","Ми підтвердили, що ви людина, можете продовжити","Сталася помилка, спробуйте ще раз","Увімкніть WASM для значно швидшого розв’язання"],
43
+ ca: ["Verifica que ets humà","Verificant...","Ets humà","Error. Torna-ho a provar.","Verifica que ets humà","Resolució de problemes","Verificació de Cap","Fes clic per verificar que ets humà","Verificant que ets humà, espera","Hem verificat que ets humà, ja pots continuar","S'ha produït un error, torna-ho a provar","Activa WASM per resoldre molt més ràpid"],
44
+ gl: ["Verifica que es humano","Verificando...","Es humano","Erro. Téntao de novo.","Verifica que es humano","Resolución de problemas","Verificación de Cap","Fai clic para verificar que es humano","Verificando que es humano, agarda","Verificamos que es humano, xa podes continuar","Produciuse un erro, téntao de novo","Activa WASM para resolver moito máis rápido"],
45
+ af: ["Bevestig dat jy 'n mens is","Verifieer tans...","Jy is 'n mens","Fout. Probeer weer.","Bevestig asseblief dat jy 'n mens is","Probleemoplossing","Cap-verifikasie","Klik om te bevestig dat jy 'n mens is","Bevestig tans dat jy 'n mens is, wag asseblief","Ons het bevestig dat jy 'n mens is, jy kan voortgaan","'n Fout het voorgekom, probeer weer","Aktiveer WASM vir aansienlik vinniger oplossing"],
46
+ id: ["Verifikasi Anda manusia","Memverifikasi...","Anda manusia","Kesalahan. Coba lagi.","Harap verifikasi Anda manusia","Pemecahan masalah","Verifikasi Cap","Klik untuk memverifikasi Anda manusia","Memverifikasi Anda manusia, harap tunggu","Kami telah memverifikasi Anda manusia, Anda dapat melanjutkan","Terjadi kesalahan, coba lagi","Aktifkan WASM untuk penyelesaian yang jauh lebih cepat"],
47
+ ms: ["Sahkan anda manusia","Mengesahkan...","Anda manusia","Ralat. Cuba lagi.","Sila sahkan anda manusia","Penyelesaian masalah","Pengesahan Cap","Klik untuk mengesahkan anda manusia","Mengesahkan anda manusia, sila tunggu","Kami telah mengesahkan anda manusia, anda boleh teruskan","Ralat berlaku, sila cuba lagi","Dayakan WASM untuk penyelesaian yang jauh lebih pantas"],
48
+ ro: ["Verificați că sunteți om","Se verifică...","Sunteți om","Eroare. Încercați din nou.","Verificați că sunteți om","Depanare","Verificare Cap","Faceți clic pentru a verifica că sunteți om","Verificăm că sunteți om, așteptați","Am verificat că sunteți om, puteți continua","A apărut o eroare, încercați din nou","Activați WASM pentru o rezolvare mult mai rapidă"],
49
+ hu: ["Igazolja, hogy ember","Ellenőrzés...","Ön ember","Hiba. Próbálja újra.","Igazolja, hogy ember","Hibaelhárítás","Cap-ellenőrzés","Kattintson az emberi voltának igazolásához","Ellenőrizzük, hogy ember-e, kérjük várjon","Igazoltuk, hogy ember, folytathatja","Hiba történt, próbálja újra","Engedélyezze a WASM-ot a sokkal gyorsabb megoldásért"],
50
+ el: ["Είμαι άνθρωπος","Επαλήθευση...","Είστε άνθρωπος","Σφάλμα. Δοκιμάστε ξανά.","Επιβεβαιώστε ότι είστε άνθρωπος","Αντιμετώπιση προβλημάτων","Επαλήθευση Cap","Κάντε κλικ για να επιβεβαιώσετε ότι είστε άνθρωπος","Επαληθεύουμε ότι είστε άνθρωπος, περιμένετε","Επιβεβαιώσαμε ότι είστε άνθρωπος, μπορείτε να συνεχίσετε","Παρουσιάστηκε σφάλμα, δοκιμάστε ξανά","Ενεργοποιήστε το WASM για πολύ ταχύτερη επίλυση"],
51
+ bg: ["Потвърдете, че сте човек","Проверка...","Вие сте човек","Грешка. Опитайте пак.","Потвърдете, че сте човек","Отстраняване на неизправности","Проверка Cap","Щракнете, за да потвърдите, че сте човек","Проверяваме, че сте човек, изчакайте","Потвърдихме, че сте човек, можете да продължите","Възникна грешка, опитайте отново","Активирайте WASM за значително по-бързо решаване"],
52
+ sr: ["Потврдите да сте човек","Провера...","Ви сте човек","Грешка. Покушајте опет.","Потврдите да сте човек","Решавање проблема","Cap провера","Кликните да потврдите да сте човек","Проверавамо да сте човек, сачекајте","Потврдили смо да сте човек, можете наставити","Дошло је до грешке, покушајте поново","Омогућите WASM за знатно брже решавање"],
53
+ hr: ["Potvrdite da ste čovjek","Provjera...","Vi ste čovjek","Greška. Pokušajte opet.","Potvrdite da ste čovjek","Rješavanje problema","Cap provjera","Kliknite da potvrdite da ste čovjek","Provjeravamo da ste čovjek, pričekajte","Potvrdili smo da ste čovjek, možete nastaviti","Došlo je do pogreške, pokušajte ponovno","Omogućite WASM za znatno brže rješavanje"],
54
+ sl: ["Potrdite, da ste človek","Preverjanje...","Ste človek","Napaka. Poskusite znova.","Potrdite, da ste človek","Odpravljanje težav","Preverjanje Cap","Kliknite za potrditev, da ste človek","Preverjamo, da ste človek, počakajte","Potrdili smo, da ste človek, lahko nadaljujete","Prišlo je do napake, poskusite znova","Omogočite WASM za bistveno hitrejše reševanje"],
55
+ et: ["Kinnitage, et olete inimene","Kontrollimine...","Olete inimene","Viga. Proovige uuesti.","Kinnitage, et olete inimene","Tõrkeotsing","Cap kontroll","Klõpsake, et kinnitada, et olete inimene","Kontrollime, et olete inimene, oodake","Kinnitasime, et olete inimene, võite jätkata","Ilmnes viga, proovige uuesti","Lubage WASM oluliselt kiiremaks lahendamiseks"],
56
+ lv: ["Esmu cilvēks","Pārbauda...","Jūs esat cilvēks","Kļūda. Mēģiniet vēlreiz.","Apstipriniet, ka esat cilvēks","Problēmu novēršana","Cap pārbaude","Noklikšķiniet, lai apstiprinātu, ka esat cilvēks","Pārbaudām, ka esat cilvēks, uzgaidiet","Mēs apstiprinājām, ka esat cilvēks, varat turpināt","Radās kļūda, mēģiniet vēlreiz","Iespējojiet WASM ievērojami ātrākai risināšanai"],
57
+ lt: ["Esu žmogus","Tikrinama...","Jūs esate žmogus","Klaida. Bandykite vėl.","Patvirtinkite, kad esate žmogus","Trikčių šalinimas","Cap patvirtinimas","Spustelėkite, kad patvirtintumėte, jog esate žmogus","Tikriname, ar esate žmogus, palaukite","Patvirtinome, kad esate žmogus, galite tęsti","Įvyko klaida, bandykite dar kartą","Įjunkite WASM, kad išspręstumėte daug greičiau"],
58
+ tr: ["Ben insanım","Doğrulanıyor...","İnsansınız","Hata. Tekrar deneyin.","Lütfen insan olduğunuzu doğrulayın","Sorun giderme","Cap doğrulaması","İnsan olduğunuzu doğrulamak için tıklayın","İnsan olduğunuz doğrulanıyor, lütfen bekleyin","İnsan olduğunuzu doğruladık, devam edebilirsiniz","Bir hata oluştu, tekrar deneyin","Çok daha hızlı çözüm için WASM'ı etkinleştirin"],
59
+ vi: ["Xác minh bạn là con người","Đang xác minh...","Bạn là con người","Lỗi. Thử lại.","Vui lòng xác minh bạn là con người","Khắc phục sự cố","Xác minh Cap","Nhấp để xác minh bạn là con người","Đang xác minh bạn là con người, vui lòng đợi","Chúng tôi đã xác minh bạn là con người, bạn có thể tiếp tục","Đã xảy ra lỗi, vui lòng thử lại","Bật WASM để giải nhanh hơn đáng kể"],
60
+ tl: ["Patunayan na ikaw ay tao","Bineberipika...","Ikaw ay tao","Error. Subukan ulit.","Pakipatunayan na ikaw ay tao","Pag-troubleshoot","Pagpapatunay ng Cap","I-click para patunayan na ikaw ay tao","Pinapatunayan na ikaw ay tao, maghintay","Napatunayan namin na ikaw ay tao, maaari nang magpatuloy","May naganap na error, subukan ulit","I-enable ang WASM para sa mas mabilis na paglutas"],
61
+ ar: ["تحقق من أنك إنسان","جارٍ التحقق...","أنت إنسان","خطأ. حاول مرة أخرى.","يرجى التحقق من أنك إنسان","استكشاف الأخطاء وإصلاحها","تحقق Cap","انقر للتحقق من أنك إنسان","جارٍ التحقق من أنك إنسان، يرجى الانتظار","لقد تحققنا من أنك إنسان، يمكنك المتابعة الآن","حدث خطأ، يرجى المحاولة مرة أخرى","فعّل WASM لحل أسرع بكثير"],
62
+ he: ["אמת שאתה אנושי","מאמת...","אתה אנושי","שגיאה. נסה שוב.","נא לאמת שאתה אנושי","פתרון בעיות","אימות Cap","לחץ כדי לאמת שאתה אנושי","מאמת שאתה אנושי, אנא המתן","אימתנו שאתה אנושי, אפשר להמשיך","אירעה שגיאה, נסה שוב","הפעל את WASM לפתרון מהיר בהרבה"],
63
+ fa: ["تأیید کنید که انسان هستید","در حال تأیید...","شما انسان هستید","خطا. دوباره تلاش کنید.","لطفاً تأیید کنید که انسان هستید","عیب‌یابی","تأیید Cap","برای تأیید اینکه انسان هستید کلیک کنید","در حال تأیید اینکه انسان هستید، لطفاً صبر کنید","تأیید کردیم که انسان هستید، می‌توانید ادامه دهید","خطایی رخ داد، دوباره تلاش کنید","برای حل بسیار سریع‌تر، WASM را فعال کنید"],
64
+ "zh-cn": ["确认你是真人","正在验证...","你是真人","出错了。请重试。","请确认你是真人","故障排除","Cap 验证","点击以确认你是真人","正在验证你是真人,请稍候","我们已确认你是真人,你可以继续了","发生错误,请重试","启用 WASM 可大幅加快求解速度"],
65
+ "zh-tw": ["確認你是真人","正在驗證...","你是真人","發生錯誤。請重試。","請確認你是真人","疑難排解","Cap 驗證","點擊以確認你是真人","正在驗證你是真人,請稍候","我們已確認你是真人,你可以繼續了","發生錯誤,請重試","啟用 WASM 可大幅加快解算速度"],
66
+ ja: ["私は人間です","確認中...","あなたは人間です","エラー。再試行を。","あなたが人間であることを確認してください","トラブルシューティング","Cap 認証","クリックして人間であることを確認","あなたが人間であることを確認しています。お待ちください","あなたが人間であることを確認しました。続行できます","エラーが発生しました。もう一度お試しください","WASM を有効にすると大幅に高速化されます"],
67
+ ko: ["사람인지 확인하세요","확인 중...","당신은 사람입니다","오류. 다시 시도하세요.","사람인지 확인해 주세요","문제 해결","Cap 인증","클릭하여 사람인지 확인하세요","사람인지 확인 중입니다. 잠시 기다려 주세요","사람임을 확인했습니다. 계속할 수 있습니다","오류가 발생했습니다. 다시 시도하세요","훨씬 빠른 처리를 위해 WASM을 활성화하세요"],
68
+ az: ["Mən insanam","Yoxlanılır...","Siz insansınız","Xəta. Yenidən cəhd edin.","İnsan olduğunuzu təsdiqləyin","Problemlərin həlli","Cap yoxlaması","İnsan olduğunuzu təsdiqləmək üçün klikləyin","İnsan olduğunuz yoxlanılır, gözləyin","İnsan olduğunuzu təsdiqlədik, davam edə bilərsiniz","Xəta baş verdi, yenidən cəhd edin","Daha sürətli həll üçün WASM-ı aktivləşdirin"],
69
+ kk: ["Адам екеніңізді растаңыз","Тексерілуде...","Сіз адамсыз","Қате. Қайталап көріңіз.","Адам екеніңізді растаңыз","Ақаулықтарды жою","Cap растауы","Адам екеніңізді растау үшін басыңыз","Адам екеніңізді тексерудеміз, күтіңіз","Адам екеніңізді растадық, жалғастыра аласыз","Қате орын алды, қайталап көріңіз","Әлдеқайда жылдам шешу үшін WASM қосыңыз"],
70
+ ka: ["მე ადამიანი ვარ","მოწმდება...","თქვენ ადამიანი ხართ","შეცდომა. სცადეთ.","დაადასტურეთ, რომ ადამიანი ხართ","პრობლემის გადაჭრა","Cap-ის დადასტურება","დააწკაპუნეთ, რომ დაადასტუროთ, რომ ადამიანი ხართ","ვამოწმებთ, რომ ადამიანი ხართ, დაელოდეთ","დავადასტურეთ, რომ ადამიანი ხართ, შეგიძლიათ გააგრძელოთ","მოხდა შეცდომა, სცადეთ ხელახლა","ჩართეთ WASM მნიშვნელოვნად სწრაფი ამოხსნისთვის"],
71
+ hy: ["Հաստատեք, որ մարդ եք","Ստուգվում է...","Դուք մարդ եք","Սխալ. Կրկին փորձեք։","Հաստատեք, որ մարդ եք","Անսարքությունների վերացում","Cap ստուգում","Սեղմեք՝ հաստատելու, որ մարդ եք","Ստուգում ենք, որ մարդ եք, սպասեք","Մենք հաստատեցինք, որ մարդ եք, կարող եք շարունակել","Սխալ տեղի ունեցավ, կրկին փորձեք","Միացրեք WASM-ը զգալիորեն ավելի արագ լուծման համար"],
72
+ ur: ["تصدیق کریں کہ آپ انسان ہیں","تصدیق ہو رہی ہے...","آپ انسان ہیں","خرابی۔ دوبارہ کوشش کریں۔","براہ کرم تصدیق کریں کہ آپ انسان ہیں","مسئلہ حل کریں","Cap تصدیق","تصدیق کے لیے کلک کریں کہ آپ انسان ہیں","تصدیق ہو رہی ہے کہ آپ انسان ہیں، انتظار کریں","ہم نے تصدیق کر لی کہ آپ انسان ہیں، اب آپ جاری رکھ سکتے ہیں","ایک خرابی پیش آئی، دوبارہ کوشش کریں","نمایاں طور پر تیز حل کے لیے WASM فعال کریں"],
73
+ hi: ["पुष्टि करें कि आप मानव हैं","सत्यापित हो रहा है...","आप मानव हैं","त्रुटि। पुनः प्रयास करें।","कृपया पुष्टि करें कि आप मानव हैं","समस्या निवारण","Cap सत्यापन","पुष्टि करने के लिए क्लिक करें कि आप मानव हैं","सत्यापित किया जा रहा है कि आप मानव हैं, प्रतीक्षा करें","हमने पुष्टि कर दी है कि आप मानव हैं, अब आप जारी रख सकते हैं","एक त्रुटि हुई, पुनः प्रयास करें","काफी तेज़ समाधान के लिए WASM सक्षम करें"],
74
+ bn: ["আপনি মানুষ তা যাচাই করুন","যাচাই করা হচ্ছে...","আপনি মানুষ","ত্রুটি। আবার চেষ্টা করুন।","অনুগ্রহ করে যাচাই করুন যে আপনি মানুষ","সমস্যা সমাধান","Cap যাচাই","আপনি মানুষ তা যাচাই করতে ক্লিক করুন","আপনি মানুষ তা যাচাই করা হচ্ছে, অপেক্ষা করুন","আমরা যাচাই করেছি যে আপনি মানুষ, এখন চালিয়ে যেতে পারেন","একটি ত্রুটি ঘটেছে, আবার চেষ্টা করুন","অনেক দ্রুত সমাধানের জন্য WASM সক্ষম করুন"],
75
+ ta: ["நான் மனிதர்","சரிபார்க்கிறது...","நீங்கள் மனிதர்","பிழை. மீண்டும் முயலவும்.","நீங்கள் மனிதர் என்பதை உறுதிப்படுத்தவும்","சிக்கல் தீர்வு","Cap சரிபார்ப்பு","நீங்கள் மனிதர் என உறுதிப்படுத்த கிளிக் செய்யவும்","நீங்கள் மனிதர் என சரிபார்க்கிறது, காத்திருக்கவும்","நீங்கள் மனிதர் என உறுதிப்படுத்தினோம், தொடரலாம்","பிழை ஏற்பட்டது, மீண்டும் முயற்சிக்கவும்","மிக வேகமான தீர்வுக்கு WASM ஐ இயக்கவும்"],
76
+ te: ["నేను మనిషిని","ధృవీకరిస్తోంది...","మీరు మనిషి","లోపం. మళ్లీ ప్రయత్నించండి.","దయచేసి మీరు మనిషి అని ధృవీకరించండి","సమస్య పరిష్కారం","Cap ధృవీకరణ","మీరు మనిషి అని ధృవీకరించడానికి క్లిక్ చేయండి","మీరు మనిషి అని ధృవీకరిస్తోంది, వేచి ఉండండి","మీరు మనిషి అని మేము ధృవీకరించాము, ఇప్పుడు కొనసాగవచ్చు","లోపం సంభవించింది, మళ్లీ ప్రయత్నించండి","చాలా వేగవంతమైన పరిష్కారం కోసం WASM ప్రారంభించండి"],
77
+ mr: ["मी मानव आहे","पडताळणी होत आहे...","तुम्ही मानव आहात","त्रुटी. पुन्हा प्रयत्न करा.","कृपया तुम्ही मानव आहात याची पुष्टी करा","समस्यानिवारण","Cap पडताळणी","तुम्ही मानव आहात याची पुष्टी करण्यासाठी क्लिक करा","तुम्ही मानव आहात याची पडताळणी होत आहे, प्रतीक्षा करा","आम्ही पुष्टी केली की तुम्ही मानव आहात, आता पुढे जाऊ शकता","त्रुटी आली, पुन्हा प्रयत्न करा","खूप जलद सोडवण्यासाठी WASM सक्षम करा"],
78
+ gu: ["તમે માનવ છો તેની ખાતરી કરો","ચકાસણી થઈ રહી છે...","તમે માનવ છો","ભૂલ. ફરી પ્રયાસ કરો.","કૃપા કરીને ખાતરી કરો કે તમે માનવ છો","સમસ્યાનિવારણ","Cap ચકાસણી","તમે માનવ છો તેની ખાતરી કરવા ક્લિક કરો","તમે માનવ છો તેની ચકાસણી થઈ રહી છે, રાહ જુઓ","અમે ખાતરી કરી કે તમે માનવ છો, હવે ચાલુ રાખી શકો છો","ભૂલ આવી, ફરી પ્રયાસ કરો","ઘણા ઝડપી ઉકેલ માટે WASM સક્ષમ કરો"],
79
+ kn: ["ನಾನು ಮನುಷ್ಯ","ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ...","ನೀವು ಮನುಷ್ಯ","ದೋಷ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.","ದಯವಿಟ್ಟು ನೀವು ಮನುಷ್ಯ ಎಂದು ದೃಢೀಕರಿಸಿ","ಸಮಸ್ಯೆ ಪರಿಹಾರ","Cap ಪರಿಶೀಲನೆ","ನೀವು ಮನುಷ್ಯ ಎಂದು ದೃಢೀಕರಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ","ನೀವು ಮನುಷ್ಯ ಎಂದು ಪರಿಶೀಲಿಸಲಾಗುತ್ತಿದೆ, ನಿರೀಕ್ಷಿಸಿ","ನೀವು ಮನುಷ್ಯ ಎಂದು ದೃಢೀಕರಿಸಿದ್ದೇವೆ, ಮುಂದುವರಿಯಬಹುದು","ದೋಷ ಸಂಭವಿಸಿದೆ, ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ","ಬಹಳ ವೇಗದ ಪರಿಹಾರಕ್ಕಾಗಿ WASM ಸಕ್ರಿಯಗೊಳಿಸಿ"],
80
+ ml: ["ഞാൻ മനുഷ്യനാണ്","പരിശോധിക്കുന്നു...","നിങ്ങൾ മനുഷ്യനാണ്","പിശക്. വീണ്ടും ശ്രമിക്കുക.","ദയവായി നിങ്ങൾ മനുഷ്യനാണെന്ന് സ്ഥിരീകരിക്കുക","പ്രശ്നപരിഹാരം","Cap പരിശോധന","നിങ്ങൾ മനുഷ്യനാണെന്ന് സ്ഥിരീകരിക്കാൻ ക്ലിക്ക് ചെയ്യുക","നിങ്ങൾ മനുഷ്യനാണെന്ന് പരിശോധിക്കുന്നു, കാത്തിരിക്കുക","നിങ്ങൾ മനുഷ്യനാണെന്ന് ഞങ്ങൾ സ്ഥിരീകരിച്ചു, തുടരാം","ഒരു പിശക് സംഭവിച്ചു, വീണ്ടും ശ്രമിക്കുക","വളരെ വേഗത്തിലുള്ള പരിഹാരത്തിന് WASM പ്രവർത്തനക്ഷമമാക്കുക"],
81
+ pa: ["ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ","ਪੁਸ਼ਟੀ ਹੋ ਰਹੀ ਹੈ...","ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ","ਗਲਤੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।","ਕਿਰਪਾ ਕਰਕੇ ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ","ਸਮੱਸਿਆ ਹੱਲ","Cap ਪੁਸ਼ਟੀ","ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਕਲਿੱਕ ਕਰੋ ਕਿ ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ","ਪੁਸ਼ਟੀ ਹੋ ਰਹੀ ਹੈ ਕਿ ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ, ਉਡੀਕ ਕਰੋ","ਅਸੀਂ ਪੁਸ਼ਟੀ ਕਰ ਲਈ ਹੈ ਕਿ ਤੁਸੀਂ ਮਨੁੱਖ ਹੋ, ਹੁਣ ਜਾਰੀ ਰੱਖ ਸਕਦੇ ਹੋ","ਇੱਕ ਗਲਤੀ ਆਈ, ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ","ਬਹੁਤ ਤੇਜ਼ ਹੱਲ ਲਈ WASM ਚਾਲੂ ਕਰੋ"],
82
+ th: ["ยืนยันว่าคุณเป็นมนุษย์","กำลังยืนยัน...","คุณเป็นมนุษย์","ข้อผิดพลาด ลองอีกครั้ง","โปรดยืนยันว่าคุณเป็นมนุษย์","การแก้ไขปัญหา","การยืนยัน Cap","คลิกเพื่อยืนยันว่าคุณเป็นมนุษย์","กำลังยืนยันว่าคุณเป็นมนุษย์ โปรดรอ","เรายืนยันแล้วว่าคุณเป็นมนุษย์ คุณดำเนินการต่อได้","เกิดข้อผิดพลาด โปรดลองอีกครั้ง","เปิดใช้งาน WASM เพื่อการแก้ปัญหาที่เร็วขึ้นมาก"],
83
+ sw: ["Mimi ni binadamu","Inathibitisha...","Wewe ni binadamu","Hitilafu. Jaribu tena.","Tafadhali thibitisha kuwa wewe ni binadamu","Utatuzi wa matatizo","Uthibitisho wa Cap","Bofya ili kuthibitisha kuwa wewe ni binadamu","Inathibitisha kuwa wewe ni binadamu, tafadhali subiri","Tumethibitisha kuwa wewe ni binadamu, unaweza kuendelea","Hitilafu imetokea, tafadhali jaribu tena","Washa WASM kwa utatuzi wa haraka zaidi"],
84
+ am: ["ሰው መሆንዎን ያረጋግጡ","በማረጋገጥ ላይ...","እርስዎ ሰው ነዎት","ስህተት። እንደገና ይሞክሩ።","እባክዎ ሰው መሆንዎን ያረጋግጡ","ችግር መፍታት","የCap ማረጋገጫ","ሰው መሆንዎን ለማረጋገጥ ጠቅ ያድርጉ","ሰው መሆንዎን በማረጋገጥ ላይ፣ እባክዎ ይጠብቁ","ሰው መሆንዎን አረጋግጠናል፣ መቀጠል ይችላሉ","ስህተት ተከስቷል፣ እባክዎ እንደገና ይሞክሩ","በጣም ፈጣን መፍትሄ ለማግኘት WASM ን ያንቁ"],
85
+ my: ["ကျွန်ုပ် လူသားဖြစ်သည်","အတည်ပြုနေသည်...","သင်သည် လူသားဖြစ်သည်","အမှား။ ထပ်ကြိုးစားပါ။","သင်သည် လူသားဖြစ်ကြောင်း အတည်ပြုပါ","ပြဿနာဖြေရှင်းခြင်း","Cap အတည်ပြုခြင်း","သင်သည် လူသားဖြစ်ကြောင်း အတည်ပြုရန် နှိပ်ပါ","သင်သည် လူသားဖြစ်ကြောင်း အတည်ပြုနေသည်၊ စောင့်ပါ","သင်သည် လူသားဖြစ်ကြောင်း ကျွန်ုပ်တို့ အတည်ပြုပြီးပါပြီ၊ ဆက်လက်လုပ်ဆောင်နိုင်ပါသည်","အမှားဖြစ်ပွားခဲ့သည်၊ ထပ်ကြိုးစားပါ","များစွာပိုမြန်သော ဖြေရှင်းမှုအတွက် WASM ကိုဖွင့်ပါ"],
86
+ km: ["បញ្ជាក់ថាអ្នកជាមនុស្ស","កំពុងផ្ទៀងផ្ទាត់...","អ្នកជាមនុស្ស","កំហុស។ សាកល្បងម្តងទៀត។","សូមបញ្ជាក់ថាអ្នកជាមនុស្ស","ការដោះស្រាយបញ្ហា","ការផ្ទៀងផ្ទាត់ Cap","ចុចដើម្បីបញ្ជាក់ថាអ្នកជាមនុស្ស","កំពុងផ្ទៀងផ្ទាត់ថាអ្នកជាមនុស្ស សូមរង់ចាំ","យើងបានផ្ទៀងផ្ទាត់ថាអ្នកជាមនុស្ស អ្នកអាចបន្តបាន","មានកំហុសកើតឡើង សូមសាកល្បងម្តងទៀត","បើក WASM សម្រាប់ការដោះស្រាយលឿនជាងមុនច្រើន"],
87
+ };
package/src/worker.js CHANGED
@@ -1,4 +1,8 @@
1
1
  (() => {
2
+ const wlog = (method, ...args) => {
3
+ if (!self.CAP_SILENT) console[method]("[cap worker]", ...args);
4
+ };
5
+
2
6
  const solveFallback = async ({ salt, target }) => {
3
7
  let nonce = 0;
4
8
  const batchSize = 50000;
@@ -50,7 +54,7 @@
50
54
  nonce++;
51
55
  }
52
56
  } catch (error) {
53
- console.error("[cap worker]", error);
57
+ wlog("error","fallback solver crashed:", error.message || error);
54
58
  self.postMessage({
55
59
  found: false,
56
60
  error: error.message,
@@ -79,9 +83,7 @@
79
83
  };
80
84
 
81
85
  if (typeof WebAssembly !== "object" || typeof WebAssembly?.instantiate !== "function") {
82
- console.warn(
83
- "[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower.",
84
- );
86
+ wlog("warn","WebAssembly unavailable, using JS fallback solver (significantly slower)");
85
87
 
86
88
  self.onmessage = async ({ data }) => {
87
89
  if (data?.kind === "rsw") return solveRsw(data);
@@ -171,7 +173,7 @@
171
173
 
172
174
  return true;
173
175
  } catch (e) {
174
- console.error("[cap worker] failed to init wasm from module:", e);
176
+ wlog("error","wasm init failed:", e.message || e);
175
177
  return false;
176
178
  }
177
179
  };
@@ -182,13 +184,13 @@
182
184
  if (wasmModule instanceof WebAssembly.Module && solve_pow_function === null) {
183
185
  const ok = initFromModule(wasmModule);
184
186
  if (!ok) {
185
- console.warn("[cap worker] wasm init failed, falling back to JS solver.");
187
+ wlog("warn","wasm init failed, falling back to JS solver");
186
188
  return solveFallback({ salt, target });
187
189
  }
188
190
  }
189
191
 
190
192
  if (solve_pow_function === null) {
191
- console.warn("[cap worker] no wasm module provided, falling back to JS solver.");
193
+ wlog("warn","no wasm module provided, falling back to JS solver");
192
194
  return solveFallback({ salt, target });
193
195
  }
194
196
 
@@ -203,7 +205,7 @@
203
205
  durationMs: (endTime - startTime).toFixed(2),
204
206
  });
205
207
  } catch (error) {
206
- console.error("[cap worker]", error);
208
+ wlog("error","solve_pow threw:", error.message || error);
207
209
 
208
210
  self.postMessage({
209
211
  found: false,