Install
npm / Wrangler
Install the package:
1
npm i @botbye/cloudflare-worker
Requires @cloudflare/workers-types >= 4 as a peer dependency.
Dashboard
Paste the pre-built bundle directly into your worker's index.js. No build step required — the SDK is available as BotBye.init, BotBye.evaluate, etc.:
1
var BotBye=(()=>{var _r=Object.create;var D=Object.defineProperty;var Or=Object.getOwnPropertyDescriptor;var Rr=Object.getOwnPropertyNames;var Nr=Object.getPrototypeOf,Tr=Object.prototype.hasOwnProperty;var o=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports),gr=(e,r)=>{for(var t in r)D(e,t,{get:r[t],enumerable:!0})},ge=(e,r,t,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of Rr(r))!Tr.call(e,a)&&a!==t&&D(e,a,{get:()=>r[a],enumerable:!(i=Or(r,a))||i.enumerable});return e};var pe=(e,r,t)=>(t=e!=null?_r(Nr(e)):{},ge(r||!e||!e.__esModule?D(t,"default",{value:e,enumerable:!0}):t,e)),pr=e=>ge(D({},"__esModule",{value:!0}),e);var v=o(l=>{"use strict";Object.defineProperty(l,"__esModule",{value:!0});l.once=l.getIpFromHeaders=l.extractMessageFromError=l.isObject=l.isNumber=l.withTimeout=l.isKeyExist=l.toString=l.isNotNil=l.isPlainObject=l.isString=void 0;var ye=function(e,r){return typeof e===r},Lr=function(e){return e==null},yr=function(e){return!Lr(e)};l.isNotNil=yr;var se=function(e){return ye(e,"string")};l.isString=se;var hr=function(e){return ye(e,"number")&&!Number.isNaN(e)};l.isNumber=hr;var le=function(e){return!(typeof e!="object"||e===null)};l.isObject=le;var Ir=function(e){if(typeof e!="object"||e===null)return!1;var r=Object.getPrototypeOf(e);return r===Object.prototype||r===null};l.isPlainObject=Ir;var Sr=function(e){return se(e)?e:String(e)};l.toString=Sr;var Ar=function(e,r){return le(e)&&r in e};l.isKeyExist=Ar;var Cr=function(e,r,t){var i=new Promise(function(a,n){return setTimeout(function(){n(t)},r)});return Promise.race([e,i])};l.withTimeout=Cr;var mr=function(e){var r="Unknown error";return e instanceof Error&&(r=e.message),r};l.extractMessageFromError=mr;var Le=function(e){var r=Array.isArray(e)?e[0]:e;if(!se(r))return null;var t=r.split(",")[0].trim();return t||null},Ur=function(e){var r;return le(e)?(r=Le(e["x-forwarded-for"]))!==null&&r!==void 0?r:Le(e["x-real-ip"]):null};l.getIpFromHeaders=Ur;var qr=function(e){var r=!1;return(function(){for(var t=[],i=0;i<arguments.length;i++)t[i]=arguments[i];r||(r=!0,e.apply(void 0,t))})};l.once=qr});var ve=o(k=>{"use strict";Object.defineProperty(k,"__esModule",{value:!0});k.getModuleHeaders=void 0;var Fr=function(e){var r;return r={},r["Module-Name"]=e.module.name,r["Module-Version"]=e.module.version,r};k.getModuleHeaders=Fr});var Ie=o(H=>{"use strict";Object.defineProperty(H,"__esModule",{value:!0});H.sendInitCall=void 0;var br=v(),Vr=ve(),he="Invalid JSON in the server response",Pr=function(e){var r=e.global,t=r.httpClient.call(r.url+"/init-request/v1",{method:"POST",headers:(0,Vr.getModuleHeaders)(r),body:{serverKey:r.serverKey}}),i=t.result,a=t.abort,n=i.then(function(u){var s={error:"Unknown error"};try{s=JSON.parse(u)}catch{throw new Error(he)}if(!(0,br.isPlainObject)(s))throw new Error(he);if("status"in s&&s.status==="ok")return"";if("error"in s)throw new Error(s.error);return""});return{result:n,abort:a}};H.sendInitCall=Pr});var Se=o(L=>{"use strict";var B=L&&L.__assign||function(){return B=Object.assign||function(e){for(var r,t=1,i=arguments.length;t<i;t++){r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},B.apply(this,arguments)};Object.defineProperty(L,"__esModule",{value:!0});L.sendEvaluateCall=void 0;var Mr=ve(),jr="/api/v1/protect/evaluate",wr=function(e){var r,t=e.global,i=e.event,a="".concat(t.url).concat(jr);try{var n=new URL("".concat(a,"?").concat((r=i.request.token)!==null&&r!==void 0?r:""));a=n.href}catch{}return t.httpClient.call(a,{method:"POST",headers:(0,Mr.getModuleHeaders)(t),body:B(B({},i),{server_key:t.serverKey})})};L.sendEvaluateCall=wr});var ce=o(y=>{"use strict";Object.defineProperty(y,"__esModule",{value:!0});y.EVENT_INFO_EVENT_STATUSES=y.EVENT_VALIDATION_TYPE=void 0;var Kr=["SUCCESSFUL","FAILED","ATTEMPTED","UNKNOWN"];y.EVENT_INFO_EVENT_STATUSES=Kr;var Dr=["validate","risk","full"];y.EVENT_VALIDATION_TYPE=Dr});var h=o(E=>{"use strict";Object.defineProperty(E,"__esModule",{value:!0});E.fieldRequiredLog=E.sanitizeIfPlainObject=E.sanitizeIfTruthy=E.sanitizeIfNotNil=E.sanitize=void 0;var Ee=v(),Ae=function(e,r,t,i,a,n,u){if((0,Ee.isKeyExist)(e,t)){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitize=Ae;var kr=function(e,r,t,i,a,n,u){if((0,Ee.isNotNil)(e[t])){Ae(e,r,t,i,a,n,u);return}u&&(r[i]=u)};E.sanitizeIfNotNil=kr;var Hr=function(e,r,t,i,a,n,u){if(e[t]){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitizeIfTruthy=Hr;var Br=function(e,r,t,i,a,n,u){if((0,Ee.isPlainObject)(e[t])){r[i]=a(e[t],n);return}u&&(r[i]=u)};E.sanitizeIfPlainObject=Br;var Wr=function(e,r,t,i){e[t]||r.warn("Field ".concat(t," is required in ").concat(i))};E.fieldRequiredLog=Wr});var qe=o(R=>{"use strict";Object.defineProperty(R,"__esModule",{value:!0});R.adaptRequestInfoWithOptionalHeaders=R.adaptRequestInfoWithRequiredHeaders=R.FALLBACK_EVENT_REQUEST_INFO=void 0;var g=v(),T=h(),Ce="0.0.0.0",me={},zr={ip:Ce,headers:me};R.FALLBACK_EVENT_REQUEST_INFO=zr;var xr=function(e){if(!(0,g.isPlainObject)(e))return me;for(var r={},t=0,i=Object.keys(e);t<i.length;t++){var a=i[t];r[a]=(0,g.toString)(e[a])}return r},Ue=function(e){return function(r,t){var i=(0,g.isPlainObject)(r)?r:{},a={};return(0,T.fieldRequiredLog)(i,t.logger,"ip","evaluateEvent.request"),e&&(0,T.fieldRequiredLog)(i,t.logger,"headers","evaluateEvent.request"),(0,T.sanitizeIfTruthy)(i,a,"ip","ip",g.toString,t,Ce),(0,T.sanitizeIfNotNil)(i,a,"token","token",g.toString,t),(0,T.sanitizeIfPlainObject)(i,a,"headers","headers",xr,t,{}),(0,T.sanitizeIfNotNil)(i,a,"requestMethod","request_method",g.toString,t),(0,T.sanitizeIfNotNil)(i,a,"requestUri","request_uri",g.toString,t),a}},Gr=Ue(!0);R.adaptRequestInfoWithRequiredHeaders=Gr;var Qr=Ue(!1);R.adaptRequestInfoWithOptionalHeaders=Qr});var Ve=o(I=>{"use strict";Object.defineProperty(I,"__esModule",{value:!0});I.adaptEventInfo=I.FALLBACK_EVENT_EVENT_INFO=void 0;var de=v(),Fe=ce(),W=h(),fe="UNKNOWN",be="UNKNOWN",Jr={type:be,status:fe};I.FALLBACK_EVENT_EVENT_INFO=Jr;var Yr=function(e,r){var t=(0,de.toString)(e).trim().toUpperCase();return Fe.EVENT_INFO_EVENT_STATUSES.includes(t)?t:(r.logger.warn("Event status should be one of: ".concat(Fe.EVENT_INFO_EVENT_STATUSES.join(" || "))),fe)},Xr=function(e,r){var t=(0,de.isPlainObject)(e)?e:{},i={};return(0,W.fieldRequiredLog)(t,r.logger,"type","evaluateEvent.event"),(0,W.fieldRequiredLog)(t,r.logger,"status","evaluateEvent.event"),(0,W.sanitize)(t,i,"type","type",de.toString,r,be),(0,W.sanitizeIfTruthy)(t,i,"status","status",Yr,r,fe),i};I.adaptEventInfo=Xr});var Me=o(S=>{"use strict";Object.defineProperty(S,"__esModule",{value:!0});S.adaptUserInfo=S.FALLBACK_EVENT_USER_INFO=void 0;var F=v(),b=h(),Pe="00000000-0000-0000-0000-000000000000",Zr={account_id:Pe};S.FALLBACK_EVENT_USER_INFO=Zr;var $r=function(e,r){var t=(0,F.isPlainObject)(e)?e:{},i={};return(0,b.fieldRequiredLog)(t,r.logger,"accountId","evaluateEvent.user"),(0,b.sanitizeIfTruthy)(t,i,"accountId","account_id",F.toString,r,Pe),(0,b.sanitizeIfNotNil)(t,i,"username","username",F.toString,r),(0,b.sanitizeIfNotNil)(t,i,"email","email",F.toString,r),(0,b.sanitizeIfNotNil)(t,i,"phone","phone",F.toString,r),i};S.adaptUserInfo=$r});var we=o(A=>{"use strict";Object.defineProperty(A,"__esModule",{value:!0});A.adaptEvaluateConfig=A.FALLBACK_EVENT_EVALUATE_CONFIG=void 0;var et=v(),rt=h(),je={};A.FALLBACK_EVENT_EVALUATE_CONFIG=je;var tt=function(e){return!!e},it=function(e,r){if(!(0,et.isPlainObject)(e))return je;var t={};return(0,rt.sanitizeIfNotNil)(e,t,"bypassBotValidation","bypass_bot_validation",tt,r),t};A.adaptEvaluateConfig=it});var De=o(z=>{"use strict";Object.defineProperty(z,"__esModule",{value:!0});z.adaptCustomFields=void 0;var Ke=v(),at={},nt=function(e,r){if(!(0,Ke.isPlainObject)(e))return at;for(var t={},i=0,a=Object.keys(e);i<a.length;i++){var n=a[i];t[n]=(0,Ke.toString)(e[n])}return t};z.adaptCustomFields=nt});var ze=o(G=>{"use strict";Object.defineProperty(G,"__esModule",{value:!0});G.adaptEvent=void 0;var _e=v(),ke=ce(),c=h(),V=qe(),He=Ve(),Be=Me(),x=we(),We=De(),ut=function(e,r){var t=(0,_e.isPlainObject)(e)?e:{},i={},a=String(t.type).trim().toLowerCase();if(ke.EVENT_VALIDATION_TYPE.includes(a)||(r.logger.warn("Event type should be one of: ".concat(ke.EVENT_VALIDATION_TYPE.join(" || "))),a="validate"),i.type=a,a==="validate"&&((0,c.fieldRequiredLog)(t,r.logger,"request","validate event"),(0,c.sanitize)(t,i,"request","request",V.adaptRequestInfoWithRequiredHeaders,r,V.FALLBACK_EVENT_REQUEST_INFO),(0,c.sanitizeIfNotNil)(t,i,"config","config",x.adaptEvaluateConfig,r),(0,c.sanitizeIfNotNil)(t,i,"customFields","custom_fields",We.adaptCustomFields,r)),a==="full"||a==="risk"){(0,c.fieldRequiredLog)(t,r.logger,"request","".concat(a," event")),(0,c.fieldRequiredLog)(t,r.logger,"event","".concat(a," event")),(0,c.fieldRequiredLog)(t,r.logger,"user","".concat(a," event"));var n=a==="risk"?V.adaptRequestInfoWithOptionalHeaders:V.adaptRequestInfoWithRequiredHeaders;(0,c.sanitize)(t,i,"request","request",n,r,V.FALLBACK_EVENT_REQUEST_INFO),(0,c.sanitize)(t,i,"event","event",He.adaptEventInfo,r,He.FALLBACK_EVENT_EVENT_INFO),(0,c.sanitize)(t,i,"user","user",Be.adaptUserInfo,r,Be.FALLBACK_EVENT_USER_INFO),(0,c.sanitizeIfNotNil)(t,i,"customFields","custom_fields",We.adaptCustomFields,r)}if(a==="risk"){(0,c.sanitize)(t,i,"config","config",x.adaptEvaluateConfig,r,x.FALLBACK_EVENT_EVALUATE_CONFIG);var u=i.config;(0,_e.isKeyExist)(u,"bypass_bot_validation")||(u.bypass_bot_validation=!0),(0,c.sanitizeIfNotNil)(t,i,"botbyeResult","botbye_result",_e.toString,r)}return a==="full"&&(0,c.sanitizeIfNotNil)(t,i,"config","config",x.adaptEvaluateConfig,r),i};G.adaptEvent=ut});var P=o(N=>{"use strict";Object.defineProperty(N,"__esModule",{value:!0});N.REQUEST_TIMEOUT_ERROR_CODE=N.BASE_REQUEST_TIMEOUT=N.REQUEST_ERROR_CODE=void 0;var ot="REQUEST_ERROR";N.REQUEST_ERROR_CODE=ot;var st="REQUEST_TIMEOUT";N.REQUEST_TIMEOUT_ERROR_CODE=st;var lt=1500;N.BASE_REQUEST_TIMEOUT=lt});var xe=o(Q=>{"use strict";Object.defineProperty(Q,"__esModule",{value:!0});Q.httpClientCallWithTimeout=void 0;var vt=v(),Oe=P(),ct=function(e,r,t,i){t===void 0&&(t=Oe.BASE_REQUEST_TIMEOUT);var a=(0,vt.withTimeout)(e.result,t,Oe.REQUEST_TIMEOUT_ERROR_CODE).catch(function(n){var u;throw n===Oe.REQUEST_TIMEOUT_ERROR_CODE&&(r.logger.debug("The request ".concat((u=i?.name)!==null&&u!==void 0?u:""," was interrupted by a timeout")),e.abort()),n});return{result:a,abort:e.abort}};Q.httpClientCallWithTimeout=ct});var Ge=o(J=>{"use strict";Object.defineProperty(J,"__esModule",{value:!0});J.EVALUATE_DECISIONS=void 0;var Et=["ALLOW","BLOCK","CHALLENGE"];J.EVALUATE_DECISIONS=Et});var M=o(d=>{"use strict";Object.defineProperty(d,"__esModule",{value:!0});d.SDK_ERROR=d.JSON_ERROR=d.CONNECTION_ERROR=d.TIMEOUT_ERROR=d.UNKNOWN_ERROR=void 0;var dt="unknown error";d.UNKNOWN_ERROR=dt;var ft="timeout";d.TIMEOUT_ERROR=ft;var _t="connection error";d.CONNECTION_ERROR=_t;var Ot="invalid json response";d.JSON_ERROR=Ot;var Rt="SDK error";d.SDK_ERROR=Rt});var j=o(Y=>{"use strict";Object.defineProperty(Y,"__esModule",{value:!0});Y.createFallbackEvaluationResult=void 0;var Nt=function(e){return{decision:"ALLOW",config:{bypass_bot_validation:!0},error:{message:e}}};Y.createFallbackEvaluationResult=Nt});var Je=o(X=>{"use strict";Object.defineProperty(X,"__esModule",{value:!0});X.withApiErrorHandle=void 0;var Qe=P(),p=v(),Tt=Ge(),C=M(),m=j(),gt=function(e){return!(!(0,p.isPlainObject)(e)||!("request_id"in e)||!(0,p.isString)(e.request_id)||!("decision"in e)||!(0,p.isString)(e.decision)||!Tt.EVALUATE_DECISIONS.includes(e.decision)||!("risk_score"in e)||!(0,p.isNumber)(e.risk_score)||!("signals"in e)||!Array.isArray(e.signals)||!("scores"in e)||!(0,p.isPlainObject)(e.scores)||!("config"in e)||!(0,p.isPlainObject)(e.config))},pt=function(e){return(0,p.isString)(e)?e:e instanceof Error?e.message:"UNKNOWN"},Lt=function(e,r){return new Promise(function(t){try{e().result.then(function(i){var a=null;try{a=JSON.parse(i)}catch{t((0,m.createFallbackEvaluationResult)(C.JSON_ERROR));return}if(!gt(a)){t((0,m.createFallbackEvaluationResult)(C.JSON_ERROR));return}t(a)}).catch(function(i){var a=pt(i);if(a.includes(Qe.REQUEST_TIMEOUT_ERROR_CODE)){t((0,m.createFallbackEvaluationResult)(C.TIMEOUT_ERROR));return}if(a.includes(Qe.REQUEST_ERROR_CODE)){t((0,m.createFallbackEvaluationResult)(C.CONNECTION_ERROR));return}t((0,m.createFallbackEvaluationResult)(C.UNKNOWN_ERROR))})}catch{t((0,m.createFallbackEvaluationResult)(C.SDK_ERROR))}})};X.withApiErrorHandle=Lt});var Ye=o(Z=>{"use strict";Object.defineProperty(Z,"__esModule",{value:!0});Z.evaluateFactory=void 0;var yt=Se(),ht=ze(),It=xe(),St=P(),At=v(),Ct=Je(),mt=j(),Ut=M(),qt=function(e){return function(r){return e.serverKey?(0,Ct.withApiErrorHandle)(function(){var t;return(0,It.httpClientCallWithTimeout)((0,yt.sendEvaluateCall)({global:e,event:(0,ht.adaptEvent)(r,e)}),e,(0,At.isNumber)((t=e.timeouts)===null||t===void 0?void 0:t.evaluate)?e.timeouts.evaluate:St.BASE_REQUEST_TIMEOUT,{name:"EVALUATE"})},e):(e.logger.error("serverKey is not set. Check the init function call."),Promise.resolve((0,mt.createFallbackEvaluationResult)(Ut.SDK_ERROR)))}};Z.evaluateFactory=qt});var Re=o($=>{"use strict";Object.defineProperty($,"__esModule",{value:!0});$.LOGGER_LEVELS=void 0;var Ft=["error","warn","info","debug","log"];$.LOGGER_LEVELS=Ft});var Ze=o(f=>{"use strict";var bt=f&&f.__spreadArray||function(e,r,t){if(t||arguments.length===2)for(var i=0,a=r.length,n;i<a;i++)(n||!(i in r))&&(n||(n=Array.prototype.slice.call(r,0,i)),n[i]=r[i]);return e.concat(n||Array.prototype.slice.call(r))};Object.defineProperty(f,"__esModule",{value:!0});f.withPackageName=f.withSafeMixins=f.withTryCatch=f.withLogLevel=void 0;var Vt=v(),ee=Re(),Xe=function(){},Ne=function(e,r){return ee.LOGGER_LEVELS.reduce(function(t,i){var a,n=((a=r[i])!==null&&a!==void 0?a:Xe).bind(r);return t[i]=e(i,n),t},r)},Pt=function(e){return ee.LOGGER_LEVELS.reduce(function(r,t){var i=(0,Vt.isObject)(e)?e[t]:void 0;return r[t]=(i??Xe).bind(e),r},{})};f.withSafeMixins=Pt;var Mt=function(e){var r=function(t,i){return function(){for(var a=[],n=0;n<arguments.length;n++)a[n]=arguments[n];i.apply(void 0,bt(["[BotBye!]"],a,!1))}};return Ne(r,e)};f.withPackageName=Mt;var jt=function(e){var r=function(a,n){return ee.LOGGER_LEVELS.indexOf(n)<=ee.LOGGER_LEVELS.indexOf(a)},t=function(a,n){return function(){for(var u=[],s=0;s<arguments.length;s++)u[s]=arguments[s];r(this.level,a)&&n.apply(void 0,u)}},i=Ne(t,e);return i.setLevel=function(a){this.level=a},i};f.withLogLevel=jt;var wt=function(e){var r=function(t,i){return function(){for(var a=[],n=0;n<arguments.length;n++)a[n]=arguments[n];try{i.apply(void 0,a)}catch{}}};return Ne(r,e)};f.withTryCatch=wt});var $e=o(U=>{"use strict";var re=U&&U.__assign||function(){return re=Object.assign||function(e){for(var r,t=1,i=arguments.length;t<i;t++){r=arguments[t];for(var a in r)Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a])}return e},re.apply(this,arguments)};Object.defineProperty(U,"__esModule",{value:!0});U.evaluateWithExtractorFactory=void 0;var Kt=v(),Dt=function(e){return(0,Kt.isKeyExist)(e,"request")},kt=function(e,r,t){return function(i){var a;if(Dt(i.request)){var n=e(i.request.request,t);i.request=re(re({},n),{token:(a=i.request.token)!==null&&a!==void 0?a:n.token})}return r(i)}};U.evaluateWithExtractorFactory=kt});var er=o(te=>{"use strict";Object.defineProperty(te,"__esModule",{value:!0});te.withAnyErrorHandle=void 0;var Ht=j(),Bt=M(),Wt=function(e){return function(r){return new Promise(function(t){try{t(e(r))}catch{t((0,Ht.createFallbackEvaluationResult)(Bt.SDK_ERROR))}})}};te.withAnyErrorHandle=Wt});var tr=o(_=>{"use strict";Object.defineProperty(_,"__esModule",{value:!0});_.isLoggerValid=_.isLoggerLevelValid=_.isTimeoutsValid=_.assertInitOptions=void 0;var q=v(),rr=Re(),zt=function(e){return(0,q.isPlainObject)(e)?rr.LOGGER_LEVELS.every(function(r){return typeof e[r]=="function"}):!1};_.isLoggerValid=zt;var xt=function(e){return!(!(0,q.isPlainObject)(e)||"evaluate"in e&&!(0,q.isNumber)(e.evaluate))};_.isTimeoutsValid=xt;var Gt=function(e){return(0,q.isString)(e)?rr.LOGGER_LEVELS.includes(e):!1};_.isLoggerLevelValid=Gt;var Qt=function(e){if(!(0,q.isString)(e.serverKey)||e.serverKey.length===0)throw new Error("initOptions.serverKey must be a non-empty string");if(e.url!=null&&(!(0,q.isString)(e.url)||e.url.length===0))throw new Error("initOptions.url must be a non-empty string")};_.assertInitOptions=Qt});var nr=o(ae=>{"use strict";Object.defineProperty(ae,"__esModule",{value:!0});ae.moduleApiFactory=void 0;var Jt=Ie(),Yt=Ye(),ie=Ze(),Xt=$e(),Te=v(),Zt=er(),w=tr(),ir=function(e){return(0,ie.withTryCatch)((0,ie.withLogLevel)((0,ie.withPackageName)((0,ie.withSafeMixins)(e))))},ar="info",$t=function(e){var r,t=ir(console);t.setLevel(ar);var i={url:(r=e.url)!==null&&r!==void 0?r:"https://verify.botbye.com",serverKey:"",module:e.module||{name:"NODE_CORE",version:"2.0.0"},httpClient:e.httpClient,logger:t},a=(0,Yt.evaluateFactory)(i);if(e.requestInfoExtractor){var n=e.requestInfoExtractor;a=(0,Xt.evaluateWithExtractorFactory)(n,a,i)}var u=(0,Te.once)(function(){(0,Jt.sendInitCall)({global:i}).result.then(function(){i.logger.info("Inited successfully")}).catch(function(s){i.logger.warn("Can't send init call: ".concat((0,Te.extractMessageFromError)(s)))})});return{init:(0,Te.once)(function(s){var K,oe;(0,w.assertInitOptions)(s),t=(0,w.isLoggerValid)((K=s.logger)===null||K===void 0?void 0:K.logger)?ir(s.logger.logger):t,i.url=s.url||i.url,i.serverKey=s.serverKey,i.logger=t,i.timeouts=(0,w.isTimeoutsValid)(s.timeouts)?s.timeouts:void 0,t.setLevel((0,w.isLoggerLevelValid)((oe=s.logger)===null||oe===void 0?void 0:oe.level)?s.logger.level:ar),e.skipInitCall||u()}),evaluate:(0,Zt.withAnyErrorHandle)(a),dev:{setLoggerLevel:function(s){(0,w.isLoggerLevelValid)(s)&&i.logger.setLevel(s)},sendInitCall:u,getLogger:function(){return t}}}};ae.moduleApiFactory=$t});var ur=o(O=>{"use strict";Object.defineProperty(O,"__esModule",{value:!0});O.SDK_ERROR=O.createFallbackEvaluationResult=O.getIpFromHeaders=O.moduleApiFactory=void 0;var ei=nr();Object.defineProperty(O,"moduleApiFactory",{enumerable:!0,get:function(){return ei.moduleApiFactory}});var ri=v();Object.defineProperty(O,"getIpFromHeaders",{enumerable:!0,get:function(){return ri.getIpFromHeaders}});var ti=j();Object.defineProperty(O,"createFallbackEvaluationResult",{enumerable:!0,get:function(){return ti.createFallbackEvaluationResult}});var ii=M();Object.defineProperty(O,"SDK_ERROR",{enumerable:!0,get:function(){return ii.SDK_ERROR}})});var or=o(ne=>{"use strict";Object.defineProperty(ne,"__esModule",{value:!0});ne.bodyToJson=void 0;var ai=function(e){if(e!=null)return typeof e=="string"?e:JSON.stringify(e)};ne.bodyToJson=ai});var lr=o(ue=>{"use strict";Object.defineProperty(ue,"__esModule",{value:!0});ue.fetchHttpClient=void 0;var ni=v(),ui=or(),sr=P(),oi={type:"fetchHttpClient",call:function(e,r){var t=new AbortController,i=function(){t.abort()},a=fetch(e,{method:r.method,body:(0,ui.bodyToJson)(r.body),headers:r.headers,keepalive:r.keepalive,signal:t.signal}).then(function(n){if(!n.ok)throw new Error(sr.REQUEST_ERROR_CODE);return n.text()}).catch(function(n){throw new Error("".concat(sr.REQUEST_ERROR_CODE," ").concat((0,ni.extractMessageFromError)(n)))});return{result:a,abort:i}}};ue.fetchHttpClient=oi});var ci={};gr(ci,{dev:()=>vi,evaluate:()=>li,factory:()=>fr,init:()=>si});var Er=pe(ur()),dr=pe(lr());var vr="CF_WORKER",cr="2.0.0";var fr=()=>{let e=(0,Er.moduleApiFactory)({httpClient:dr.fetchHttpClient,module:{name:vr,version:cr},skipInitCall:!0,requestInfoExtractor:(t,i)=>{try{let a={};return t.headers.forEach((n,u)=>{a[u]=n}),{ip:t.headers.get("CF-Connecting-IP")??"0.0.0.0",requestUri:new URL(t.url).pathname,requestMethod:t.method,headers:a}}catch{return i.logger.warn("Not valid type of request passed. event.request.request should be an instance of CF Workers Request. Fallback value will be used"),{ip:"0.0.0.0",headers:{}}}}}),r=!1;return{...e,evaluate:(...t)=>(r||(e.dev.sendInitCall(),r=!0),e.evaluate(...t))}},{init:si,evaluate:li,dev:vi}=fr();return pr(ci);})();
Configuration
Call init at module load time, before the fetch handler runs:
1
2
3
4
5
6
7
8
9
10
11
12
13
import { init } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { init } = BotBye;
init({
serverKey: "00000000-0000-0000-0000-000000000000", // Use your project server-key
});
export default {
async fetch(request, env, ctx) {
// ...
},
};
init() itself makes no network calls — it only stores configuration. The connection handshake with BotBye is deferred to the first evaluate() call.
init options
| Option | Type | Required | Description |
|---|---|---|---|
| serverKey | string | Yes | Server key from your BotBye project |
| url | string | No | Override BotBye API endpoint (default: https://verify.botbye.com) |
| logger.level | string | No | Log level: "error", "warn", "info", "debug", "log" (default: "info") |
| logger.logger | TLogger | No | Custom logger instance implementing { error, warn, info, debug, log } |
| timeouts.evaluate | number | No | Timeout in milliseconds for each evaluate call |
Usage
Call evaluate inside the fetch handler where bot protection is needed. Pass an event object describing the request — the SDK returns a decision.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;
export default {
async fetch(request, env, ctx) {
const result = await evaluate({
type: "validate",
request: {
request,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: request.headers.get("x-botbye-token"),
},
});
if (result.decision === "BLOCK") {
return new Response(JSON.stringify({ error: "Forbidden" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
// proceed normally
},
};
There are three event types — validate, risk, and full — each suited for a different layer of your application.
validate — edge-level bot check
Use when you just want to know: was this request made by a bot? Pass the CF Workers Request object directly — the SDK extracts IP, headers, method, and URI automatically.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
{
type: "validate";
request:
// Option A: pass the CF Workers Request object directly — SDK extracts everything automatically
| { request: Request; token?: string | null }
// Option B: construct request info manually
| { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };
customFields?: Record<string, string>;
}
The SDK extracts IP, headers, method, and URI from the CF Workers Request object automatically. You can also pass request info manually — see Option B above. The token is a one-time token generated by the BotBye client-side SDK that contains information about the user's device. Pass whatever the client sent; if no token is received, the decision will be "BLOCK".
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;
export default {
async fetch(request, env, ctx) {
const result = await evaluate({
type: "validate",
request: {
request,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: request.headers.get("x-botbye-token"),
},
});
if (result.decision === "BLOCK") {
return new Response(JSON.stringify({ error: "Forbidden" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
// proceed normally
},
};
risk — domain-level risk scoring
Use when you can read user context from the request (e.g. on a login endpoint). Send event.type, event.status, and user.accountId alongside the request to get a domain-level risk score.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
type: "risk";
request:
// Only ip is needed at this level
| { ip: string; headers?: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null }
// CF Workers Request also accepted if convenient
| { request: Request };
event: {
type: string; // e.g. "login", "password_change", "checkout"
status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
};
user: {
accountId: string;
username?: string | null;
email?: string | null;
phone?: string | null;
};
customFields?: Record<string, string>;
botbyeResult?: string; // if a validate call was made earlier, pass its result.botbyeResult here to link the requests; omit if there was no prior validate
}
event and user are the key fields here — they define what action is being performed and who is performing it, which is what drives the risk score. ip is equally important: BotBye tracks which IPs access the account to detect patterns like account sharing, credential stuffing, and suspicious geo logins. Pass it directly as { ip }, or pass the CF Workers Request object if that's more convenient.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;
export default {
async fetch(request, env, ctx) {
const { email } = await request.clone().json();
const result = await evaluate({
type: "risk",
request: { request },
event: {
type: "login",
status: "ATTEMPTED",
},
user: {
accountId: email,
email,
},
});
if (result.decision === "BLOCK") {
return new Response(JSON.stringify({ error: "Forbidden" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
return fetch(request);
},
};
Linking validate and risk events
When the same request is evaluated at two layers — for example, once at the edge (type: "validate") and then again inside a domain service (type: "risk") — BotBye can link both events and display them as a single event in the dashboard.
Step 1 — CF Worker (edge): run validate and capture botbye_result:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default {
async fetch(request, env, ctx) {
const edgeResult = await evaluate({
type: "validate",
request: {
request,
// "x-botbye-token" is an example — pass the token from wherever you store it
token: request.headers.get("x-botbye-token"),
},
});
const edgeBotbyeResult = edgeResult.botbye_result;
// Pass edgeBotbyeResult downstream — a custom request header, KV, function argument, etc.
},
};
Step 2 — domain service (auth, payment, account management): pass it as botbyeResult in the risk call:
1
2
3
4
5
6
7
8
9
10
11
12
13
const riskResult = await evaluate({
type: "risk",
request: { request },
event: {
type: "login",
status: "ATTEMPTED",
},
user: {
accountId: email,
email,
},
botbyeResult: edgeBotbyeResult,
});
botbye_result is optional in the response — if it is absent, omit botbyeResult and the events will be recorded independently.
full — edge check and domain scoring in one call
Use when the Worker itself is the endpoint and you have all context at once: the raw request with token, the user from the body, and the outcome of the action.
Event fields:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
type: "full";
request:
| { request: Request; token?: string | null }
| { ip: string; headers: Record<string, string>; requestMethod?: string | null; requestUri?: string | null; token?: string | null };
event: {
type: string;
status: "ATTEMPTED" | "SUCCESSFUL" | "FAILED" | "UNKNOWN";
};
user: {
accountId: string;
username?: string | null;
email?: string | null;
phone?: string | null;
};
customFields?: Record<string, string>;
}
Equivalent to running validate and risk in a single call.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { evaluate } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { evaluate } = BotBye;
export default {
async fetch(request, env, ctx) {
const { email, password } = await request.json();
const user = await findUser(email, env);
const loginSucceeded = user && (await checkPassword(user, password));
const result = await evaluate({
type: "full",
request: {
request,
token: request.headers.get("x-botbye-token"),
},
event: {
type: "login",
status: loginSucceeded ? "SUCCESSFUL" : "FAILED",
},
user: {
accountId: user?.id ?? email,
email,
},
});
if (result.decision === "BLOCK") {
return new Response(JSON.stringify({ error: "Forbidden" }), {
status: 403,
headers: { "Content-Type": "application/json" },
});
}
// proceed normally
},
};
Response
evaluate always returns a Promise<TEvaluationResult>:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type TEvaluationResult =
| {
decision: "ALLOW" | "BLOCK" | "CHALLENGE";
request_id: string;
risk_score: number;
scores: Record<string, number>;
signals: string[];
botbye_result?: string;
}
| {
decision: "ALLOW";
botbye_result?: string;
error: { message: string };
};
Check result.decision to decide how to handle the request:
- "ALLOW" — request appears legitimate, proceed normally
- "BLOCK" — bot or suspicious activity detected, block the request
- "CHALLENGE" — uncertain, consider issuing a CAPTCHA, MFA, or additional verification step
When the response contains an error field, BotBye could not evaluate the request (e.g. invalid server key). In that case decision defaults to "ALLOW" so that a misconfiguration does not block real users — but you should monitor and fix the underlying error.
Examples of BotBye API responses
Blocked (bot detected):
1
2
3
4
5
6
7
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "BLOCK",
"risk_score": 0.95,
"scores": { "bot": 0.95 },
"signals": ["AutomationTool"]
}
Allowed:
1
2
3
4
5
6
7
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "ALLOW",
"risk_score": 0.05,
"scores": { "bot": 0.05, "ato": 0.02 },
"signals": []
}
Challenge:
1
2
3
4
5
6
7
8
{
"request_id": "f77b2abd-c5d7-44f0-be4f-174b04876583",
"decision": "CHALLENGE",
"risk_score": 0.65,
"scores": { "bot": 0.65 },
"signals": ["SuspiciousFingerprint"],
"challenge": { "type": "captcha", "token": "..." }
}
Invalid server-key:
1
2
3
4
{
"decision": "ALLOW",
"error": { "message": "[BotBye] Bad Request: Invalid Server Key" }
}
Advanced: multiple instances
Use factory to create independent SDK instances (useful when protecting multiple projects from one Worker):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { factory } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { factory } = BotBye;
const sdk = factory();
sdk.init({
// Use your project server-key
serverKey: "00000000-0000-0000-0000-000000000000",
});
const result = await sdk.evaluate({
type: "validate",
request: { request, token },
});
Dev utilities
1
2
3
4
5
import { dev } from "@botbye/cloudflare-worker";
// Or if integrating via the Dashboard:
// const { dev } = BotBye;
dev.setLoggerLevel("debug"); // "error" | "warn" | "info" | "debug" | "log"