@cap.js/widget 0.1.16 → 0.1.18

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.
@@ -1 +1 @@
1
- (()=>{const t=(e,o,n,i)=>{o.onclick=null;const s=parseInt(o.getAttribute("data-cap-floating-offset"))||8,l=o.getAttribute("data-cap-floating-position")||"top",a=o.getBoundingClientRect();Object.assign(n.style,{display:"block",position:"absolute",zIndex:"99999"});const c=a.left+(a.width-n.offsetWidth)/2,r=Math.min(c,window.innerWidth-n.offsetWidth);n.style.top="top"===l?`${Math.max(window.scrollY,a.top-n.offsetHeight-s+window.scrollY)}px`:`${Math.min(a.bottom+s+window.scrollY,window.innerHeight-n.offsetHeight+window.scrollY)}px`,n.style.left=`${Math.max(r,2)}px`,n.solve(),n.addEventListener("solve",(({detail:s})=>{o.setAttribute("data-cap-token",s.token),o.setAttribute("data-cap-progress","done"),setTimeout((()=>{i.forEach((t=>{o.addEventListener("click",t),t.call(o,e)})),setTimeout((()=>{o.onclick=null,i.forEach((t=>o.removeEventListener("click",t))),o.onclick=e=>t(e,o,n,i)}),50)}),500),setTimeout((()=>{n.style.display="none"}),700)}))},e=e=>{const o=e.getAttribute("data-cap-floating");if(!o)return;const n=document.querySelector(o);if(!document.contains(n)&&!n.solve)throw new Error(`[Cap floating] Element "${o}" doesn't exist or isn't a Cap widget`);n.style.display="none";const i=[e.onclick].filter(Boolean);"function"==typeof getEventListeners&&i.push(...(getEventListeners(e).click||[]).map((t=>t.listener))),i.length&&(e.onclick=null,i.forEach((t=>e.removeEventListener("click",t)))),e.addEventListener("click",(o=>{o.stopImmediatePropagation(),o.preventDefault(),t(o,e,n,i)}))},o=t=>{e(t),t.querySelectorAll("[data-cap-floating]").forEach(e)};o(document.body),new MutationObserver((t=>t.forEach((t=>t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&o(t)})))))).observe(document.body,{childList:!0,subtree:!0})})();
1
+ (()=>{const t=(e,o,n,i)=>{o.onclick=null;const s=parseInt(o.getAttribute("data-cap-floating-offset"))||8,a=o.getAttribute("data-cap-floating-position")||"top",l=o.getBoundingClientRect();Object.assign(n.style,{display:"block",position:"absolute",zIndex:"99999"});const c=l.left+(l.width-n.offsetWidth)/2,r=Math.min(c,window.innerWidth-n.offsetWidth);n.style.top="top"===a?`${Math.max(window.scrollY,l.top-n.offsetHeight-s+window.scrollY)}px`:`${Math.min(l.bottom+s+window.scrollY,window.innerHeight-n.offsetHeight+window.scrollY)}px`,n.style.left=`${Math.max(r,2)}px`,n.solve(),n.addEventListener("solve",(({detail:s})=>{o.setAttribute("data-cap-token",s.token),o.setAttribute("data-cap-progress","done"),setTimeout((()=>{i.forEach((t=>{o.addEventListener("click",t),t.call(o,e)})),setTimeout((()=>{o.onclick=null,i.forEach((t=>o.removeEventListener("click",t))),o.onclick=e=>t(e,o,n,i)}),50)}),500),setTimeout((()=>{n.style.display="none"}),700)}))},e=e=>{const o=e.getAttribute("data-cap-floating");if(!o)return;const n=document.querySelector(o);if(!document.contains(n)&&!n.solve)throw new Error(`[cap floating] "${o}" doesn't exist or isn't a Cap widget`);n.style.display="none";const i=[e.onclick].filter(Boolean);"function"==typeof getEventListeners&&i.push(...(getEventListeners(e).click||[]).map((t=>t.listener))),i.length&&(e.onclick=null,i.forEach((t=>e.removeEventListener("click",t)))),e.addEventListener("click",(o=>{o.stopImmediatePropagation(),o.preventDefault(),t(o,e,n,i)}))},o=t=>{e(t),t.querySelectorAll("[data-cap-floating]").forEach(e)};o(document.body),new MutationObserver((t=>t.forEach((t=>t.addedNodes.forEach((t=>{t.nodeType===Node.ELEMENT_NODE&&o(t)})))))).observe(document.body,{childList:!0,subtree:!0})})();
package/cap.compat.min.js CHANGED
@@ -1 +1 @@
1
- !function(){let e;const t="0.0.4",r=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class s extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#n;#a=!1;#o;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","[cap]"]}constructor(){super(),this.#o&&this.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o=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)}initialize(){this.#e=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#n=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#i.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:t,token:s}=await(await r(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(t),n=await(await r(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");const a=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${a}']`)&&(this.querySelector(`input[name='${a}']`).value=n.token),this.dispatchEvent("solve",{token:n.token}),this.token=n.token,this.#t&&clearTimeout(this.#t);const o=new Date(n.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const r=e.length;let s=0;const i=Array(this.#r).fill(null).map((()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}})),n=([e,n],a)=>new Promise(((o,c)=>{const d=i[a];if(!d)return void c(new Error("Worker not available"));const h=setTimeout((()=>{try{d.terminate(),i[a]=new Worker(this.#e)}catch(e){console.error("[cap] Error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:t})=>{t.found&&(clearTimeout(h),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([e,n,t.nonce]))},d.onerror=e=>{clearTimeout(h),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:e,target:n,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`})})),a=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),s=await Promise.all(r.map(((e,t)=>n(e,t))));a.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] Error terminating worker:",e)}}))}return a}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#i.classList.add("captcha"),this.#i.setAttribute("role","button"),this.#i.setAttribute("tabindex","0"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by&nbsp;</span>Cap</a>`,this.#s.innerHTML='<style>\n\n.captcha * {box-sizing:border-box;}\n\n.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);\nuser-select:none;\n\nheight:var(--cap-widget-height, 30px);\n\nwidth:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}\n\n.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}\n\n.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.appendChild(this.#i)}addEventListeners(){this.#i&&(this.#i.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#i.addEventListener("click",(()=>{this.#i.hasAttribute("disabled")||this.solve()})),this.#i.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#i.hasAttribute("disabled")||(e.preventDefault(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);if(r)try{new Function("event",r).call(this,t)}catch(t){console.error(`[cap] Error executing ${e}:`,t)}}error(e="Unknown error"){console.error("[cap] Error:",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;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.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o.clear(),this.#s&&(this.#s.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class i{constructor(e={},t){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}e=`(() => {${function(){if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:t}})=>{let r=0;let s=0;const i=new TextEncoder,n=new Uint8Array(t.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(t.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,s=i.encode(t),o=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(o,0,a);let d=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){d=!1;break}if(d)return void self.postMessage({nonce:r,found:!0});r++}s+=5e4}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap] WebAssembly is not supported, falling back to alternative solver.");let e,t;self.onmessage=async({data:{salt:r,target:s,wasmUrl:i}})=>{e!==i&&(e=i,await import(i).then((e=>e.default().then((r=>{t=(r&&r.exports?r.exports:e).solve_pow})))).catch((e=>{console.error("[cap] using fallback solver due to error:",e)})));try{const e=performance.now(),i=t(r,s),n=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(n-e).toFixed(2)})}catch(e){console.error("[cap] solver error",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:`Worker error: ${e.message||e}`})}}.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}})()`,window.Cap=i,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",s),"object"==typeof exports&&"undefined"!=typeof module?module.exports=i:"function"==typeof define&&define.amd&&define([],(function(){return i})),"undefined"!=typeof exports&&(exports.default=i)}();
1
+ !function(){let e;const t="0.0.5",r=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class s extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#n;#a=!1;#o;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","[cap]"]}constructor(){super(),this.#o&&this.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o=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)}initialize(){this.#e=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#n=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#i.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:t,token:s}=await(await r(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(t),n=await(await r(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");const a=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${a}']`)&&(this.querySelector(`input[name='${a}']`).value=n.token),this.dispatchEvent("solve",{token:n.token}),this.token=n.token,this.#t&&clearTimeout(this.#t);const o=new Date(n.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const r=e.length;let s=0;const i=Array(this.#r).fill(null).map((()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}})),n=([e,n],a)=>new Promise(((o,c)=>{const d=i[a];if(!d)return void c(new Error("Worker not available"));const h=setTimeout((()=>{try{d.terminate(),i[a]=new Worker(this.#e)}catch(e){console.error("[cap] Error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:t})=>{t.found&&(clearTimeout(h),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([e,n,t.nonce]))},d.onerror=e=>{clearTimeout(h),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:e,target:n,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`})})),a=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),s=await Promise.all(r.map(((e,t)=>n(e,t))));a.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] Error terminating worker:",e)}}))}return a}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#i.classList.add("captcha"),this.#i.setAttribute("role","button"),this.#i.setAttribute("tabindex","0"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by&nbsp;</span>Cap</a>`,this.#s.innerHTML='<style>\n\n.captcha * {box-sizing:border-box;}\n\n.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);\nuser-select:none;\n\nheight:var(--cap-widget-height, 30px);\n\nwidth:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}\n\n.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}\n\n.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.appendChild(this.#i)}addEventListeners(){this.#i&&(this.#i.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#i.addEventListener("click",(()=>{this.#i.hasAttribute("disabled")||this.solve()})),this.#i.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#i.hasAttribute("disabled")||(e.preventDefault(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);if(r)try{new Function("event",r).call(this,t)}catch(t){console.error(`[cap] Error executing ${e}:`,t)}}error(e="Unknown error"){console.error("[cap] Error:",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;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.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o.clear(),this.#s&&(this.#s.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class i{constructor(e={},t){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}e=`(() => {${function(){if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:t}})=>{let r=0;let s=0;const i=new TextEncoder,n=new Uint8Array(t.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(t.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,s=i.encode(t),o=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(o,0,a);let d=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){d=!1;break}if(d)return void self.postMessage({nonce:r,found:!0});r++}s+=5e4}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap] WebAssembly is not supported, falling back to alternative solver.");let e,t;self.onmessage=async({data:{salt:r,target:s,wasmUrl:i}})=>{e!==i&&(e=i,await import(i).then((e=>e.default().then((r=>{t=(r&&r.exports?r.exports:e).solve_pow})))).catch((e=>{console.error("[cap] using fallback solver due to error:",e)})));try{const e=performance.now(),i=t(r,s),n=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(n-e).toFixed(2)})}catch(e){console.error("[cap] solver error",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:`Worker error: ${e.message||e}`})}}.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}})()`,window.Cap=i,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",s),"object"==typeof exports&&"undefined"!=typeof module?module.exports=i:"function"==typeof define&&define.amd&&define([],(function(){return i})),"undefined"!=typeof exports&&(exports.default=i)}();
package/cap.min.js CHANGED
@@ -1 +1 @@
1
- !function(){let e;const t="0.0.4",r=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class s extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#n;#a=!1;#o;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","[cap]"]}constructor(){super(),this.#o&&this.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o=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)}initialize(){this.#e=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#n=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#i.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:t,token:s}=await(await r(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(t),n=await(await r(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");const a=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${a}']`)&&(this.querySelector(`input[name='${a}']`).value=n.token),this.dispatchEvent("solve",{token:n.token}),this.token=n.token,this.#t&&clearTimeout(this.#t);const o=new Date(n.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(e){const r=e.length;let s=0;const i=Array(this.#r).fill(null).map((()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}})),n=([e,n],a)=>new Promise(((o,c)=>{const d=i[a];if(!d)return void c(new Error("Worker not available"));const h=setTimeout((()=>{try{d.terminate(),i[a]=new Worker(this.#e)}catch(e){console.error("[cap] Error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:t})=>{t.found&&(clearTimeout(h),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([e,n,t.nonce]))},d.onerror=e=>{clearTimeout(h),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:e,target:n,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${t}/browser/cap_wasm.min.js`})})),a=[];try{for(let t=0;t<e.length;t+=this.#r){const r=e.slice(t,Math.min(t+this.#r,e.length)),s=await Promise.all(r.map(((e,t)=>n(e,t))));a.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] Error terminating worker:",e)}}))}return a}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#i.classList.add("captcha"),this.#i.setAttribute("role","button"),this.#i.setAttribute("tabindex","0"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by&nbsp;</span>Cap</a>`,this.#s.innerHTML='<style>\n\n.captcha * {box-sizing:border-box;}\n\n.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);\nuser-select:none;\n\nheight:var(--cap-widget-height, 30px);\n\nwidth:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}\n\n.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}\n\n.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.appendChild(this.#i)}addEventListeners(){this.#i&&(this.#i.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#i.addEventListener("click",(()=>{this.#i.hasAttribute("disabled")||this.solve()})),this.#i.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#i.hasAttribute("disabled")||(e.preventDefault(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);if(r)try{new Function("event",r).call(this,t)}catch(t){console.error(`[cap] Error executing ${e}:`,t)}}error(e="Unknown error"){console.error("[cap] Error:",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;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.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o.clear(),this.#s&&(this.#s.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class i{constructor(e={},t){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}e=`(() => {${function(){if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:t}})=>{let r=0;let s=0;const i=new TextEncoder,n=new Uint8Array(t.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(t.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,s=i.encode(t),o=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(o,0,a);let d=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){d=!1;break}if(d)return void self.postMessage({nonce:r,found:!0});r++}s+=5e4}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap] WebAssembly is not supported, falling back to alternative solver.");let e,t;self.onmessage=async({data:{salt:r,target:s,wasmUrl:i}})=>{e!==i&&(e=i,await import(i).then((e=>e.default().then((r=>{t=(r&&r.exports?r.exports:e).solve_pow})))).catch((e=>{console.error("[cap] using fallback solver due to error:",e)})));try{const e=performance.now(),i=t(r,s),n=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(n-e).toFixed(2)})}catch(e){console.error("[cap] solver error",e),self.postMessage({found:!1,error:e.message||String(e)})}},self.onerror=e=>{self.postMessage({found:!1,error:`Worker error: ${e.message||e}`})}}.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}})()`,window.Cap=i,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",s),"object"==typeof exports&&"undefined"!=typeof module?module.exports=i:"function"==typeof define&&define.amd&&define([],(function(){return i})),"undefined"!=typeof exports&&(exports.default=i)}();
1
+ !function(){const e="0.0.5",t=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};window.CAP_CUSTOM_WASM_URL||[`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm.min.js`,`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm_bg.wasm`].forEach((e=>{const t=document.createElement("link");t.rel="prefetch",t.href=e,t.as=e.endsWith(".wasm")?"fetch":"script",document.head.appendChild(t)}));class r extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#i;#n;#a=!1;#o;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","[cap]"]}constructor(){super(),this.#o&&this.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o=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)}initialize(){this.#e=URL.createObjectURL(new Blob(['(()=>{if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:r}})=>{let o=0;let t=0;const s=new TextEncoder,n=new Uint8Array(r.length/2);for(let e=0;e<n.length;e++)n[e]=parseInt(r.substring(2*e,2*e+2),16);const a=n.length;for(;;)try{for(let r=0;r<5e4;r++){const r=e+o,t=s.encode(r),l=await crypto.subtle.digest("SHA-256",t),c=new Uint8Array(l,0,a);let f=!0;for(let e=0;e<a;e++)if(c[e]!==n[e]){f=!1;break}if(f)return void self.postMessage({nonce:o,found:!0});o++}t+=5e4}catch(e){return console.error("[cap worker] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}},console.warn("[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower.");let e,r;self.onmessage=async({data:{salt:o,target:t,wasmUrl:s}})=>{e!==s&&(e=s,await import(s).then((e=>e.default().then((o=>{r=(o&&o.exports?o.exports:e).solve_pow})))).catch((e=>{console.error("[cap worker] using fallback solver due to error:",e)})));try{const e=performance.now(),s=r(o,t),n=performance.now();self.postMessage({nonce:Number(s),found:!0,durationMs:(n-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"}))}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#o.get(e);if(s&&this.removeEventListener(t,s),r){const r=t=>{const r=this.getAttribute(e);"function"==typeof window[r]&&window[r].call(this,t)};this.#o.set(e,r),this.addEventListener(t,r)}}"data-cap-worker-count"===e&&this.setWorkersCount(parseInt(r)),"data-cap-i18n-initial-state"===e&&this.#i&&this.#i?.querySelector("p")?.innerText&&(this.#i.querySelector("p").innerText=this.getI18nText("initial-state","I'm a human"))}async connectedCallback(){this.#n=this,this.#s=this.attachShadow({mode:"open"}),this.#i=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#i.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count"),t=e?parseInt(e,10):null;this.setWorkersCount(t||navigator.hardwareConcurrency||8);const r=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#n.innerHTML=`<input type="hidden" name="${r}">`}async solve(){if(!this.#a)try{this.#a=!0,this.updateUI("verifying",this.getI18nText("verifying-label","Verifying..."),!0),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:r,token:s}=await(await t(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(r),n=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");const a=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${a}']`)&&(this.querySelector(`input[name='${a}']`).value=n.token),this.dispatchEvent("solve",{token:n.token}),this.token=n.token,this.#t&&clearTimeout(this.#t);const o=new Date(n.expires).getTime()-Date.now();return o>0&&o<864e5?this.#t=setTimeout((()=>this.reset()),o):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{this.#a=!1}}async solveChallenges(t){const r=t.length;let s=0;const i=Array(this.#r).fill(null).map((()=>{try{return new Worker(this.#e)}catch(e){throw console.error("[cap] Failed to create worker:",e),new Error("Worker creation failed")}})),n=([t,n],a)=>new Promise(((o,c)=>{const d=i[a];if(!d)return void c(new Error("Worker not available"));const h=setTimeout((()=>{try{d.terminate(),i[a]=new Worker(this.#e)}catch(e){console.error("[cap] error terminating/recreating worker:",e)}c(new Error("Worker timeout"))}),3e4);d.onmessage=({data:e})=>{e.found&&(clearTimeout(h),s++,this.dispatchEvent("progress",{progress:Math.round(s/r*100)}),o([t,n,e.nonce]))},d.onerror=e=>{clearTimeout(h),this.error(`Error in worker: ${e.message||e}`),c(e)},d.postMessage({salt:t,target:n,wasmUrl:window.CAP_CUSTOM_WASM_URL||`https://cdn.jsdelivr.net/npm/@cap.js/wasm@${e}/browser/cap_wasm.min.js`})})),a=[];try{for(let e=0;e<t.length;e+=this.#r){const r=t.slice(e,Math.min(e+this.#r,t.length)),s=await Promise.all(r.map(((e,t)=>n(e,t))));a.push(...s)}}finally{i.forEach((e=>{if(e)try{e.terminate()}catch(e){console.error("[cap] error terminating worker:",e)}}))}return a}setWorkersCount(e){const t=parseInt(e,10),r=Math.min(navigator.hardwareConcurrency||8,16);this.#r=!isNaN(t)&&t>0&&t<=r?t:navigator.hardwareConcurrency||8}createUI(){this.#i.classList.add("captcha"),this.#i.setAttribute("role","button"),this.#i.setAttribute("tabindex","0"),this.#i.setAttribute("disabled","true"),this.#i.innerHTML=`<div class="checkbox"></div><p>${this.getI18nText("initial-state","I'm a human")}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by&nbsp;</span>Cap</a>`,this.#s.innerHTML='<style>.captcha * {box-sizing:border-box;}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);\nuser-select:none;height:var(--cap-widget-height, 30px);width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying]\n.checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.appendChild(this.#i)}addEventListeners(){this.#i&&(this.#i.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#i.addEventListener("click",(()=>{this.#i.hasAttribute("disabled")||this.solve()})),this.#i.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#i.hasAttribute("disabled")||(e.preventDefault(),this.solve())})),this.addEventListener("progress",this.boundHandleProgress),this.addEventListener("solve",this.boundHandleSolve),this.addEventListener("error",this.boundHandleError),this.addEventListener("reset",this.boundHandleReset))}updateUI(e,t,r=!1){this.#i&&(this.#i.setAttribute("data-state",e),this.#i.querySelector("p").innerText=t,r?this.#i.setAttribute("disabled","true"):this.#i.removeAttribute("disabled"))}handleProgress(e){if(!this.#i)return;const t=this.#i.querySelector("p"),r=this.#i.querySelector(".checkbox");t&&r&&(r.style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`${this.getI18nText("verifying-label","Verifying...")} ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done",this.getI18nText("solved-label","You're a human"),!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error",this.getI18nText("error-label","Error. Try again.")),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("",this.getI18nText("initial-state","I'm a human")),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);r&&new Function("event",r).call(this,t)}error(e="Unknown error"){console.error("[cap]",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t={}){const r=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(r)}reset(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.dispatchEvent("reset"),this.token=null;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.#o.forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),this.#o.clear(),this.#s&&(this.#s.innerHTML=""),this.reset(),this.cleanup()}cleanup(){this.#t&&(clearTimeout(this.#t),this.#t=null),this.#e&&(URL.revokeObjectURL(this.#e),this.#e="")}}class s{constructor(e={},t){let r=t||document.createElement("cap-widget");if(Object.entries(e).forEach((([e,t])=>{r.setAttribute(e,t)})),!e.apiEndpoint)throw r.remove(),new Error("Missing API endpoint");r.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=r,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:()=>r.token,configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}window.Cap=s,customElements.get("cap-widget")||window?.CAP_DONT_SKIP_REDEFINE?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",r),"object"==typeof exports&&"undefined"!=typeof module?module.exports=s:"function"==typeof define&&define.amd&&define([],(function(){return s})),"undefined"!=typeof exports&&(exports.default=s)}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap.js/widget",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Client-side widget for Cap, a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 PoW.",
5
5
  "keywords": [
6
6
  "security",
@@ -51,9 +51,5 @@
51
51
  "license": "Apache-2.0",
52
52
  "author": "Tiago Rangel",
53
53
  "type": "commonjs",
54
- "main": "cap.min.js",
55
- "scripts": {
56
- "test": "echo \"Error: no test specified\" && exit 1",
57
- "npm:publish": "sudo npm publish --access public"
58
- }
54
+ "main": "cap.min.js"
59
55
  }
@@ -63,7 +63,7 @@
63
63
 
64
64
  const capWidget = document.querySelector(capWidgetSelector);
65
65
  if (!document.contains(capWidget) && !capWidget.solve) {
66
- throw new Error(`[Cap floating] Element "${capWidgetSelector}" doesn't exist or isn't a Cap widget`);
66
+ throw new Error(`[cap floating] "${capWidgetSelector}" doesn't exist or isn't a Cap widget`);
67
67
  }
68
68
 
69
69
  capWidget.style.display = "none";
package/src/cap.js CHANGED
@@ -1,7 +1,5 @@
1
1
  (function () {
2
- let workerScript;
3
-
4
- const WASM_VERSION = "0.0.4";
2
+ const WASM_VERSION = "0.0.5";
5
3
 
6
4
  const capFetch = function () {
7
5
  if (window?.CAP_CUSTOM_FETCH) {
@@ -68,7 +66,10 @@
68
66
 
69
67
  initialize() {
70
68
  this.#workerUrl = URL.createObjectURL(
71
- new Blob([workerScript], {
69
+ // MARK: worker injection
70
+ // this placeholder will be replaced with the actual worker by the build script
71
+
72
+ new Blob([`%%workerScript%%`], {
72
73
  type: "application/javascript",
73
74
  })
74
75
  );
@@ -220,7 +221,7 @@
220
221
  workers[workerId] = new Worker(this.#workerUrl);
221
222
  } catch (error) {
222
223
  console.error(
223
- "[cap] Error terminating/recreating worker:",
224
+ "[cap] error terminating/recreating worker:",
224
225
  error
225
226
  );
226
227
  }
@@ -270,7 +271,7 @@
270
271
  try {
271
272
  w.terminate();
272
273
  } catch (error) {
273
- console.error("[cap] Error terminating worker:", error);
274
+ console.error("[cap] error terminating worker:", error);
274
275
  }
275
276
  }
276
277
  });
@@ -299,20 +300,10 @@
299
300
  "initial-state",
300
301
  "I'm a human"
301
302
  )}</p><a href="https://capjs.js.org/" class="credits" target="_blank" rel="follow noopener"><span>Secured by&nbsp;</span>Cap</a>`;
302
- this.#shadow.innerHTML = `<style>
303
-
304
- .captcha * {box-sizing:border-box;}
305
-
306
- .captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);
307
- user-select:none;
308
303
 
309
- height:var(--cap-widget-height, 30px);
310
-
311
- width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}
312
-
313
- .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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}
314
-
315
- .captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying] .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>`;
304
+ this.#shadow.innerHTML = `<style>.captcha * {box-sizing:border-box;}.captcha{background-color:var(--cap-background,#fdfdfd);border:1px solid var(--cap-border-color,#dddddd8f);border-radius:var(--cap-border-radius,14px);
305
+ user-select:none;height:var(--cap-widget-height, 30px);width:var(--cap-widget-width, 230px);display:flex;align-items:center;padding:var(--cap-widget-padding,14px);gap:var(--cap-gap,15px);cursor:pointer;transition:filter .2s,transform .2s;position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color,#212121)}.captcha:hover{filter:brightness(98%)}.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);transition:opacity .2s;margin-top:var(--cap-checkbox-margin,2px);margin-bottom:var(--cap-checkbox-margin,2px)}.captcha *{font-family:var(--cap-font,system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity .2s}.captcha[data-state=verifying]
306
+ .checkbox{background: none;display:flex;align-items:center;justify-content:center;transform: scale(1.1);border: none;border-radius: 50%;background: conic-gradient(var(--cap-spinner-color,#000) 0%, var(--cap-spinner-color,#000) var(--progress, 0%), var(--cap-spinner-background-color,#eee) var(--progress, 0%), var(--cap-spinner-background-color,#eee) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background,#fdfdfd);width: calc(100% - var(--cap-spinner-thickness,5px));height: calc(100% - var(--cap-spinner-thickness,5px));border-radius: 50%;margin:calc(var(--cap-spinner-thickness,5px) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;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}.captcha[data-state=error] .checkbox{border:1px solid transparent;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/svg%3E"));background-size:cover}.captcha[disabled]{cursor:not-allowed}.captcha[disabled][data-state=verifying]{cursor:progress}.captcha[disabled][data-state=done]{cursor:default}.captcha .credits{position:absolute;bottom:10px;right:10px;font-size:var(--cap-credits-font-size,12px);color:var(--cap-color,#212121);opacity:var(--cap-opacity-hover,0.8)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>`;
316
307
 
317
308
  this.#shadow.appendChild(this.#div);
318
309
  }
@@ -407,16 +398,11 @@
407
398
  return;
408
399
  }
409
400
 
410
- try {
411
- const func = new Function("event", code);
412
- func.call(this, event);
413
- } catch (error) {
414
- console.error(`[cap] Error executing ${attributeName}:`, error);
415
- }
401
+ new Function("event", code).call(this, event);
416
402
  }
417
403
 
418
404
  error(message = "Unknown error") {
419
- console.error("[cap] Error:", message);
405
+ console.error("[cap]", message);
420
406
  this.dispatchEvent("error", { isCap: true, message });
421
407
  }
422
408
 
@@ -512,133 +498,13 @@
512
498
  }
513
499
  }
