@cap.js/widget 0.0.22 → 0.1.1

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.compat.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";function _classPrivateFieldLooseBase(e,t){if(!{}.hasOwnProperty.call(e,t))throw new TypeError("attempted to use private field on non-instance");return e}var id=0;function _classPrivateFieldLooseKey(e){return"__private_"+id+++"_"+e}!function(){let e;const t=function(){var e;return null!=(e=window)&&e.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)},s=function(e,t){return void 0===t&&(t=1e4),new Promise(((s,a)=>{const i=setTimeout((()=>{a(new Error("Initialize timeout"))}),t),r=()=>{e()?(clearTimeout(i),s()):setTimeout(r,500)};r()}))};var a=_classPrivateFieldLooseKey("workerUrl"),i=_classPrivateFieldLooseKey("resetTimer"),r=_classPrivateFieldLooseKey("workersCount"),o=_classPrivateFieldLooseKey("token"),n=_classPrivateFieldLooseKey("shadow"),c=_classPrivateFieldLooseKey("div"),l=_classPrivateFieldLooseKey("host"),d=_classPrivateFieldLooseKey("solving"),h=_classPrivateFieldLooseKey("eventHandlers");class p extends HTMLElement{static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","workers"]}constructor(){super(),Object.defineProperty(this,a,{writable:!0,value:""}),Object.defineProperty(this,i,{writable:!0,value:null}),Object.defineProperty(this,r,{writable:!0,value:navigator.hardwareConcurrency||8}),Object.defineProperty(this,o,{writable:!0,value:null}),Object.defineProperty(this,n,{writable:!0,value:void 0}),Object.defineProperty(this,c,{writable:!0,value:void 0}),Object.defineProperty(this,l,{writable:!0,value:void 0}),Object.defineProperty(this,d,{writable:!0,value:!1}),Object.defineProperty(this,h,{writable:!0,value:void 0}),_classPrivateFieldLooseBase(this,h)[h]&&_classPrivateFieldLooseBase(this,h)[h].forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),_classPrivateFieldLooseBase(this,h)[h]=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)}async initialize(){_classPrivateFieldLooseBase(this,a)[a]&&URL.revokeObjectURL(_classPrivateFieldLooseBase(this,a)[a]);try{await s((()=>!!e)),_classPrivateFieldLooseBase(this,a)[a]=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}catch(e){throw this.error("Failed to initialize worker"),e}}attributeChangedCallback(e,t,s){if(e.startsWith("on")){const t=e.slice(2),a=_classPrivateFieldLooseBase(this,h)[h].get(e);if(a&&this.removeEventListener(t,a),s){const s=t=>{const s=this.getAttribute(e);"function"==typeof window[s]&&window[s].call(this,t)};_classPrivateFieldLooseBase(this,h)[h].set(e,s),this.addEventListener(t,s)}}}async connectedCallback(){_classPrivateFieldLooseBase(this,l)[l]=this,_classPrivateFieldLooseBase(this,n)[n]=this.attachShadow({mode:"open"}),_classPrivateFieldLooseBase(this,c)[c]=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),_classPrivateFieldLooseBase(this,c)[c].removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count");this.setWorkersCount(parseInt(e)?parseInt(e,10):navigator.hardwareConcurrency||8),_classPrivateFieldLooseBase(this,l)[l].innerHTML='<input type="hidden" name="cap-token">'}async solve(){if(!_classPrivateFieldLooseBase(this,d)[d])try{_classPrivateFieldLooseBase(this,d)[d]=!0,this.updateUI("verifying","Verifying...",!0),await s((()=>!!_classPrivateFieldLooseBase(this,a)[a])),this.dispatchEvent("progress",{progress:0});try{const e=this.getAttribute("data-cap-api-endpoint");if(!e)throw new Error("Missing API endpoint");const{challenge:s,token:a}=await(await t(`${e}challenge`,{method:"POST"})).json(),r=await this.solveChallenges(s),n=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:a,solutions:r}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!n.success)throw new Error("Invalid solution");this.querySelector("input[name='cap-token']")&&(this.querySelector("input[name='cap-token']").value=n.token),this.dispatchEvent("solve",{token:n.token}),_classPrivateFieldLooseBase(this,o)[o]=n.token,_classPrivateFieldLooseBase(this,i)[i]&&clearTimeout(_classPrivateFieldLooseBase(this,i)[i]);const c=new Date(n.expires).getTime()-Date.now();return c>0&&c<864e5?_classPrivateFieldLooseBase(this,i)[i]=setTimeout((()=>this.reset()),c):this.error("Invalid expiration time"),{success:!0,token:_classPrivateFieldLooseBase(this,o)[o]}}catch(e){throw this.error(e.message),e}}finally{_classPrivateFieldLooseBase(this,d)[d]=!1}}async solveChallenges(e){const t=e.length;let s=0;const i=Array(_classPrivateFieldLooseBase(this,r)[r]).fill(null).map((()=>new Worker(_classPrivateFieldLooseBase(this,a)[a]))),o=(e,r)=>{let[o,n]=e;return new Promise(((e,c)=>{const l=i[r],d=setTimeout((()=>{l.terminate(),i[r]=new Worker(_classPrivateFieldLooseBase(this,a)[a]),c(new Error("Worker timeout"))}),3e4);l.onmessage=a=>{let{data:i}=a;i.found&&(clearTimeout(d),s++,this.dispatchEvent("progress",{progress:Math.round(s/t*100)}),e([o,n,i.nonce]))},l.onerror=e=>{clearTimeout(d),this.error(`Error in worker: ${e}`),c(e)},l.postMessage({salt:o,target:n})}))},n=[];try{for(let t=0;t<e.length;t+=_classPrivateFieldLooseBase(this,r)[r]){const s=e.slice(t,Math.min(t+_classPrivateFieldLooseBase(this,r)[r],e.length)),a=await Promise.all(s.map(((e,t)=>o(e,t))));n.push(...a)}}finally{i.forEach((e=>e.terminate()))}return n}setWorkersCount(e){const t=parseInt(e,10),s=Math.min(navigator.hardwareConcurrency||8,16);_classPrivateFieldLooseBase(this,r)[r]=!isNaN(t)&&t>0&&t<=s?t:navigator.hardwareConcurrency||8}createUI(){_classPrivateFieldLooseBase(this,c)[c].classList.add("captcha"),_classPrivateFieldLooseBase(this,c)[c].setAttribute("role","button"),_classPrivateFieldLooseBase(this,c)[c].setAttribute("tabindex","0"),_classPrivateFieldLooseBase(this,c)[c].setAttribute("disabled","true"),_classPrivateFieldLooseBase(this,c)[c].innerHTML='<div class="checkbox"></div><p>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>',_classPrivateFieldLooseBase(this,n)[n].innerHTML='<style>.captcha{background-color:var(--cap-background);border:1px solid var(--cap-border-color);border-radius:var(--cap-border-radius);width:var(--cap-widget-width);display:flex;align-items:center;padding:var(--cap-widget-padding);gap:var(--cap-gap);cursor:pointer;transition:filter var(--cap-transition-duration),transform var(--cap-transition-duration);position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color)}.captcha:hover{filter:var(--cap-hover-filter)}.captcha:not([disabled]):active{transform:scale(var(--cap-active-scale))}.checkbox{width:var(--cap-checkbox-size);height:var(--cap-checkbox-size);border:var(--cap-checkbox-border);border-radius:var(--cap-checkbox-border-radius);background-color:var(--cap-checkbox-background);transition:opacity var(--cap-transition-duration);margin-top:var(--cap-checkbox-margin);margin-bottom:var(--cap-checkbox-margin)}.captcha *{font-family:var(--cap-font)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity var(--cap-transition-duration)}.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) 0%, var(--cap-spinner-color) var(--progress, 0%), var(--cap-spinner-background-color) var(--progress, 0%), var(--cap-spinner-background-color) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background);width: calc(100% - var(--cap-spinner-thickness));height: calc(100% - var(--cap-spinner-thickness));border-radius: 50%;margin:calc(var(--cap-spinner-thickness) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;background-image:var(--cap-checkmark);background-size:cover}.captcha[data-state=error] .checkbox{border:1px solid transparent;background-image:var(--cap-error-cross);background-size:cover}.captcha[disabled]{\ncursor: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);color:var(--cap-color);opacity:var(--cap-opacity-hover)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',_classPrivateFieldLooseBase(this,n)[n].appendChild(_classPrivateFieldLooseBase(this,c)[c])}addEventListeners(){_classPrivateFieldLooseBase(this,c)[c].querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),_classPrivateFieldLooseBase(this,c)[c].addEventListener("click",(()=>{_classPrivateFieldLooseBase(this,c)[c].hasAttribute("disabled")||this.solve()})),_classPrivateFieldLooseBase(this,c)[c].addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||_classPrivateFieldLooseBase(this,c)[c].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,s){void 0===s&&(s=!1),_classPrivateFieldLooseBase(this,c)[c].setAttribute("data-state",e),_classPrivateFieldLooseBase(this,c)[c].querySelector("p").innerText=t,s?_classPrivateFieldLooseBase(this,c)[c].setAttribute("disabled","true"):_classPrivateFieldLooseBase(this,c)[c].removeAttribute("disabled")}handleProgress(e){const t=_classPrivateFieldLooseBase(this,c)[c].querySelector("p");t&&(_classPrivateFieldLooseBase(this,c)[c].querySelector(".checkbox").style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`Verifying... ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done","You're a human",!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error","Error. Try again."),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("","I'm a human"),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const s=this.getAttribute(e);if(!s)return;new Function("event",s).call(this,t)}error(e){void 0===e&&(e="Unknown error"),console.error("[Cap] Error:",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t){void 0===t&&(t={});const s=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(s)}reset(){_classPrivateFieldLooseBase(this,i)[i]&&(clearTimeout(_classPrivateFieldLooseBase(this,i)[i]),_classPrivateFieldLooseBase(this,i)[i]=null),this.dispatchEvent("reset"),_classPrivateFieldLooseBase(this,o)[o]=null,this.querySelector("input[name='cap-token']")&&(this.querySelector("input[name='cap-token']").value="")}get token(){return _classPrivateFieldLooseBase(this,o)[o]}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),_classPrivateFieldLooseBase(this,h)[h].forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),_classPrivateFieldLooseBase(this,h)[h].clear(),_classPrivateFieldLooseBase(this,n)[n]&&(_classPrivateFieldLooseBase(this,n)[n].innerHTML=""),this.reset(),this.cleanup()}cleanup(){_classPrivateFieldLooseBase(this,i)[i]&&(clearTimeout(_classPrivateFieldLooseBase(this,i)[i]),_classPrivateFieldLooseBase(this,i)[i]=null),_classPrivateFieldLooseBase(this,a)[a]&&(URL.revokeObjectURL(_classPrivateFieldLooseBase(this,a)[a]),_classPrivateFieldLooseBase(this,a)[a]="")}}class v{constructor(e,t){void 0===e&&(e={});let s=t||document.createElement("cap-widget");if(Object.entries(e).forEach((e=>{let[t,a]=e;s.setAttribute(t,a)})),!e.apiEndpoint)throw s.remove(),new Error("Missing API endpoint");s.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=s,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:()=>s.getToken(),configurable:!0,enumerable:!0}),t||(s.style.display="none",document.documentElement.appendChild(s))}}const u=new CSSStyleSheet;u.replaceSync('html{--cap-font:system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif;--cap-color:#212121;--cap-background:#fdfdfd;--cap-border-color:#dddddd8f;--cap-border-radius:14px;--cap-checkbox-border:1px solid #aaaaaad1;--cap-checkbox-border-radius:6px;--cap-checkbox-background:#fafafa91;--cap-widget-width:240px;--cap-widget-padding:14px;--cap-checkbox-size:24px;--cap-checkbox-margin:2px;--cap-transition-duration:0.2s;--cap-gap:15px;--cap-opacity-hover:0.8;--cap-hover-filter:brightness(97%);--cap-active-scale:0.98;--cap-credits-font-size:12px;--cap-spinner-color:black;--cap-spinner-background-color:#eee;--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");--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");--cap-spinner-thickness:5px;}'),document.adoptedStyleSheets.push(u);const b=function(){let e;self.onmessage=async t=>{let{data:{salt:s,target:a}}=t,i=0;const r=5e4;let o=0;const n=new TextEncoder;if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly.instantiate){console.log("[cap] WASM not enabled, falling back to crypto.subtle\nThis is significanty slower than the WASM implementation");const e=new Uint8Array(a.length/2);for(let t=0;t<e.length;t++)e[t]=parseInt(a.substring(2*t,2*t+2),16);const t=e.length;for(;;)try{for(let a=0;a<r;a++){const a=s+i,r=n.encode(a),o=await crypto.subtle.digest("SHA-256",r),c=new Uint8Array(o,0,t);let l=!0;for(let s=0;s<t;s++)if(c[s]!==e[s]){l=!1;break}if(l)return void self.postMessage({nonce:i,found:!0});i++}o+=r}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}}e||(e=await hashwasm.createSHA256());const c=new Uint8Array(128);for(;;)try{for(let t=0;t<r;t++){const t=s+i.toString(),r=n.encode(t);c.set(r),e.init(),e.update(c.subarray(0,r.length));if(e.digest("hex").startsWith(a))return void self.postMessage({nonce:i,found:!0});i++}o+=r}catch(e){return void self.postMessage({found:!1,error:e.message})}}};setTimeout((async function(){e=await(await t("https://cdn.jsdelivr.net/npm/@cap.js/widget/wasm-hashes.min.js")).text()+b.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}),1),window.Cap=v,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",p),"object"==typeof exports&&"undefined"!=typeof module?module.exports=v:"function"==typeof define&&define.amd&&define([],(function(){return v})),"undefined"!=typeof exports&&(exports.default=v)}();
1
+ "use strict";function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,s=new WeakMap;return(_getRequireWildcardCache=function(e){return e?s:t})(e)}function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var s=_getRequireWildcardCache(t);if(s&&s.has(e))return s.get(e);var a={__proto__:null},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&{}.hasOwnProperty.call(e,i)){var o=r?Object.getOwnPropertyDescriptor(e,i):null;o&&(o.get||o.set)?Object.defineProperty(a,i,o):a[i]=e[i]}return a.default=e,s&&s.set(e,a),a}function _classPrivateFieldLooseBase(e,t){if(!{}.hasOwnProperty.call(e,t))throw new TypeError("attempted to use private field on non-instance");return e}var id=0;function _classPrivateFieldLooseKey(e){return"__private_"+id+++"_"+e}!function(){let e;const t=function(){var e;return null!=(e=window)&&e.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};var s=_classPrivateFieldLooseKey("workerUrl"),a=_classPrivateFieldLooseKey("resetTimer"),r=_classPrivateFieldLooseKey("workersCount"),i=_classPrivateFieldLooseKey("shadow"),o=_classPrivateFieldLooseKey("div"),n=_classPrivateFieldLooseKey("host"),c=_classPrivateFieldLooseKey("solving"),l=_classPrivateFieldLooseKey("eventHandlers");class d extends HTMLElement{static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","workers","[cap]"]}constructor(){super(),Object.defineProperty(this,s,{writable:!0,value:""}),Object.defineProperty(this,a,{writable:!0,value:null}),Object.defineProperty(this,r,{writable:!0,value:navigator.hardwareConcurrency||8}),this.token=null,Object.defineProperty(this,i,{writable:!0,value:void 0}),Object.defineProperty(this,o,{writable:!0,value:void 0}),Object.defineProperty(this,n,{writable:!0,value:void 0}),Object.defineProperty(this,c,{writable:!0,value:!1}),Object.defineProperty(this,l,{writable:!0,value:void 0}),_classPrivateFieldLooseBase(this,l)[l]&&_classPrivateFieldLooseBase(this,l)[l].forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),_classPrivateFieldLooseBase(this,l)[l]=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(){_classPrivateFieldLooseBase(this,s)[s]=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}attributeChangedCallback(e,t,s){if(e.startsWith("on")){const t=e.slice(2),a=_classPrivateFieldLooseBase(this,l)[l].get(e);if(a&&this.removeEventListener(t,a),s){const s=t=>{const s=this.getAttribute(e);"function"==typeof window[s]&&window[s].call(this,t)};_classPrivateFieldLooseBase(this,l)[l].set(e,s),this.addEventListener(t,s)}}}async connectedCallback(){_classPrivateFieldLooseBase(this,n)[n]=this,_classPrivateFieldLooseBase(this,i)[i]=this.attachShadow({mode:"open"}),_classPrivateFieldLooseBase(this,o)[o]=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),_classPrivateFieldLooseBase(this,o)[o].removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count");this.setWorkersCount(parseInt(e)?parseInt(e,10):navigator.hardwareConcurrency||8);const t=this.getAttribute("data-cap-hidden-field-name")||"cap-token";_classPrivateFieldLooseBase(this,n)[n].innerHTML=`<input type="hidden" name="${t}">`}async solve(){if(!_classPrivateFieldLooseBase(this,c)[c])try{_classPrivateFieldLooseBase(this,c)[c]=!0,this.updateUI("verifying","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:s,token:r}=await(await t(`${e}challenge`,{method:"POST"})).json(),i=await this.solveChallenges(s),o=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:r,solutions:i}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!o.success)throw new Error("Invalid solution");const n=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${n}']`)&&(this.querySelector(`input[name='${n}']`).value=o.token),this.dispatchEvent("solve",{token:o.token}),this.token=o.token,_classPrivateFieldLooseBase(this,a)[a]&&clearTimeout(_classPrivateFieldLooseBase(this,a)[a]);const c=new Date(o.expires).getTime()-Date.now();return c>0&&c<864e5?_classPrivateFieldLooseBase(this,a)[a]=setTimeout((()=>this.reset()),c):this.error("Invalid expiration time"),{success:!0,token:this.token}}catch(e){throw this.error(e.message),e}}finally{_classPrivateFieldLooseBase(this,c)[c]=!1}}async solveChallenges(e){const t=e.length;let a=0;const i=Array(_classPrivateFieldLooseBase(this,r)[r]).fill(null).map((()=>new Worker(_classPrivateFieldLooseBase(this,s)[s]))),o=(e,r)=>{let[o,n]=e;return new Promise(((e,c)=>{const l=i[r],d=setTimeout((()=>{l.terminate(),i[r]=new Worker(_classPrivateFieldLooseBase(this,s)[s]),c(new Error("Worker timeout"))}),3e4);l.onmessage=s=>{let{data:r}=s;r.found&&(clearTimeout(d),a++,this.dispatchEvent("progress",{progress:Math.round(a/t*100)}),e([o,n,r.nonce]))},l.onerror=e=>{clearTimeout(d),this.error(`Error in worker: ${e}`),c(e)},l.postMessage({salt:o,target:n})}))},n=[];try{for(let t=0;t<e.length;t+=_classPrivateFieldLooseBase(this,r)[r]){const s=e.slice(t,Math.min(t+_classPrivateFieldLooseBase(this,r)[r],e.length)),a=await Promise.all(s.map(((e,t)=>o(e,t))));n.push(...a)}}finally{i.forEach((e=>e.terminate()))}return n}setWorkersCount(e){const t=parseInt(e,10),s=Math.min(navigator.hardwareConcurrency||8,16);_classPrivateFieldLooseBase(this,r)[r]=!isNaN(t)&&t>0&&t<=s?t:navigator.hardwareConcurrency||8}createUI(){_classPrivateFieldLooseBase(this,o)[o].classList.add("captcha"),_classPrivateFieldLooseBase(this,o)[o].setAttribute("role","button"),_classPrivateFieldLooseBase(this,o)[o].setAttribute("tabindex","0"),_classPrivateFieldLooseBase(this,o)[o].setAttribute("disabled","true"),_classPrivateFieldLooseBase(this,o)[o].innerHTML='<div class="checkbox"></div><p>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>',_classPrivateFieldLooseBase(this,i)[i].innerHTML='<style>.captcha{background-color:var(--cap-background);border:1px solid var(--cap-border-color);border-radius:var(--cap-border-radius);width:var(--cap-widget-width);display:flex;align-items:center;padding:var(--cap-widget-padding);gap:var(--cap-gap);cursor:pointer;transition:filter var(--cap-transition-duration),transform var(--cap-transition-duration);position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color)}.captcha:hover{filter:var(--cap-hover-filter)}.captcha:not([disabled]):active{transform:scale(var(--cap-active-scale))}.checkbox{width:var(--cap-checkbox-size);height:var(--cap-checkbox-size);border:var(--cap-checkbox-border);border-radius:var(--cap-checkbox-border-radius);background-color:var(--cap-checkbox-background);transition:opacity var(--cap-transition-duration);margin-top:var(--cap-checkbox-margin);margin-bottom:var(--cap-checkbox-margin)}.captcha *{font-family:var(--cap-font)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity var(--cap-transition-duration)}.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) 0%, var(--cap-spinner-color) var(--progress, 0%), var(--cap-spinner-background-color) var(--progress, 0%), var(--cap-spinner-background-color) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background);width: calc(100% - var(--cap-spinner-thickness));height: calc(100% - var(--cap-spinner-thickness));border-radius: 50%;margin:calc(var(--cap-spinner-thickness) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;background-image:var(--cap-checkmark);background-size:cover}.captcha[data-state=error] .checkbox{border:1px solid transparent;background-image:var(--cap-error-cross);background-size:cover}.captcha[disabled]{\ncursor: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);color:var(--cap-color);opacity:var(--cap-opacity-hover)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',_classPrivateFieldLooseBase(this,i)[i].appendChild(_classPrivateFieldLooseBase(this,o)[o])}addEventListeners(){_classPrivateFieldLooseBase(this,o)[o].querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),_classPrivateFieldLooseBase(this,o)[o].addEventListener("click",(()=>{_classPrivateFieldLooseBase(this,o)[o].hasAttribute("disabled")||this.solve()})),_classPrivateFieldLooseBase(this,o)[o].addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||_classPrivateFieldLooseBase(this,o)[o].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,s){void 0===s&&(s=!1),_classPrivateFieldLooseBase(this,o)[o].setAttribute("data-state",e),_classPrivateFieldLooseBase(this,o)[o].querySelector("p").innerText=t,s?_classPrivateFieldLooseBase(this,o)[o].setAttribute("disabled","true"):_classPrivateFieldLooseBase(this,o)[o].removeAttribute("disabled")}handleProgress(e){const t=_classPrivateFieldLooseBase(this,o)[o].querySelector("p");t&&(_classPrivateFieldLooseBase(this,o)[o].querySelector(".checkbox").style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`Verifying... ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done","You're a human",!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error","Error. Try again."),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("","I'm a human"),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const s=this.getAttribute(e);if(!s)return;new Function("event",s).call(this,t)}error(e){void 0===e&&(e="Unknown error"),console.error("[cap] Error:",e),this.dispatchEvent("error",{isCap:!0,message:e})}dispatchEvent(e,t){void 0===t&&(t={});const s=new CustomEvent(e,{bubbles:!0,composed:!0,detail:t});super.dispatchEvent(s)}reset(){_classPrivateFieldLooseBase(this,a)[a]&&(clearTimeout(_classPrivateFieldLooseBase(this,a)[a]),_classPrivateFieldLooseBase(this,a)[a]=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 token(){return this.token}disconnectedCallback(){this.removeEventListener("progress",this.boundHandleProgress),this.removeEventListener("solve",this.boundHandleSolve),this.removeEventListener("error",this.boundHandleError),this.removeEventListener("reset",this.boundHandleReset),_classPrivateFieldLooseBase(this,l)[l].forEach(((e,t)=>{this.removeEventListener(t.slice(2),e)})),_classPrivateFieldLooseBase(this,l)[l].clear(),_classPrivateFieldLooseBase(this,i)[i]&&(_classPrivateFieldLooseBase(this,i)[i].innerHTML=""),this.reset(),this.cleanup()}cleanup(){_classPrivateFieldLooseBase(this,a)[a]&&(clearTimeout(_classPrivateFieldLooseBase(this,a)[a]),_classPrivateFieldLooseBase(this,a)[a]=null),_classPrivateFieldLooseBase(this,s)[s]&&(URL.revokeObjectURL(_classPrivateFieldLooseBase(this,s)[s]),_classPrivateFieldLooseBase(this,s)[s]="")}}class h{constructor(e,t){void 0===e&&(e={});let s=t||document.createElement("cap-widget");if(Object.entries(e).forEach((e=>{let[t,a]=e;s.setAttribute(t,a)})),!e.apiEndpoint)throw s.remove(),new Error("Missing API endpoint");s.setAttribute("data-cap-api-endpoint",e.apiEndpoint),this.widget=s,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:()=>s.getToken(),configurable:!0,enumerable:!0}),t||(s.style.display="none",document.documentElement.appendChild(s))}}const p=new CSSStyleSheet;p.replaceSync('html{--cap-font:system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif;--cap-color:#212121;--cap-background:#fdfdfd;--cap-border-color:#dddddd8f;--cap-border-radius:14px;--cap-checkbox-border:1px solid #aaaaaad1;--cap-checkbox-border-radius:6px;--cap-checkbox-background:#fafafa91;--cap-widget-width:240px;--cap-widget-padding:14px;--cap-checkbox-size:24px;--cap-checkbox-margin:2px;--cap-transition-duration:0.2s;--cap-gap:15px;--cap-opacity-hover:0.8;--cap-hover-filter:brightness(97%);--cap-active-scale:0.98;--cap-credits-font-size:12px;--cap-spinner-color:black;--cap-spinner-background-color:#eee;--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");--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");--cap-spinner-thickness:5px;}'),document.adoptedStyleSheets.push(p);e=`(() => {${function(){var e;let t,s;if("object"!=typeof WebAssembly||"function"!=typeof(null==(e=WebAssembly)?void 0:e.instantiate))return self.onmessage=async e=>{let{data:{salt:t,target:s}}=e,a=0;let r=0;const i=new TextEncoder,o=new Uint8Array(s.length/2);for(let e=0;e<o.length;e++)o[e]=parseInt(s.substring(2*e,2*e+2),16);const n=o.length;for(;;)try{for(let e=0;e<5e4;e++){const e=t+a,s=i.encode(e),r=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(r,0,n);let l=!0;for(let e=0;e<n;e++)if(c[e]!==o[e]){l=!1;break}if(l)return void self.postMessage({nonce:a,found:!0});a++}r+=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.");t=Promise.resolve().then((()=>_interopRequireWildcard(require("https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.3/browser/cap_wasm.min.js")))).then((e=>e.default().then((t=>{s=(t&&t.exports?t.exports:e).solve_pow})))).catch((e=>{useFallback=!0,console.error("[cap] using fallback solver due to error:",e)})),self.onmessage=async e=>{let{data:{salt:a,target:r}}=e;try{await t;const e=performance.now(),i=s(a,r),o=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(o-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=h,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):customElements.define("cap-widget",d),"object"==typeof exports&&"undefined"!=typeof module?module.exports=h:"function"==typeof define&&define.amd&&define([],(function(){return h})),"undefined"!=typeof exports&&(exports.default=h)}();
package/cap.min.js CHANGED
@@ -1 +1 @@
1
- "use strict";!function(){let e;const t=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)},r=(e,t=1e4)=>new Promise(((r,s)=>{const i=setTimeout((()=>{s(new Error("Initialize timeout"))}),t),a=()=>{e()?(clearTimeout(i),r()):setTimeout(a,500)};a()}));class s extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;#s=null;#i;#a;#n;#o=!1;#c;static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","workers"]}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)}async initialize(){this.#e&&URL.revokeObjectURL(this.#e);try{await r((()=>!!e)),this.#e=URL.createObjectURL(new Blob([e],{type:"application/javascript"}))}catch(e){throw this.error("Failed to initialize worker"),e}}attributeChangedCallback(e,t,r){if(e.startsWith("on")){const t=e.slice(2),s=this.#c.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.#c.set(e,r),this.addEventListener(t,r)}}}async connectedCallback(){this.#n=this,this.#i=this.attachShadow({mode:"open"}),this.#a=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#a.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count");this.setWorkersCount(parseInt(e)?parseInt(e,10):navigator.hardwareConcurrency||8),this.#n.innerHTML='<input type="hidden" name="cap-token">'}async solve(){if(!this.#o)try{this.#o=!0,this.updateUI("verifying","Verifying...",!0),await r((()=>!!this.#e)),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),a=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}),!a.success)throw new Error("Invalid solution");this.querySelector("input[name='cap-token']")&&(this.querySelector("input[name='cap-token']").value=a.token),this.dispatchEvent("solve",{token:a.token}),this.#s=a.token,this.#t&&clearTimeout(this.#t);const n=new Date(a.expires).getTime()-Date.now();return n>0&&n<864e5?this.#t=setTimeout((()=>this.reset()),n):this.error("Invalid expiration time"),{success:!0,token:this.#s}}catch(e){throw this.error(e.message),e}}finally{this.#o=!1}}async solveChallenges(e){const t=e.length;let r=0;const s=Array(this.#r).fill(null).map((()=>new Worker(this.#e))),i=([e,i],a)=>new Promise(((n,o)=>{const c=s[a],d=setTimeout((()=>{c.terminate(),s[a]=new Worker(this.#e),o(new Error("Worker timeout"))}),3e4);c.onmessage=({data:s})=>{s.found&&(clearTimeout(d),r++,this.dispatchEvent("progress",{progress:Math.round(r/t*100)}),n([e,i,s.nonce]))},c.onerror=e=>{clearTimeout(d),this.error(`Error in worker: ${e}`),o(e)},c.postMessage({salt:e,target:i})})),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)=>i(e,t))));a.push(...s)}}finally{s.forEach((e=>e.terminate()))}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.#a.classList.add("captcha"),this.#a.setAttribute("role","button"),this.#a.setAttribute("tabindex","0"),this.#a.setAttribute("disabled","true"),this.#a.innerHTML='<div class="checkbox"></div><p>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.#i.innerHTML='<style>.captcha{background-color:var(--cap-background);border:1px solid var(--cap-border-color);border-radius:var(--cap-border-radius);width:var(--cap-widget-width);display:flex;align-items:center;padding:var(--cap-widget-padding);gap:var(--cap-gap);cursor:pointer;transition:filter var(--cap-transition-duration),transform var(--cap-transition-duration);position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color)}.captcha:hover{filter:var(--cap-hover-filter)}.captcha:not([disabled]):active{transform:scale(var(--cap-active-scale))}.checkbox{width:var(--cap-checkbox-size);height:var(--cap-checkbox-size);border:var(--cap-checkbox-border);border-radius:var(--cap-checkbox-border-radius);background-color:var(--cap-checkbox-background);transition:opacity var(--cap-transition-duration);margin-top:var(--cap-checkbox-margin);margin-bottom:var(--cap-checkbox-margin)}.captcha *{font-family:var(--cap-font)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity var(--cap-transition-duration)}.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) 0%, var(--cap-spinner-color) var(--progress, 0%), var(--cap-spinner-background-color) var(--progress, 0%), var(--cap-spinner-background-color) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background);width: calc(100% - var(--cap-spinner-thickness));height: calc(100% - var(--cap-spinner-thickness));border-radius: 50%;margin:calc(var(--cap-spinner-thickness) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;background-image:var(--cap-checkmark);background-size:cover}.captcha[data-state=error] .checkbox{border:1px solid transparent;background-image:var(--cap-error-cross);background-size:cover}.captcha[disabled]{\ncursor: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);color:var(--cap-color);opacity:var(--cap-opacity-hover)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#i.appendChild(this.#a)}addEventListeners(){this.#a.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#a.addEventListener("click",(()=>{this.#a.hasAttribute("disabled")||this.solve()})),this.#a.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#a.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.#a.setAttribute("data-state",e),this.#a.querySelector("p").innerText=t,r?this.#a.setAttribute("disabled","true"):this.#a.removeAttribute("disabled")}handleProgress(e){const t=this.#a.querySelector("p");t&&(this.#a.querySelector(".checkbox").style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`Verifying... ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done","You're a human",!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error","Error. Try again."),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("","I'm a human"),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);if(!r)return;new Function("event",r).call(this,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.#s=null,this.querySelector("input[name='cap-token']")&&(this.querySelector("input[name='cap-token']").value="")}get token(){return this.#s}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.#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.getToken(),configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}const a=new CSSStyleSheet;a.replaceSync('html{--cap-font:system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif;--cap-color:#212121;--cap-background:#fdfdfd;--cap-border-color:#dddddd8f;--cap-border-radius:14px;--cap-checkbox-border:1px solid #aaaaaad1;--cap-checkbox-border-radius:6px;--cap-checkbox-background:#fafafa91;--cap-widget-width:240px;--cap-widget-padding:14px;--cap-checkbox-size:24px;--cap-checkbox-margin:2px;--cap-transition-duration:0.2s;--cap-gap:15px;--cap-opacity-hover:0.8;--cap-hover-filter:brightness(97%);--cap-active-scale:0.98;--cap-credits-font-size:12px;--cap-spinner-color:black;--cap-spinner-background-color:#eee;--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");--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");--cap-spinner-thickness:5px;}'),document.adoptedStyleSheets.push(a);const n=function(){let e;self.onmessage=async({data:{salt:t,target:r}})=>{let s=0;const i=5e4;let a=0;const n=new TextEncoder;if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly.instantiate){console.log("[cap] WASM not enabled, falling back to crypto.subtle\nThis is significanty slower than the WASM implementation");const e=new Uint8Array(r.length/2);for(let t=0;t<e.length;t++)e[t]=parseInt(r.substring(2*t,2*t+2),16);const o=e.length;for(;;)try{for(let r=0;r<i;r++){const r=t+s,i=n.encode(r),a=await crypto.subtle.digest("SHA-256",i),c=new Uint8Array(a,0,o);let d=!0;for(let t=0;t<o;t++)if(c[t]!==e[t]){d=!1;break}if(d)return void self.postMessage({nonce:s,found:!0});s++}a+=i}catch(e){return console.error("[cap] fallback worker error",e),void self.postMessage({found:!1,error:e.message})}}e||(e=await hashwasm.createSHA256());const o=new Uint8Array(128);for(;;)try{for(let a=0;a<i;a++){const i=t+s.toString(),a=n.encode(i);o.set(a),e.init(),e.update(o.subarray(0,a.length));if(e.digest("hex").startsWith(r))return void self.postMessage({nonce:s,found:!0});s++}a+=i}catch(e){return void self.postMessage({found:!1,error:e.message})}}};setTimeout((async function(){e=await(await t("https://cdn.jsdelivr.net/npm/@cap.js/widget/wasm-hashes.min.js")).text()+n.toString().replace(/^function\s*\([^\)]*\)\s*{|\}$/g,"").trim()}),1),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
+ "use strict";function _getRequireWildcardCache(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,r=new WeakMap;return(_getRequireWildcardCache=function(e){return e?r:t})(e)}function _interopRequireWildcard(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var r=_getRequireWildcardCache(t);if(r&&r.has(e))return r.get(e);var s={__proto__:null},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&{}.hasOwnProperty.call(e,i)){var n=a?Object.getOwnPropertyDescriptor(e,i):null;n&&(n.get||n.set)?Object.defineProperty(s,i,n):s[i]=e[i]}return s.default=e,r&&r.set(e,s),s}!function(){let e;const t=function(){return window?.CAP_CUSTOM_FETCH?window.CAP_CUSTOM_FETCH(...arguments):fetch(...arguments)};class r extends HTMLElement{#e="";#t=null;#r=navigator.hardwareConcurrency||8;token=null;#s;#a;#i;#n=!1;#o;static get observedAttributes(){return["onsolve","onprogress","onreset","onerror","workers","[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)}}}async connectedCallback(){this.#i=this,this.#s=this.attachShadow({mode:"open"}),this.#a=document.createElement("div"),this.createUI(),this.addEventListeners(),await this.initialize(),this.#a.removeAttribute("disabled");const e=this.getAttribute("data-cap-worker-count");this.setWorkersCount(parseInt(e)?parseInt(e,10):navigator.hardwareConcurrency||8);const t=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.#i.innerHTML=`<input type="hidden" name="${t}">`}async solve(){if(!this.#n)try{this.#n=!0,this.updateUI("verifying","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(),a=await this.solveChallenges(r),i=await(await t(`${e}redeem`,{method:"POST",body:JSON.stringify({token:s,solutions:a}),headers:{"Content-Type":"application/json"}})).json();if(this.dispatchEvent("progress",{progress:100}),!i.success)throw new Error("Invalid solution");const n=this.getAttribute("data-cap-hidden-field-name")||"cap-token";this.querySelector(`input[name='${n}']`)&&(this.querySelector(`input[name='${n}']`).value=i.token),this.dispatchEvent("solve",{token:i.token}),this.token=i.token,this.#t&&clearTimeout(this.#t);const o=new Date(i.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.#n=!1}}async solveChallenges(e){const t=e.length;let r=0;const s=Array(this.#r).fill(null).map((()=>new Worker(this.#e))),a=([e,a],i)=>new Promise(((n,o)=>{const c=s[i],d=setTimeout((()=>{c.terminate(),s[i]=new Worker(this.#e),o(new Error("Worker timeout"))}),3e4);c.onmessage=({data:s})=>{s.found&&(clearTimeout(d),r++,this.dispatchEvent("progress",{progress:Math.round(r/t*100)}),n([e,a,s.nonce]))},c.onerror=e=>{clearTimeout(d),this.error(`Error in worker: ${e}`),o(e)},c.postMessage({salt:e,target:a})})),i=[];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)=>a(e,t))));i.push(...s)}}finally{s.forEach((e=>e.terminate()))}return i}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.#a.classList.add("captcha"),this.#a.setAttribute("role","button"),this.#a.setAttribute("tabindex","0"),this.#a.setAttribute("disabled","true"),this.#a.innerHTML='<div class="checkbox"></div><p>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{background-color:var(--cap-background);border:1px solid var(--cap-border-color);border-radius:var(--cap-border-radius);width:var(--cap-widget-width);display:flex;align-items:center;padding:var(--cap-widget-padding);gap:var(--cap-gap);cursor:pointer;transition:filter var(--cap-transition-duration),transform var(--cap-transition-duration);position:relative;-webkit-tap-highlight-color:rgba(255,255,255,0);overflow:hidden;color:var(--cap-color)}.captcha:hover{filter:var(--cap-hover-filter)}.captcha:not([disabled]):active{transform:scale(var(--cap-active-scale))}.checkbox{width:var(--cap-checkbox-size);height:var(--cap-checkbox-size);border:var(--cap-checkbox-border);border-radius:var(--cap-checkbox-border-radius);background-color:var(--cap-checkbox-background);transition:opacity var(--cap-transition-duration);margin-top:var(--cap-checkbox-margin);margin-bottom:var(--cap-checkbox-margin)}.captcha *{font-family:var(--cap-font)}.captcha p{margin:0;font-weight:500;font-size:15px;user-select:none;transition:opacity var(--cap-transition-duration)}.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) 0%, var(--cap-spinner-color) var(--progress, 0%), var(--cap-spinner-background-color) var(--progress, 0%), var(--cap-spinner-background-color) 100%);position: relative;}.captcha[data-state=verifying] .checkbox::after {content: "";background-color: var(--cap-background);width: calc(100% - var(--cap-spinner-thickness));height: calc(100% - var(--cap-spinner-thickness));border-radius: 50%;margin:calc(var(--cap-spinner-thickness) / 2)}.captcha[data-state=done] .checkbox{border:1px solid transparent;background-image:var(--cap-checkmark);background-size:cover}.captcha[data-state=error] .checkbox{border:1px solid transparent;background-image:var(--cap-error-cross);background-size:cover}.captcha[disabled]{\ncursor: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);color:var(--cap-color);opacity:var(--cap-opacity-hover)}.captcha .credits span{display:none;text-decoration:underline}.captcha .credits:hover span{display:inline-block}</style>',this.#s.appendChild(this.#a)}addEventListeners(){this.#a.querySelector("a").addEventListener("click",(e=>{e.stopPropagation(),e.preventDefault(),window.open("https://capjs.js.org","_blank")})),this.#a.addEventListener("click",(()=>{this.#a.hasAttribute("disabled")||this.solve()})),this.#a.addEventListener("keydown",(e=>{"Enter"!==e.key&&" "!==e.key||this.#a.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.#a.setAttribute("data-state",e),this.#a.querySelector("p").innerText=t,r?this.#a.setAttribute("disabled","true"):this.#a.removeAttribute("disabled")}handleProgress(e){const t=this.#a.querySelector("p");t&&(this.#a.querySelector(".checkbox").style.setProperty("--progress",`${e.detail.progress}%`),t.innerText=`Verifying... ${e.detail.progress}%`),this.executeAttributeCode("onprogress",e)}handleSolve(e){this.updateUI("done","You're a human",!0),this.executeAttributeCode("onsolve",e)}handleError(e){this.updateUI("error","Error. Try again."),this.executeAttributeCode("onerror",e)}handleReset(e){this.updateUI("","I'm a human"),this.executeAttributeCode("onreset",e)}executeAttributeCode(e,t){const r=this.getAttribute(e);if(!r)return;new Function("event",r).call(this,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 token(){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.getToken(),configurable:!0,enumerable:!0}),t||(r.style.display="none",document.documentElement.appendChild(r))}}const a=new CSSStyleSheet;a.replaceSync('html{--cap-font:system,-apple-system,"BlinkMacSystemFont",".SFNSText-Regular","San Francisco","Roboto","Segoe UI","Helvetica Neue","Lucida Grande","Ubuntu","arial",sans-serif;--cap-color:#212121;--cap-background:#fdfdfd;--cap-border-color:#dddddd8f;--cap-border-radius:14px;--cap-checkbox-border:1px solid #aaaaaad1;--cap-checkbox-border-radius:6px;--cap-checkbox-background:#fafafa91;--cap-widget-width:240px;--cap-widget-padding:14px;--cap-checkbox-size:24px;--cap-checkbox-margin:2px;--cap-transition-duration:0.2s;--cap-gap:15px;--cap-opacity-hover:0.8;--cap-hover-filter:brightness(97%);--cap-active-scale:0.98;--cap-credits-font-size:12px;--cap-spinner-color:black;--cap-spinner-background-color:#eee;--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");--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");--cap-spinner-thickness:5px;}'),document.adoptedStyleSheets.push(a);e=`(() => {${function(){let e,t;if("object"!=typeof WebAssembly||"function"!=typeof WebAssembly?.instantiate)return self.onmessage=async({data:{salt:e,target:t}})=>{let r=0;let s=0;const a=new TextEncoder,i=new Uint8Array(t.length/2);for(let e=0;e<i.length;e++)i[e]=parseInt(t.substring(2*e,2*e+2),16);const n=i.length;for(;;)try{for(let t=0;t<5e4;t++){const t=e+r,s=a.encode(t),o=await crypto.subtle.digest("SHA-256",s),c=new Uint8Array(o,0,n);let d=!0;for(let e=0;e<n;e++)if(c[e]!==i[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.");e=Promise.resolve().then((()=>_interopRequireWildcard(require("https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.3/browser/cap_wasm.min.js")))).then((e=>e.default().then((r=>{t=(r&&r.exports?r.exports:e).solve_pow})))).catch((e=>{useFallback=!0,console.error("[cap] using fallback solver due to error:",e)})),self.onmessage=async({data:{salt:r,target:s}})=>{try{await e;const a=performance.now(),i=t(r,s),n=performance.now();self.postMessage({nonce:Number(i),found:!0,durationMs:(n-a).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=s,customElements.get("cap-widget")?console.warn("The cap-widget element has already been defined. Skipping re-defining it."):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.0.22",
3
+ "version": "0.1.1",
4
4
  "description": "Client-side widget for Cap. Cap is a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 PoW.",
5
5
  "keywords": [
6
6
  "account security",
package/src/cap.js CHANGED
@@ -8,30 +8,12 @@
8
8
  return fetch(...arguments);
9
9
  };
10
10
 
11
- const until = (predFn, timeout = 10000) => {
12
- return new Promise((resolve, reject) => {
13
- const timeoutId = setTimeout(() => {
14
- reject(new Error("Initialize timeout"));
15
- }, timeout);
16
-
17
- const poll = () => {
18
- if (predFn()) {
19
- clearTimeout(timeoutId);
20
- resolve();
21
- } else {
22
- setTimeout(poll, 500);
23
- }
24
- };
25
- poll();
26
- });
27
- };
28
-
29
11
  // MARK: Widget
30
12
  class CapWidget extends HTMLElement {
31
13
  #workerUrl = "";
32
14
  #resetTimer = null;
33
15
  #workersCount = navigator.hardwareConcurrency || 8;
34
- #token = null;
16
+ token = null;
35
17
  #shadow;
36
18
  #div;
37
19
  #host;
@@ -39,7 +21,14 @@
39
21
  #eventHandlers;
40
22
 
41
23
  static get observedAttributes() {
42
- return ["onsolve", "onprogress", "onreset", "onerror", "workers"];
24
+ return [
25
+ "onsolve",
26
+ "onprogress",
27
+ "onreset",
28
+ "onerror",
29
+ "workers",
30
+ "[cap]",
31
+ ];
43
32
  }
44
33
 
45
34
  constructor() {
@@ -57,25 +46,15 @@
57
46
  this.boundHandleReset = this.handleReset.bind(this);
58
47
  }
59
48
 
60
- async initialize() {
61
- if (this.#workerUrl) {
62
- URL.revokeObjectURL(this.#workerUrl);
63
- }
64
-
65
- try {
66
- await until(() => !!workerScript);
67
- this.#workerUrl = URL.createObjectURL(
68
- new Blob([workerScript], {
69
- type: "application/javascript",
70
- })
71
- );
72
- } catch (err) {
73
- this.error("Failed to initialize worker");
74
- throw err;
75
- }
49
+ initialize() {
50
+ this.#workerUrl = URL.createObjectURL(
51
+ new Blob([workerScript], {
52
+ type: "application/javascript",
53
+ })
54
+ );
76
55
  }
77
56
 
78
- attributeChangedCallback(name, oldValue, newValue) {
57
+ attributeChangedCallback(name, _, value) {
79
58
  if (name.startsWith("on")) {
80
59
  const eventName = name.slice(2);
81
60
  const oldHandler = this.#eventHandlers.get(name);
@@ -83,7 +62,7 @@
83
62
  this.removeEventListener(eventName, oldHandler);
84
63
  }
85
64
 
86
- if (newValue) {
65
+ if (value) {
87
66
  const handler = (event) => {
88
67
  const callback = this.getAttribute(name);
89
68
  if (typeof window[callback] === "function") {
@@ -111,7 +90,8 @@
111
90
  ? parseInt(workers, 10)
112
91
  : navigator.hardwareConcurrency || 8
113
92
  );
114
- this.#host.innerHTML = `<input type="hidden" name="cap-token">`;
93
+ const fieldName = this.getAttribute("data-cap-hidden-field-name") || "cap-token";
94
+ this.#host.innerHTML = `<input type="hidden" name="${fieldName}">`;
115
95
  }
116
96
 
117
97
  async solve() {
@@ -123,7 +103,6 @@
123
103
  this.#solving = true;
124
104
  this.updateUI("verifying", "Verifying...", true);
125
105
 
126
- await until(() => !!this.#workerUrl);
127
106
  this.dispatchEvent("progress", { progress: 0 });
128
107
 
129
108
  try {
@@ -148,12 +127,13 @@
148
127
  this.dispatchEvent("progress", { progress: 100 });
149
128
 
150
129
  if (!resp.success) throw new Error("Invalid solution");
151
- if (this.querySelector("input[name='cap-token']")) {
152
- this.querySelector("input[name='cap-token']").value = resp.token;
130
+ const fieldName = this.getAttribute("data-cap-hidden-field-name") || "cap-token";
131
+ if (this.querySelector(`input[name='${fieldName}']`)) {
132
+ this.querySelector(`input[name='${fieldName}']`).value = resp.token;
153
133
  }
154
134
 
155
135
  this.dispatchEvent("solve", { token: resp.token });
156
- this.#token = resp.token;
136
+ this.token = resp.token;
157
137
 
158
138
  if (this.#resetTimer) clearTimeout(this.#resetTimer);
159
139
  const expiresIn = new Date(resp.expires).getTime() - Date.now();
@@ -163,7 +143,7 @@
163
143
  this.error("Invalid expiration time");
164
144
  }
165
145
 
166
- return { success: true, token: this.#token };
146
+ return { success: true, token: this.token };
167
147
  } catch (err) {
168
148
  this.error(err.message);
169
149
  throw err;
@@ -324,7 +304,7 @@
324
304
  }
325
305
 
326
306
  error(message = "Unknown error") {
327
- console.error("[Cap] Error:", message);
307
+ console.error("[cap] Error:", message);
328
308
  this.dispatchEvent("error", { isCap: true, message });
329
309
  }
330
310
 
@@ -343,14 +323,15 @@
343
323
  this.#resetTimer = null;
344
324
  }
345
325
  this.dispatchEvent("reset");
346
- this.#token = null;
347
- if (this.querySelector("input[name='cap-token']")) {
348
- this.querySelector("input[name='cap-token']").value = "";
326
+ this.token = null;
327
+ const fieldName = this.getAttribute("data-cap-hidden-field-name") || "cap-token";
328
+ if (this.querySelector(`input[name='${fieldName}']`)) {
329
+ this.querySelector(`input[name='${fieldName}']`).value = "";
349
330
  }
350
331
  }
351
332
 
352
333
  get token() {
353
- return this.#token;
334
+ return this.token;
354
335
  }
355
336
 
356
337
  disconnectedCallback() {
@@ -425,26 +406,20 @@
425
406
  );
426
407
  document.adoptedStyleSheets.push(sheet);
427
408
 
428
- // MARK: Solver worker
429
409
  const workerFunct = function () {
430
- let hasher;
410
+ let initPromise, solve_pow_function;
431
411
 
432
- self.onmessage = async ({ data: { salt, target } }) => {
433
- let nonce = 0;
434
- const batchSize = 50000;
435
- let processed = 0;
436
- const encoder = new TextEncoder();
437
-
438
- // Alternative solver in case WASM is not available
439
- if (
440
- !(
441
- typeof WebAssembly === "object" &&
442
- typeof WebAssembly.instantiate === "function"
443
- )
444
- ) {
445
- console.log(
446
- "[cap] WASM not enabled, falling back to crypto.subtle\nThis is significanty slower than the WASM implementation"
447
- );
412
+ if (
413
+ typeof WebAssembly !== "object" ||
414
+ typeof WebAssembly?.instantiate !== "function"
415
+ ) {
416
+ self.onmessage = async ({ data: { salt, target } }) => {
417
+ // Fallback solver in case WASM is not available
418
+
419
+ let nonce = 0;
420
+ const batchSize = 50000;
421
+ let processed = 0;
422
+ const encoder = new TextEncoder();
448
423
 
449
424
  const targetBytes = new Uint8Array(target.length / 2);
450
425
  for (let k = 0; k < targetBytes.length; k++) {
@@ -495,54 +470,62 @@
495
470
  return;
496
471
  }
497
472
  }
498
- }
473
+ };
499
474
 
500
- if (!hasher) {
501
- hasher = await hashwasm.createSHA256();
502
- }
475
+ return console.warn(
476
+ "[cap] WebAssembly is not supported, falling back to alternative solver."
477
+ );
478
+ }
503
479
 
504
- const buffer = new Uint8Array(128);
480
+ initPromise = import(
481
+ "https://cdn.jsdelivr.net/npm/@cap.js/wasm@0.0.3/browser/cap_wasm.min.js"
482
+ )
483
+ .then((wasmModule) => {
484
+ return wasmModule.default().then((instance) => {
485
+ solve_pow_function = (
486
+ instance && instance.exports ? instance.exports : wasmModule
487
+ ).solve_pow;
488
+ });
489
+ })
490
+ .catch((e) => {
491
+ useFallback = true;
492
+ console.error("[cap] using fallback solver due to error:", e);
493
+ });
505
494
 
506
- while (true) {
507
- try {
508
- for (let i = 0; i < batchSize; i++) {
509
- const input = salt + nonce.toString();
510
- const inputBytes = encoder.encode(input);
511
- buffer.set(inputBytes);
512
-
513
- hasher.init();
514
- hasher.update(buffer.subarray(0, inputBytes.length));
515
- const hash = hasher.digest("hex");
516
-
517
- if (hash.startsWith(target)) {
518
- self.postMessage({ nonce, found: true });
519
- return;
520
- }
495
+ self.onmessage = async ({ data: { salt, target } }) => {
496
+ try {
497
+ await initPromise;
521
498
 
522
- nonce++;
523
- }
499
+ const startTime = performance.now();
500
+ const nonce = solve_pow_function(salt, target);
501
+ const endTime = performance.now();
524
502
 
525
- processed += batchSize;
526
- } catch (error) {
527
- self.postMessage({ found: false, error: error.message });
528
- return;
529
- }
503
+ self.postMessage({
504
+ nonce: Number(nonce),
505
+ found: true,
506
+ durationMs: (endTime - startTime).toFixed(2),
507
+ });
508
+ } catch (error) {
509
+ console.error("[cap] solver error", error);
510
+ self.postMessage({
511
+ found: false,
512
+ error: error.message || String(error),
513
+ });
530
514
  }
531
515
  };
516
+
517
+ self.onerror = (error) => {
518
+ self.postMessage({
519
+ found: false,
520
+ error: `Worker error: ${error.message || error}`,
521
+ });
522
+ };
532
523
  };
533
524
 
534
- setTimeout(async function () {
535
- workerScript =
536
- (await (
537
- await capFetch(
538
- "https://cdn.jsdelivr.net/npm/@cap.js/widget/wasm-hashes.min.js"
539
- )
540
- ).text()) +
541
- workerFunct
542
- .toString()
543
- .replace(/^function\s*\([^\)]*\)\s*{|\}$/g, "")
544
- .trim();
545
- }, 1);
525
+ workerScript = `(() => {${workerFunct
526
+ .toString()
527
+ .replace(/^function\s*\([^\)]*\)\s*{|\}$/g, "")
528
+ .trim()}})()`;
546
529
 
547
530
  window.Cap = Cap;
548
531