@deflectbot/deflect-sdk 1.4.5 → 1.4.6

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/dist/index.d.ts CHANGED
@@ -26,6 +26,7 @@ export declare class DeflectClient {
26
26
  private isWarmupInProgress;
27
27
  private hasWarmupError;
28
28
  private warmupPromise;
29
+ private scriptFetchPromise;
29
30
  private pulseReporter;
30
31
  private getTokenPromise;
31
32
  private lastErrorTime;
@@ -47,6 +48,7 @@ export declare class DeflectClient {
47
48
  private tryWarmup;
48
49
  private validateActionId;
49
50
  private prefetchNextScript;
51
+ private fetchScriptOnce;
50
52
  private isTestMode;
51
53
  /** Get a challenge token for this client's actionId */
52
54
  getToken(): Promise<string>;
package/dist/index.esm.js CHANGED
@@ -11,6 +11,7 @@ class DeflectClient {
11
11
  this.isWarmupInProgress = false;
12
12
  this.hasWarmupError = false;
13
13
  this.warmupPromise = null;
14
+ this.scriptFetchPromise = null;
14
15
  // Guards against infinite loading
15
16
  this.getTokenPromise = null;
16
17
  this.lastErrorTime = 0;
@@ -93,14 +94,11 @@ class DeflectClient {
93
94
  return;
94
95
  }
95
96
  this.isWarmupInProgress = true;
96
- this.incrementFetchCount();
97
+ const fetchPromise = this.fetchScriptOnce();
97
98
  this.warmupPromise = (async () => {
98
99
  try {
99
- this.scriptCache = await fetchScript(
100
- this.config.actionId,
101
- this.config.scriptUrl,
102
- this.config.extraArgs
103
- );
100
+ const script = await fetchPromise;
101
+ this.scriptCache = script;
104
102
  this.hasWarmupError = false;
105
103
  this.recordFetchSuccess();
106
104
  } catch {
@@ -127,6 +125,23 @@ class DeflectClient {
127
125
  });
128
126
  }
129
127
  }
128
+ fetchScriptOnce() {
129
+ if (!this.scriptFetchPromise) {
130
+ this.incrementFetchCount();
131
+ this.scriptFetchPromise = fetchScript(
132
+ this.config.actionId,
133
+ this.config.scriptUrl,
134
+ this.config.extraArgs
135
+ ).then((script) => {
136
+ this.scriptFetchPromise = null;
137
+ return script;
138
+ }).catch((error) => {
139
+ this.scriptFetchPromise = null;
140
+ throw error;
141
+ });
142
+ }
143
+ return this.scriptFetchPromise;
144
+ }
130
145
  isTestMode() {
131
146
  if (this.config.actionId === "PULSE_TEST" || this.config.actionId === "SENTRY_TEST") {
132
147
  throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
@@ -179,12 +194,7 @@ class DeflectClient {
179
194
  script = this.scriptCache;
180
195
  this.scriptCache = null;
181
196
  } else {
182
- this.incrementFetchCount();
183
- script = await fetchScript(
184
- this.config.actionId,
185
- this.config.scriptUrl,
186
- this.config.extraArgs
187
- );
197
+ script = await this.fetchScriptOnce();
188
198
  }
189
199
  return this.executeScript(script);
190
200
  } catch (error) {
package/dist/index.js CHANGED
@@ -255,6 +255,7 @@
255
255
  this.isWarmupInProgress = false;
256
256
  this.hasWarmupError = false;
257
257
  this.warmupPromise = null;
258
+ this.scriptFetchPromise = null;
258
259
  // Guards against infinite loading
259
260
  this.getTokenPromise = null;
260
261
  this.lastErrorTime = 0;
@@ -340,14 +341,11 @@
340
341
  return;
341
342
  }
342
343
  this.isWarmupInProgress = true;
343
- this.incrementFetchCount();
344
+ const fetchPromise = this.fetchScriptOnce();
344
345
  this.warmupPromise = (async () => {
345
346
  try {
346
- this.scriptCache = await fetchScript(
347
- this.config.actionId,
348
- this.config.scriptUrl,
349
- this.config.extraArgs
350
- );
347
+ const script = await fetchPromise;
348
+ this.scriptCache = script;
351
349
  this.hasWarmupError = false;
352
350
  this.recordFetchSuccess();
353
351
  } catch {
@@ -374,6 +372,23 @@
374
372
  });
375
373
  }
376
374
  }
375
+ fetchScriptOnce() {
376
+ if (!this.scriptFetchPromise) {
377
+ this.incrementFetchCount();
378
+ this.scriptFetchPromise = fetchScript(
379
+ this.config.actionId,
380
+ this.config.scriptUrl,
381
+ this.config.extraArgs
382
+ ).then((script) => {
383
+ this.scriptFetchPromise = null;
384
+ return script;
385
+ }).catch((error) => {
386
+ this.scriptFetchPromise = null;
387
+ throw error;
388
+ });
389
+ }
390
+ return this.scriptFetchPromise;
391
+ }
377
392
  isTestMode() {
378
393
  if (this.config.actionId === "PULSE_TEST" || this.config.actionId === "SENTRY_TEST") {
379
394
  throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
@@ -426,12 +441,7 @@
426
441
  script = this.scriptCache;
427
442
  this.scriptCache = null;
428
443
  } else {
429
- this.incrementFetchCount();
430
- script = await fetchScript(
431
- this.config.actionId,
432
- this.config.scriptUrl,
433
- this.config.extraArgs
434
- );
444
+ script = await this.fetchScriptOnce();
435
445
  }
436
446
  return this.executeScript(script);
437
447
  } catch (error) {
package/dist/index.min.js CHANGED
@@ -1,2 +1,2 @@
1
1
  "use strict";(()=>{var p={endpoint:"https://service.yabadabado.top/pulse",projectId:"deflect-sdk",deploymentId:"688529b539803661332b3f70",service:"deflect-sdk",release:"unknown"},k={name:"deflect-js-sdk",version:p.release||"unknown",language:"javascript"},m=class{constructor(e){this.config={...p,...e,endpoint:(e?.endpoint||p.endpoint).replace(/\/$/,"")},this.enabled=!!(this.config.endpoint&&this.config.projectId&&this.config.deploymentId)}captureException(e,t={}){if(!this.enabled||typeof fetch!="function")return;let r=this.buildErrorEvent(e,t);fetch(`${this.config.endpoint}/capture`,{method:"POST",headers:{"Content-Type":"application/json",project_id:this.config.projectId},body:JSON.stringify(r),keepalive:!0}).catch(()=>{})}buildErrorEvent(e,t){let r=this.normalizeError(e),i=this.config.environment||typeof window<"u"&&window.location.hostname||"unknown",o=typeof navigator>"u"?void 0:{name:navigator.userAgent||"browser",version:navigator.appVersion},s=typeof navigator>"u"?void 0:{name:navigator.platform},a=typeof window>"u"?void 0:{model:`${window.screen.width}x${window.screen.height}`,arch:typeof navigator<"u"?navigator.platform:void 0},b=typeof window>"u"?void 0:{method:"GET",url:window.location.href,headers:typeof navigator<"u"?{"User-Agent":navigator.userAgent,...typeof document<"u"&&document.referrer?{Referer:document.referrer}:{}}:void 0};return{event_type:"error",event_id:typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():Math.random().toString(16).slice(2)+Date.now().toString(16),deployment_id:this.config.deploymentId,project_id:this.config.projectId,environment:i,service:this.config.service,timestamp:new Date().toISOString(),release:this.config.release,sdk:{...k,version:this.config.release||k.version},tags:t.tags,context:t.context,error:{level:t.level||"error",message:r.message,exception:this.buildExceptionPayload(r),request:b,runtime:o,os:s,device:a}}}normalizeError(e){if(e instanceof Error)return e;let t=typeof e=="string"?e:"Unknown error";return new Error(t)}buildExceptionPayload(e){let t=this.parseStack(e);return{type:e.name||"Error",value:e.message,stacktrace:t.length?{frames:t}:void 0}}parseStack(e){if(!e.stack)return[];let t=[],r=e.stack.split(`
2
- `).slice(1);for(let i of r){let o=i.trim().replace(/^at\s+/,""),s=o.includes("(")&&o.endsWith(")"),a=s?o.slice(0,o.indexOf("(")).trim():"",c=(s?o.slice(o.indexOf("(")+1,o.length-1):o).split(":"),u=c[0]||void 0,S=c.length>1?Number(c[1]):void 0,T=c.length>2?Number(c[2]):void 0;u&&t.push({filename:u,abs_path:u,function:a||void 0,lineno:Number.isFinite(S)?S:void 0,colno:Number.isFinite(T)?T:void 0,in_app:!u.includes("node_modules")})}return t}},g=m;function x(n,e,t){let r=e||"https://js.deflect.bot/main.js",i=new URL(r),o=i.searchParams;if(o.set("action_id",n),o.set("_",Date.now().toString()),t&&typeof t=="object"){for(let s in t)if(Object.prototype.hasOwnProperty.call(t,s)){let a=t[s];o.set(s,String(a))}}return i.toString()}async function l(n,e,t){let r=x(n,e,t),i=await fetch(r,{cache:"no-store",mode:"cors",credentials:"omit"}),o=await i.text(),s=_(o);if(s)throw s;return{content:o,sessionId:i.headers.get("session_id")||void 0}}function _(n){try{let e=JSON.parse(n);if(e.success===!1||e.error){let t=e.error||"Script fetch failed",r=new Error(t);return(t==="action_does_not_exist"||t.includes("invalid action"))&&(r.isUserError=!0),r}}catch(e){if(!(e instanceof SyntaxError))throw e}return null}function w(n){let e=new Blob([n],{type:"text/javascript"});return URL.createObjectURL(e)}function E(n){return new Promise((e,t)=>{let r=document.createElement("script");r.type="module",r.src=n,r.onload=()=>e(r),r.onerror=()=>t(new Error("Script failed to load")),document.head.appendChild(r)})}function v(n,e=1e4){return new Promise((t,r)=>{let i=setInterval(()=>{typeof window<"u"&&typeof window.Deflect?.[n]=="function"&&(clearInterval(i),t())},50);setTimeout(()=>{clearInterval(i),r(new Error(`Timeout: ${n} did not become available within ${e/1e3} seconds`))},e)})}async function I(n){if(typeof window>"u"||typeof window.Deflect?.[n]!="function")throw new Error(`${n} not available on window.Deflect`);return window.Deflect[n]()}function y(n,e,t){URL.revokeObjectURL(n),e.remove(),typeof window<"u"&&window.Deflect?.[t]&&delete window.Deflect[t]}var d=2e3,F=5,h=15,f=6e4,C=class n{constructor(e,t){this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.warmupPromise=null;this.getTokenPromise=null;this.lastErrorTime=0;this.consecutiveErrorCount=0;this.fetchCountInWindow=0;this.fetchWindowStart=0;if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");this.validateActionId(e.actionId),this.config={prefetch:!0,...e},this.pulseReporter=t||new g(n.resolvePulseConfig()),this.config.prefetch!==!1&&!this.isTestMode()&&this.scheduleWarmup()}getActionId(){return this.config.actionId}scheduleWarmup(){typeof window>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.tryWarmup(),{once:!0}):setTimeout(()=>this.tryWarmup(),100))}static resolvePulseConfig(){let e=typeof window<"u"&&typeof window.Deflect=="object"&&window.Deflect?.pulseConfig&&typeof window.Deflect.pulseConfig=="object"?window.Deflect.pulseConfig:{};return{...e,environment:e.environment||(typeof window<"u"?window.location.hostname:void 0)}}reportError(e,t,r){this.pulseReporter.captureException(e,{tags:t,context:r})}isInErrorCooldown(){if(this.consecutiveErrorCount===0)return!1;let e=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4);return Date.now()-this.lastErrorTime<e}isRateLimited(){let e=Date.now();return e-this.fetchWindowStart>f?(this.fetchCountInWindow=0,this.fetchWindowStart=e,!1):this.fetchCountInWindow>=h}hasExceededMaxErrors(){return this.consecutiveErrorCount>=F}recordFetchSuccess(){this.consecutiveErrorCount=0,this.lastErrorTime=0}recordFetchError(){this.consecutiveErrorCount++,this.lastErrorTime=Date.now()}incrementFetchCount(){let e=Date.now();e-this.fetchWindowStart>f&&(this.fetchCountInWindow=0,this.fetchWindowStart=e),this.fetchCountInWindow++}async tryWarmup(){this.config.prefetch===!1||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError||this.isRateLimited()||this.isInErrorCooldown()||this.hasExceededMaxErrors()||(this.isWarmupInProgress=!0,this.incrementFetchCount(),this.warmupPromise=(async()=>{try{this.scriptCache=await l(this.config.actionId,this.config.scriptUrl,this.config.extraArgs),this.hasWarmupError=!1,this.recordFetchSuccess()}catch{this.hasWarmupError=!0,this.recordFetchError()}finally{this.isWarmupInProgress=!1}})(),await this.warmupPromise.catch(()=>{}))}validateActionId(e){let t=e.trim();if(!/^[a-zA-Z0-9/_-]+$/.test(t))throw new Error("Invalid actionId format: contains disallowed characters");return encodeURIComponent(t)}prefetchNextScript(){!this.hasWarmupError&&this.config.prefetch!==!1&&this.tryWarmup().catch(()=>{})}isTestMode(){if(this.config.actionId==="PULSE_TEST"||this.config.actionId==="SENTRY_TEST")throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");return this.config.actionId==="t/FFFFFFFFFFFFF/111111111"||this.config.actionId==="t/FFFFFFFFFFFFF/000000000"}async getToken(){if(this.getTokenPromise)return this.getTokenPromise;this.getTokenPromise=this.getTokenInternal();try{return await this.getTokenPromise}finally{this.getTokenPromise=null}}async getTokenInternal(){try{if(this.isTestMode())return"TESTTOKEN";if(this.hasExceededMaxErrors())throw new Error(`Too many consecutive errors (${this.consecutiveErrorCount}). Create a new client or wait before retrying.`);if(this.isRateLimited())throw new Error(`Rate limit exceeded: ${h} requests per minute. Please wait before retrying.`);if(this.isInErrorCooldown()){let r=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4)-(Date.now()-this.lastErrorTime);throw new Error(`In error cooldown. Please wait ${Math.ceil(r/1e3)} seconds before retrying.`)}let e;return this.warmupPromise&&await this.warmupPromise.catch(()=>{}),this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):(this.incrementFetchCount(),e=await l(this.config.actionId,this.config.scriptUrl,this.config.extraArgs)),this.executeScript(e)}catch(e){let t=e instanceof Error?e.message:"";t.includes("cooldown")||t.includes("Rate limit")||t.includes("Too many consecutive")||this.recordFetchError();let i=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:i?"true":"false",method:"getToken",action_id:this.config.actionId,has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",consecutive_errors:this.consecutiveErrorCount.toString(),stage:"call"},{actionId:this.config.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError,consecutiveErrorCount:this.consecutiveErrorCount,fetchCountInWindow:this.fetchCountInWindow}),e}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect=window.Deflect||{},window.Deflect.sessionId=e.sessionId);let t=w(e.content),r=null;try{r=await E(t),await v("getChallengeToken");let i=await I("getChallengeToken");return this.recordFetchSuccess(),this.prefetchNextScript(),i}catch(i){throw this.reportError(i,{deflect_sdk_error:"true",method:"executeScript",stage:"load_or_execute",action_id:this.config.actionId},{hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),i}finally{r?y(t,r,"getChallengeToken"):URL.revokeObjectURL(t)}}async warmup(){try{return this.config.prefetch===!1?!1:(await this.tryWarmup(),this.scriptCache!==null)}catch{return!1}}clearCache(){this.scriptCache=null}resetErrorState(){this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0,this.getTokenPromise=null}getStatus(){return{actionId:this.config.actionId,isRateLimited:this.isRateLimited(),isInCooldown:this.isInErrorCooldown(),consecutiveErrors:this.consecutiveErrorCount,fetchesInWindow:this.fetchCountInWindow,canFetch:!this.isRateLimited()&&!this.isInErrorCooldown()&&!this.hasExceededMaxErrors()}}async injectToken(e){if(!e||!e.target||!(e.target instanceof HTMLFormElement))throw new Error("injectToken: must be called from a form submit event");e.preventDefault();let t=e.target,r=await this.getToken();Array.from(t.querySelectorAll('input[name="deflect_token"]')).forEach(o=>o.remove());let i=document.createElement("input");i.type="hidden",i.name="deflect_token",i.value=r,t.appendChild(i),t.submit()}},P=class{constructor(){this.config=null;this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.warmupPromise=null;this.getTokenPromise=null;this.lastErrorTime=0;this.consecutiveErrorCount=0;this.fetchCountInWindow=0;this.fetchWindowStart=0;this.initializeGlobalState(),this.pulseReporter=new g(this.resolvePulseConfig()),this.setupAutomaticWarmup()}initializeGlobalState(){typeof window>"u"||(window.Deflect=window.Deflect||{})}setupAutomaticWarmup(){typeof window>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.tryWarmup()):setTimeout(()=>this.tryWarmup(),100))}resolvePulseConfig(){let e=typeof window<"u"&&typeof window.Deflect=="object"&&window.Deflect?.pulseConfig&&typeof window.Deflect.pulseConfig=="object"?window.Deflect.pulseConfig:{};return{...e,environment:e.environment||(typeof window<"u"?window.location.hostname:void 0)}}reportError(e,t,r){this.pulseReporter.captureException(e,{tags:t,context:r})}isInErrorCooldown(){if(this.consecutiveErrorCount===0)return!1;let e=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4);return Date.now()-this.lastErrorTime<e}isRateLimited(){let e=Date.now();return e-this.fetchWindowStart>f?(this.fetchCountInWindow=0,this.fetchWindowStart=e,!1):this.fetchCountInWindow>=h}hasExceededMaxErrors(){return this.consecutiveErrorCount>=F}recordFetchSuccess(){this.consecutiveErrorCount=0,this.lastErrorTime=0}recordFetchError(){this.consecutiveErrorCount++,this.lastErrorTime=Date.now()}incrementFetchCount(){let e=Date.now();e-this.fetchWindowStart>f&&(this.fetchCountInWindow=0,this.fetchWindowStart=e),this.fetchCountInWindow++}async tryWarmup(){!this.config?.actionId||this.config.prefetch===!1||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError||this.isRateLimited()||this.isInErrorCooldown()||this.hasExceededMaxErrors()||(this.validateActionId(this.config.actionId),this.isWarmupInProgress=!0,this.incrementFetchCount(),this.warmupPromise=(async()=>{try{this.scriptCache=await l(this.config.actionId,this.config.scriptUrl,this.config.extraArgs),this.hasWarmupError=!1,this.recordFetchSuccess()}catch{this.hasWarmupError=!0,this.recordFetchError()}finally{this.isWarmupInProgress=!1}})(),await this.warmupPromise.catch(()=>{}))}validateActionId(e){let t=e.trim();if(!/^[a-zA-Z0-9/_-]+$/.test(t))throw new Error("Invalid actionId format: contains disallowed characters");return encodeURIComponent(t)}prefetchNextScript(){!this.hasWarmupError&&this.config?.prefetch!==!1&&this.tryWarmup().catch(()=>{})}isTestMode(){if(this.config?.actionId==="PULSE_TEST"||this.config?.actionId==="SENTRY_TEST")throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");return this.config?.actionId==="t/FFFFFFFFFFFFF/111111111"||this.config?.actionId==="t/FFFFFFFFFFFFF/000000000"}configure(e){try{if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");if(this.validateActionId(e.actionId),this.config?.actionId===e.actionId&&this.config.prefetch===e.prefetch&&this.config.scriptUrl===e.scriptUrl)return;this.config?.actionId!==e.actionId&&(this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0),this.scriptCache=null,this.getTokenPromise=null,this.config={prefetch:!0,...e},typeof window<"u"&&(window.Deflect.actionId=e.actionId),!this.isTestMode()&&this.config.prefetch!==!1&&this.tryWarmup()}catch(t){let r=t&&typeof t=="object"&&"isUserError"in t&&t.isUserError===!0;throw this.reportError(t,{deflect_sdk_error:"true",deflect_user_error:r?"true":"false",method:"configure",action_id:e?.actionId||"unknown"},{actionId:e?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),t}}async getToken(){if(this.getTokenPromise)return this.getTokenPromise;this.getTokenPromise=this.getTokenInternal();try{return await this.getTokenPromise}finally{this.getTokenPromise=null}}async getTokenInternal(){try{if(!this.config?.actionId)throw new Error("Must call configure() before getToken()");if(this.validateActionId(this.config.actionId),this.isTestMode())return"TESTTOKEN";if(this.hasExceededMaxErrors())throw new Error(`Too many consecutive errors (${this.consecutiveErrorCount}). Call configure() with a new actionId or wait before retrying.`);if(this.isRateLimited())throw new Error(`Rate limit exceeded: ${h} requests per minute. Please wait before retrying.`);if(this.isInErrorCooldown()){let r=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4)-(Date.now()-this.lastErrorTime);throw new Error(`In error cooldown. Please wait ${Math.ceil(r/1e3)} seconds before retrying.`)}let e;return this.warmupPromise&&await this.warmupPromise.catch(()=>{}),this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):(this.incrementFetchCount(),e=await l(this.config.actionId,this.config.scriptUrl,this.config.extraArgs)),this.executeScript(e)}catch(e){let t=e instanceof Error?e.message:"";t.includes("cooldown")||t.includes("Rate limit")||t.includes("Too many consecutive")||this.recordFetchError();let i=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:i?"true":"false",method:"getToken",action_id:this.config?.actionId||"unknown",has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",consecutive_errors:this.consecutiveErrorCount.toString(),stage:"call"},{actionId:this.config?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError,consecutiveErrorCount:this.consecutiveErrorCount,fetchCountInWindow:this.fetchCountInWindow}),e}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect.sessionId=e.sessionId);let t=w(e.content),r=null;try{r=await E(t),await v("getChallengeToken");let i=await I("getChallengeToken");return this.recordFetchSuccess(),this.prefetchNextScript(),i}catch(i){throw this.reportError(i,{deflect_sdk_error:"true",method:"executeScript",stage:"load_or_execute",action_id:this.config?.actionId||"unknown"},{hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),i}finally{r?y(t,r,"getChallengeToken"):URL.revokeObjectURL(t)}}async warmup(){if(!this.config?.actionId)return!1;try{return this.config.prefetch===!1?!1:(await this.tryWarmup(),this.scriptCache!==null)}catch{return!1}}clearCache(){this.scriptCache=null}resetErrorState(){this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0,this.getTokenPromise=null}getStatus(){return{isRateLimited:this.isRateLimited(),isInCooldown:this.isInErrorCooldown(),consecutiveErrors:this.consecutiveErrorCount,fetchesInWindow:this.fetchCountInWindow,canFetch:!this.isRateLimited()&&!this.isInErrorCooldown()&&!this.hasExceededMaxErrors()}}async injectToken(e){if(!e||!e.target||!(e.target instanceof HTMLFormElement))throw new Error("injectToken: must be called from a form submit event");e.preventDefault();let t=e.target,r=await this.getToken();Array.from(t.querySelectorAll('input[name="deflect_token"]')).forEach(o=>o.remove());let i=document.createElement("input");i.type="hidden",i.name="deflect_token",i.value=r,t.appendChild(i),t.submit()}createClient(e){return new C(e,this.pulseReporter)}},W=new P;typeof window<"u"&&(window.Deflect=W);var $=W;})();
2
+ `).slice(1);for(let i of r){let o=i.trim().replace(/^at\s+/,""),s=o.includes("(")&&o.endsWith(")"),a=s?o.slice(0,o.indexOf("(")).trim():"",c=(s?o.slice(o.indexOf("(")+1,o.length-1):o).split(":"),l=c[0]||void 0,S=c.length>1?Number(c[1]):void 0,T=c.length>2?Number(c[2]):void 0;l&&t.push({filename:l,abs_path:l,function:a||void 0,lineno:Number.isFinite(S)?S:void 0,colno:Number.isFinite(T)?T:void 0,in_app:!l.includes("node_modules")})}return t}},g=m;function x(n,e,t){let r=e||"https://js.deflect.bot/main.js",i=new URL(r),o=i.searchParams;if(o.set("action_id",n),o.set("_",Date.now().toString()),t&&typeof t=="object"){for(let s in t)if(Object.prototype.hasOwnProperty.call(t,s)){let a=t[s];o.set(s,String(a))}}return i.toString()}async function u(n,e,t){let r=x(n,e,t),i=await fetch(r,{cache:"no-store",mode:"cors",credentials:"omit"}),o=await i.text(),s=_(o);if(s)throw s;return{content:o,sessionId:i.headers.get("session_id")||void 0}}function _(n){try{let e=JSON.parse(n);if(e.success===!1||e.error){let t=e.error||"Script fetch failed",r=new Error(t);return(t==="action_does_not_exist"||t.includes("invalid action"))&&(r.isUserError=!0),r}}catch(e){if(!(e instanceof SyntaxError))throw e}return null}function w(n){let e=new Blob([n],{type:"text/javascript"});return URL.createObjectURL(e)}function E(n){return new Promise((e,t)=>{let r=document.createElement("script");r.type="module",r.src=n,r.onload=()=>e(r),r.onerror=()=>t(new Error("Script failed to load")),document.head.appendChild(r)})}function v(n,e=1e4){return new Promise((t,r)=>{let i=setInterval(()=>{typeof window<"u"&&typeof window.Deflect?.[n]=="function"&&(clearInterval(i),t())},50);setTimeout(()=>{clearInterval(i),r(new Error(`Timeout: ${n} did not become available within ${e/1e3} seconds`))},e)})}async function I(n){if(typeof window>"u"||typeof window.Deflect?.[n]!="function")throw new Error(`${n} not available on window.Deflect`);return window.Deflect[n]()}function y(n,e,t){URL.revokeObjectURL(n),e.remove(),typeof window<"u"&&window.Deflect?.[t]&&delete window.Deflect[t]}var d=2e3,F=5,h=15,f=6e4,C=class n{constructor(e,t){this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.warmupPromise=null;this.scriptFetchPromise=null;this.getTokenPromise=null;this.lastErrorTime=0;this.consecutiveErrorCount=0;this.fetchCountInWindow=0;this.fetchWindowStart=0;if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");this.validateActionId(e.actionId),this.config={prefetch:!0,...e},this.pulseReporter=t||new g(n.resolvePulseConfig()),this.config.prefetch!==!1&&!this.isTestMode()&&this.scheduleWarmup()}getActionId(){return this.config.actionId}scheduleWarmup(){typeof window>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.tryWarmup(),{once:!0}):setTimeout(()=>this.tryWarmup(),100))}static resolvePulseConfig(){let e=typeof window<"u"&&typeof window.Deflect=="object"&&window.Deflect?.pulseConfig&&typeof window.Deflect.pulseConfig=="object"?window.Deflect.pulseConfig:{};return{...e,environment:e.environment||(typeof window<"u"?window.location.hostname:void 0)}}reportError(e,t,r){this.pulseReporter.captureException(e,{tags:t,context:r})}isInErrorCooldown(){if(this.consecutiveErrorCount===0)return!1;let e=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4);return Date.now()-this.lastErrorTime<e}isRateLimited(){let e=Date.now();return e-this.fetchWindowStart>f?(this.fetchCountInWindow=0,this.fetchWindowStart=e,!1):this.fetchCountInWindow>=h}hasExceededMaxErrors(){return this.consecutiveErrorCount>=F}recordFetchSuccess(){this.consecutiveErrorCount=0,this.lastErrorTime=0}recordFetchError(){this.consecutiveErrorCount++,this.lastErrorTime=Date.now()}incrementFetchCount(){let e=Date.now();e-this.fetchWindowStart>f&&(this.fetchCountInWindow=0,this.fetchWindowStart=e),this.fetchCountInWindow++}async tryWarmup(){if(this.config.prefetch===!1||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError||this.isRateLimited()||this.isInErrorCooldown()||this.hasExceededMaxErrors())return;this.isWarmupInProgress=!0;let e=this.fetchScriptOnce();this.warmupPromise=(async()=>{try{let t=await e;this.scriptCache=t,this.hasWarmupError=!1,this.recordFetchSuccess()}catch{this.hasWarmupError=!0,this.recordFetchError()}finally{this.isWarmupInProgress=!1}})(),await this.warmupPromise.catch(()=>{})}validateActionId(e){let t=e.trim();if(!/^[a-zA-Z0-9/_-]+$/.test(t))throw new Error("Invalid actionId format: contains disallowed characters");return encodeURIComponent(t)}prefetchNextScript(){!this.hasWarmupError&&this.config.prefetch!==!1&&this.tryWarmup().catch(()=>{})}fetchScriptOnce(){return this.scriptFetchPromise||(this.incrementFetchCount(),this.scriptFetchPromise=u(this.config.actionId,this.config.scriptUrl,this.config.extraArgs).then(e=>(this.scriptFetchPromise=null,e)).catch(e=>{throw this.scriptFetchPromise=null,e})),this.scriptFetchPromise}isTestMode(){if(this.config.actionId==="PULSE_TEST"||this.config.actionId==="SENTRY_TEST")throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");return this.config.actionId==="t/FFFFFFFFFFFFF/111111111"||this.config.actionId==="t/FFFFFFFFFFFFF/000000000"}async getToken(){if(this.getTokenPromise)return this.getTokenPromise;this.getTokenPromise=this.getTokenInternal();try{return await this.getTokenPromise}finally{this.getTokenPromise=null}}async getTokenInternal(){try{if(this.isTestMode())return"TESTTOKEN";if(this.hasExceededMaxErrors())throw new Error(`Too many consecutive errors (${this.consecutiveErrorCount}). Create a new client or wait before retrying.`);if(this.isRateLimited())throw new Error(`Rate limit exceeded: ${h} requests per minute. Please wait before retrying.`);if(this.isInErrorCooldown()){let r=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4)-(Date.now()-this.lastErrorTime);throw new Error(`In error cooldown. Please wait ${Math.ceil(r/1e3)} seconds before retrying.`)}let e;return this.warmupPromise&&await this.warmupPromise.catch(()=>{}),this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):e=await this.fetchScriptOnce(),this.executeScript(e)}catch(e){let t=e instanceof Error?e.message:"";t.includes("cooldown")||t.includes("Rate limit")||t.includes("Too many consecutive")||this.recordFetchError();let i=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:i?"true":"false",method:"getToken",action_id:this.config.actionId,has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",consecutive_errors:this.consecutiveErrorCount.toString(),stage:"call"},{actionId:this.config.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError,consecutiveErrorCount:this.consecutiveErrorCount,fetchCountInWindow:this.fetchCountInWindow}),e}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect=window.Deflect||{},window.Deflect.sessionId=e.sessionId);let t=w(e.content),r=null;try{r=await E(t),await v("getChallengeToken");let i=await I("getChallengeToken");return this.recordFetchSuccess(),this.prefetchNextScript(),i}catch(i){throw this.reportError(i,{deflect_sdk_error:"true",method:"executeScript",stage:"load_or_execute",action_id:this.config.actionId},{hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),i}finally{r?y(t,r,"getChallengeToken"):URL.revokeObjectURL(t)}}async warmup(){try{return this.config.prefetch===!1?!1:(await this.tryWarmup(),this.scriptCache!==null)}catch{return!1}}clearCache(){this.scriptCache=null}resetErrorState(){this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0,this.getTokenPromise=null}getStatus(){return{actionId:this.config.actionId,isRateLimited:this.isRateLimited(),isInCooldown:this.isInErrorCooldown(),consecutiveErrors:this.consecutiveErrorCount,fetchesInWindow:this.fetchCountInWindow,canFetch:!this.isRateLimited()&&!this.isInErrorCooldown()&&!this.hasExceededMaxErrors()}}async injectToken(e){if(!e||!e.target||!(e.target instanceof HTMLFormElement))throw new Error("injectToken: must be called from a form submit event");e.preventDefault();let t=e.target,r=await this.getToken();Array.from(t.querySelectorAll('input[name="deflect_token"]')).forEach(o=>o.remove());let i=document.createElement("input");i.type="hidden",i.name="deflect_token",i.value=r,t.appendChild(i),t.submit()}},P=class{constructor(){this.config=null;this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.warmupPromise=null;this.getTokenPromise=null;this.lastErrorTime=0;this.consecutiveErrorCount=0;this.fetchCountInWindow=0;this.fetchWindowStart=0;this.initializeGlobalState(),this.pulseReporter=new g(this.resolvePulseConfig()),this.setupAutomaticWarmup()}initializeGlobalState(){typeof window>"u"||(window.Deflect=window.Deflect||{})}setupAutomaticWarmup(){typeof window>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.tryWarmup()):setTimeout(()=>this.tryWarmup(),100))}resolvePulseConfig(){let e=typeof window<"u"&&typeof window.Deflect=="object"&&window.Deflect?.pulseConfig&&typeof window.Deflect.pulseConfig=="object"?window.Deflect.pulseConfig:{};return{...e,environment:e.environment||(typeof window<"u"?window.location.hostname:void 0)}}reportError(e,t,r){this.pulseReporter.captureException(e,{tags:t,context:r})}isInErrorCooldown(){if(this.consecutiveErrorCount===0)return!1;let e=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4);return Date.now()-this.lastErrorTime<e}isRateLimited(){let e=Date.now();return e-this.fetchWindowStart>f?(this.fetchCountInWindow=0,this.fetchWindowStart=e,!1):this.fetchCountInWindow>=h}hasExceededMaxErrors(){return this.consecutiveErrorCount>=F}recordFetchSuccess(){this.consecutiveErrorCount=0,this.lastErrorTime=0}recordFetchError(){this.consecutiveErrorCount++,this.lastErrorTime=Date.now()}incrementFetchCount(){let e=Date.now();e-this.fetchWindowStart>f&&(this.fetchCountInWindow=0,this.fetchWindowStart=e),this.fetchCountInWindow++}async tryWarmup(){!this.config?.actionId||this.config.prefetch===!1||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError||this.isRateLimited()||this.isInErrorCooldown()||this.hasExceededMaxErrors()||(this.validateActionId(this.config.actionId),this.isWarmupInProgress=!0,this.incrementFetchCount(),this.warmupPromise=(async()=>{try{this.scriptCache=await u(this.config.actionId,this.config.scriptUrl,this.config.extraArgs),this.hasWarmupError=!1,this.recordFetchSuccess()}catch{this.hasWarmupError=!0,this.recordFetchError()}finally{this.isWarmupInProgress=!1}})(),await this.warmupPromise.catch(()=>{}))}validateActionId(e){let t=e.trim();if(!/^[a-zA-Z0-9/_-]+$/.test(t))throw new Error("Invalid actionId format: contains disallowed characters");return encodeURIComponent(t)}prefetchNextScript(){!this.hasWarmupError&&this.config?.prefetch!==!1&&this.tryWarmup().catch(()=>{})}isTestMode(){if(this.config?.actionId==="PULSE_TEST"||this.config?.actionId==="SENTRY_TEST")throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");return this.config?.actionId==="t/FFFFFFFFFFFFF/111111111"||this.config?.actionId==="t/FFFFFFFFFFFFF/000000000"}configure(e){try{if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");if(this.validateActionId(e.actionId),this.config?.actionId===e.actionId&&this.config.prefetch===e.prefetch&&this.config.scriptUrl===e.scriptUrl)return;this.config?.actionId!==e.actionId&&(this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0),this.scriptCache=null,this.getTokenPromise=null,this.config={prefetch:!0,...e},typeof window<"u"&&(window.Deflect.actionId=e.actionId),!this.isTestMode()&&this.config.prefetch!==!1&&this.tryWarmup()}catch(t){let r=t&&typeof t=="object"&&"isUserError"in t&&t.isUserError===!0;throw this.reportError(t,{deflect_sdk_error:"true",deflect_user_error:r?"true":"false",method:"configure",action_id:e?.actionId||"unknown"},{actionId:e?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),t}}async getToken(){if(this.getTokenPromise)return this.getTokenPromise;this.getTokenPromise=this.getTokenInternal();try{return await this.getTokenPromise}finally{this.getTokenPromise=null}}async getTokenInternal(){try{if(!this.config?.actionId)throw new Error("Must call configure() before getToken()");if(this.validateActionId(this.config.actionId),this.isTestMode())return"TESTTOKEN";if(this.hasExceededMaxErrors())throw new Error(`Too many consecutive errors (${this.consecutiveErrorCount}). Call configure() with a new actionId or wait before retrying.`);if(this.isRateLimited())throw new Error(`Rate limit exceeded: ${h} requests per minute. Please wait before retrying.`);if(this.isInErrorCooldown()){let r=Math.min(d*Math.pow(2,this.consecutiveErrorCount-1),3e4)-(Date.now()-this.lastErrorTime);throw new Error(`In error cooldown. Please wait ${Math.ceil(r/1e3)} seconds before retrying.`)}let e;return this.warmupPromise&&await this.warmupPromise.catch(()=>{}),this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):(this.incrementFetchCount(),e=await u(this.config.actionId,this.config.scriptUrl,this.config.extraArgs)),this.executeScript(e)}catch(e){let t=e instanceof Error?e.message:"";t.includes("cooldown")||t.includes("Rate limit")||t.includes("Too many consecutive")||this.recordFetchError();let i=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:i?"true":"false",method:"getToken",action_id:this.config?.actionId||"unknown",has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",consecutive_errors:this.consecutiveErrorCount.toString(),stage:"call"},{actionId:this.config?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError,consecutiveErrorCount:this.consecutiveErrorCount,fetchCountInWindow:this.fetchCountInWindow}),e}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect.sessionId=e.sessionId);let t=w(e.content),r=null;try{r=await E(t),await v("getChallengeToken");let i=await I("getChallengeToken");return this.recordFetchSuccess(),this.prefetchNextScript(),i}catch(i){throw this.reportError(i,{deflect_sdk_error:"true",method:"executeScript",stage:"load_or_execute",action_id:this.config?.actionId||"unknown"},{hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),i}finally{r?y(t,r,"getChallengeToken"):URL.revokeObjectURL(t)}}async warmup(){if(!this.config?.actionId)return!1;try{return this.config.prefetch===!1?!1:(await this.tryWarmup(),this.scriptCache!==null)}catch{return!1}}clearCache(){this.scriptCache=null}resetErrorState(){this.hasWarmupError=!1,this.consecutiveErrorCount=0,this.lastErrorTime=0,this.getTokenPromise=null}getStatus(){return{isRateLimited:this.isRateLimited(),isInCooldown:this.isInErrorCooldown(),consecutiveErrors:this.consecutiveErrorCount,fetchesInWindow:this.fetchCountInWindow,canFetch:!this.isRateLimited()&&!this.isInErrorCooldown()&&!this.hasExceededMaxErrors()}}async injectToken(e){if(!e||!e.target||!(e.target instanceof HTMLFormElement))throw new Error("injectToken: must be called from a form submit event");e.preventDefault();let t=e.target,r=await this.getToken();Array.from(t.querySelectorAll('input[name="deflect_token"]')).forEach(o=>o.remove());let i=document.createElement("input");i.type="hidden",i.name="deflect_token",i.value=r,t.appendChild(i),t.submit()}createClient(e){return new C(e,this.pulseReporter)}},W=new P;typeof window<"u"&&(window.Deflect=W);var $=W;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deflectbot/deflect-sdk",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "SDK for deflect.bot - Use it for seamless captcha integration on any website.",
5
5
  "main": "dist/index.min.js",
6
6
  "module": "dist/index.esm.js",