514
500
  }
515
-
516
- const workerFunct = function () {
517
- if (
518
- typeof WebAssembly !== "object" ||
519
- typeof WebAssembly?.instantiate !== "function"
520
- ) {
521
- self.onmessage = async ({ data: { salt, target } }) => {
522
- // Fallback solver in case WASM is not available
523
-
524
- let nonce = 0;
525
- const batchSize = 50000;
526
- let processed = 0;
527
- const encoder = new TextEncoder();
528
-
529
- const targetBytes = new Uint8Array(target.length / 2);
530
- for (let k = 0; k < targetBytes.length; k++) {
531
- targetBytes[k] = parseInt(target.substring(k * 2, k * 2 + 2), 16);
532
- }
533
- const targetBytesLength = targetBytes.length;
534
-
535
- while (true) {
536
- try {
537
- for (let i = 0; i < batchSize; i++) {
538
- const inputString = salt + nonce;
539
- const inputBytes = encoder.encode(inputString);
540
-
541
- const hashBuffer = await crypto.subtle.digest(
542
- "SHA-256",
543
- inputBytes
544
- );
545
-
546
- const hashBytes = new Uint8Array(
547
- hashBuffer,
548
- 0,
549
- targetBytesLength
550
- );
551
-
552
- let matches = true;
553
- for (let k = 0; k < targetBytesLength; k++) {
554
- if (hashBytes[k] !== targetBytes[k]) {
555
- matches = false;
556
- break;
557
- }
558
- }
559
-
560
- if (matches) {
561
- self.postMessage({ nonce, found: true });
562
- return;
563
- }
564
-
565
- nonce++;
566
- }
567
-
568
- processed += batchSize;
569
- } catch (error) {
570
- console.error("[cap] fallback worker error", error);
571
- self.postMessage({
572
- found: false,
573
- error: error.message,
574
- });
575
- return;
576
- }
577
- }
578
- };
579
-
580
- return console.warn(
581
- "[cap] WebAssembly is not supported, falling back to alternative solver."
582
- );
583
- }
584
-
585
- let wasmCacheUrl, solve_pow_function;
586
-
587
- self.onmessage = async ({ data: { salt, target, wasmUrl } }) => {
588
- if (wasmCacheUrl !== wasmUrl) {
589
- wasmCacheUrl = wasmUrl;
590
- await import(wasmUrl)
591
- .then((wasmModule) => {
592
- return wasmModule.default().then((instance) => {
593
- solve_pow_function = (
594
- instance && instance.exports ? instance.exports : wasmModule
595
- ).solve_pow;
596
- });
597
- })
598
- .catch((e) => {
599
- console.error("[cap] using fallback solver due to error:", e);
600
- });
601
- }
602
-
603
- try {
604
- const startTime = performance.now();
605
- const nonce = solve_pow_function(salt, target);
606
- const endTime = performance.now();
607
-
608
- self.postMessage({
609
- nonce: Number(nonce),
610
- found: true,
611
- durationMs: (endTime - startTime).toFixed(2),
612
- });
613
- } catch (error) {
614
- console.error("[cap] solver error", error);
615
- self.postMessage({
616
- found: false,
617
- error: error.message || String(error),
618
- });
619
- }
620
- };
621
-
622
- self.onerror = (error) => {
623
- self.postMessage({
624
- found: false,
625
- error: `Worker error: ${error.message || error}`,
626
- });
627
- };
628
- };
629
-
630
- workerScript = `(() => {${workerFunct
631
- .toString()
632
- .replace(/^function\s*\([^\)]*\)\s*{|\}$/g, "")
633
- .trim()}})()`;
634
-
635
501
  window.Cap = Cap;
636
502
 
637
- if (!customElements.get("cap-widget")) {
503
+ if (!customElements.get("cap-widget") && !window?.CAP_DONT_SKIP_REDEFINE) {
638
504
  customElements.define("cap-widget", CapWidget);
639
505
  } else {
640
506
  console.warn(
641
- "The cap-widget element has already been defined. Skipping re-defining it."
507
+ "[cap] the cap-widget element has already been defined, skipping re-defining it.\nto prevent this, set window.CAP_DONT_SKIP_REDEFINE to true"
642
508
  );
643
509
  }
644
510
 
package/src/worker.js ADDED
@@ -0,0 +1,109 @@
1
+ (() => {
2
+ if (
3
+ typeof WebAssembly !== "object" ||
4
+ typeof WebAssembly?.instantiate !== "function"
5
+ ) {
6
+ self.onmessage = async ({ data: { salt, target } }) => {
7
+ // Fallback solver in case WASM is not available
8
+
9
+ let nonce = 0;
10
+ const batchSize = 50000;
11
+ let processed = 0;
12
+ const encoder = new TextEncoder();
13
+
14
+ const targetBytes = new Uint8Array(target.length / 2);
15
+ for (let k = 0; k < targetBytes.length; k++) {
16
+ targetBytes[k] = parseInt(target.substring(k * 2, k * 2 + 2), 16);
17
+ }
18
+ const targetBytesLength = targetBytes.length;
19
+
20
+ while (true) {
21
+ try {
22
+ for (let i = 0; i < batchSize; i++) {
23
+ const inputString = salt + nonce;
24
+ const inputBytes = encoder.encode(inputString);
25
+
26
+ const hashBuffer = await crypto.subtle.digest(
27
+ "SHA-256",
28
+ inputBytes
29
+ );
30
+
31
+ const hashBytes = new Uint8Array(hashBuffer, 0, targetBytesLength);
32
+
33
+ let matches = true;
34
+ for (let k = 0; k < targetBytesLength; k++) {
35
+ if (hashBytes[k] !== targetBytes[k]) {
36
+ matches = false;
37
+ break;
38
+ }
39
+ }
40
+
41
+ if (matches) {
42
+ self.postMessage({ nonce, found: true });
43
+ return;
44
+ }
45
+
46
+ nonce++;
47
+ }
48
+
49
+ processed += batchSize;
50
+ } catch (error) {
51
+ console.error("[cap worker] fallback worker error", error);
52
+ self.postMessage({
53
+ found: false,
54
+ error: error.message,
55
+ });
56
+ return;
57
+ }
58
+ }
59
+ };
60
+
61
+ return console.warn(
62
+ "[cap worker] wasm not supported, falling back to alternative solver. this will be significantly slower."
63
+ );
64
+ }
65
+
66
+ let wasmCacheUrl, solve_pow_function;
67
+
68
+ self.onmessage = async ({ data: { salt, target, wasmUrl } }) => {
69
+ if (wasmCacheUrl !== wasmUrl) {
70
+ wasmCacheUrl = wasmUrl;
71
+ await import(wasmUrl)
72
+ .then((wasmModule) => {
73
+ return wasmModule.default().then((instance) => {
74
+ solve_pow_function = (
75
+ instance && instance.exports ? instance.exports : wasmModule
76
+ ).solve_pow;
77
+ });
78
+ })
79
+ .catch((e) => {
80
+ console.error("[cap worker] using fallback solver due to error:", e);
81
+ });
82
+ }
83
+
84
+ try {
85
+ const startTime = performance.now();
86
+ const nonce = solve_pow_function(salt, target);
87
+ const endTime = performance.now();
88
+
89
+ self.postMessage({
90
+ nonce: Number(nonce),
91
+ found: true,
92
+ durationMs: (endTime - startTime).toFixed(2),
93
+ });
94
+ } catch (error) {
95
+ console.error("[cap worker]", error);
96
+ self.postMessage({
97
+ found: false,
98
+ error: error.message || String(error),
99
+ });
100
+ }
101
+ };
102
+
103
+ self.onerror = (error) => {
104
+ self.postMessage({
105
+ found: false,
106
+ error,
107
+ });
108
+ };
109
+ })();