@deflectbot/deflect-sdk 1.4.5 → 1.4.61

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
@@ -1,6 +1,5 @@
1
1
  import PulseReporter from "./pulse";
2
2
  import { DeflectExtraArgs } from "./scriptClient";
3
- /** Shared configuration for both the singleton and client instances */
4
3
  export interface DeflectConfig {
5
4
  /** The action ID to fetch the script for. */
6
5
  actionId: string;
@@ -8,7 +7,7 @@ export interface DeflectConfig {
8
7
  scriptUrl?: string;
9
8
  /** Enable/disable automatic warmup/prefetch. Defaults to true. */
10
9
  prefetch?: boolean;
11
- /** Additional query params to append to the JS fetch (e.g., debug: true => &debug=true) */
10
+ /** Additional query params to append to the JS fetch */
12
11
  extraArgs?: DeflectExtraArgs;
13
12
  }
14
13
  declare global {
@@ -26,6 +25,7 @@ export declare class DeflectClient {
26
25
  private isWarmupInProgress;
27
26
  private hasWarmupError;
28
27
  private warmupPromise;
28
+ private scriptFetchPromise;
29
29
  private pulseReporter;
30
30
  private getTokenPromise;
31
31
  private lastErrorTime;
@@ -47,6 +47,7 @@ export declare class DeflectClient {
47
47
  private tryWarmup;
48
48
  private validateActionId;
49
49
  private prefetchNextScript;
50
+ private fetchScriptOnce;
50
51
  private isTestMode;
51
52
  /** Get a challenge token for this client's actionId */
52
53
  getToken(): Promise<string>;
package/dist/index.esm.js CHANGED
@@ -5,12 +5,21 @@ const ERROR_COOLDOWN_MS = 2e3;
5
5
  const MAX_CONSECUTIVE_ERRORS = 5;
6
6
  const MAX_FETCHES_PER_WINDOW = 15;
7
7
  const FETCH_WINDOW_MS = 6e4;
8
+ void (async () => {
9
+ try {
10
+ if (typeof navigator !== "undefined" && navigator.mediaDevices?.enumerateDevices) {
11
+ await navigator.mediaDevices.enumerateDevices();
12
+ }
13
+ } catch {
14
+ }
15
+ })();
8
16
  class DeflectClient {
9
17
  constructor(config, pulseReporter) {
10
18
  this.scriptCache = null;
11
19
  this.isWarmupInProgress = false;
12
20
  this.hasWarmupError = false;
13
21
  this.warmupPromise = null;
22
+ this.scriptFetchPromise = null;
14
23
  // Guards against infinite loading
15
24
  this.getTokenPromise = null;
16
25
  this.lastErrorTime = 0;
@@ -93,14 +102,11 @@ class DeflectClient {
93
102
  return;
94
103
  }
95
104
  this.isWarmupInProgress = true;
96
- this.incrementFetchCount();
105
+ const fetchPromise = this.fetchScriptOnce();
97
106
  this.warmupPromise = (async () => {
98
107
  try {
99
- this.scriptCache = await fetchScript(
100
- this.config.actionId,
101
- this.config.scriptUrl,
102
- this.config.extraArgs
103
- );
108
+ const script = await fetchPromise;
109
+ this.scriptCache = script;
104
110
  this.hasWarmupError = false;
105
111
  this.recordFetchSuccess();
106
112
  } catch {
@@ -127,6 +133,23 @@ class DeflectClient {
127
133
  });
128
134
  }
129
135
  }
136
+ fetchScriptOnce() {
137
+ if (!this.scriptFetchPromise) {
138
+ this.incrementFetchCount();
139
+ this.scriptFetchPromise = fetchScript(
140
+ this.config.actionId,
141
+ this.config.scriptUrl,
142
+ this.config.extraArgs
143
+ ).then((script) => {
144
+ this.scriptFetchPromise = null;
145
+ return script;
146
+ }).catch((error) => {
147
+ this.scriptFetchPromise = null;
148
+ throw error;
149
+ });
150
+ }
151
+ return this.scriptFetchPromise;
152
+ }
130
153
  isTestMode() {
131
154
  if (this.config.actionId === "PULSE_TEST" || this.config.actionId === "SENTRY_TEST") {
132
155
  throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
@@ -179,12 +202,7 @@ class DeflectClient {
179
202
  script = this.scriptCache;
180
203
  this.scriptCache = null;
181
204
  } else {
182
- this.incrementFetchCount();
183
- script = await fetchScript(
184
- this.config.actionId,
185
- this.config.scriptUrl,
186
- this.config.extraArgs
187
- );
205
+ script = await this.fetchScriptOnce();
188
206
  }
189
207
  return this.executeScript(script);
190
208
  } catch (error) {
package/dist/index.js CHANGED
@@ -249,12 +249,21 @@
249
249
  var MAX_CONSECUTIVE_ERRORS = 5;
250
250
  var MAX_FETCHES_PER_WINDOW = 15;
251
251
  var FETCH_WINDOW_MS = 6e4;
252
+ void (async () => {
253
+ try {
254
+ if (typeof navigator !== "undefined" && navigator.mediaDevices?.enumerateDevices) {
255
+ await navigator.mediaDevices.enumerateDevices();
256
+ }
257
+ } catch {
258
+ }
259
+ })();
252
260
  var DeflectClient = class _DeflectClient {
253
261
  constructor(config, pulseReporter) {
254
262
  this.scriptCache = null;
255
263
  this.isWarmupInProgress = false;
256
264
  this.hasWarmupError = false;
257
265
  this.warmupPromise = null;
266
+ this.scriptFetchPromise = null;
258
267
  // Guards against infinite loading
259
268
  this.getTokenPromise = null;
260
269
  this.lastErrorTime = 0;
@@ -340,14 +349,11 @@
340
349
  return;
341
350
  }
342
351
  this.isWarmupInProgress = true;
343
- this.incrementFetchCount();
352
+ const fetchPromise = this.fetchScriptOnce();
344
353
  this.warmupPromise = (async () => {
345
354
  try {
346
- this.scriptCache = await fetchScript(
347
- this.config.actionId,
348
- this.config.scriptUrl,
349
- this.config.extraArgs
350
- );
355
+ const script = await fetchPromise;
356
+ this.scriptCache = script;
351
357
  this.hasWarmupError = false;
352
358
  this.recordFetchSuccess();
353
359
  } catch {
@@ -374,6 +380,23 @@
374
380
  });
375
381
  }
376
382
  }
383
+ fetchScriptOnce() {
384
+ if (!this.scriptFetchPromise) {
385
+ this.incrementFetchCount();
386
+ this.scriptFetchPromise = fetchScript(
387
+ this.config.actionId,
388
+ this.config.scriptUrl,
389
+ this.config.extraArgs
390
+ ).then((script) => {
391
+ this.scriptFetchPromise = null;
392
+ return script;
393
+ }).catch((error) => {
394
+ this.scriptFetchPromise = null;
395
+ throw error;
396
+ });
397
+ }
398
+ return this.scriptFetchPromise;
399
+ }
377
400
  isTestMode() {
378
401
  if (this.config.actionId === "PULSE_TEST" || this.config.actionId === "SENTRY_TEST") {
379
402
  throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
@@ -426,12 +449,7 @@
426
449
  script = this.scriptCache;
427
450
  this.scriptCache = null;
428
451
  } else {
429
- this.incrementFetchCount();
430
- script = await fetchScript(
431
- this.config.actionId,
432
- this.config.scriptUrl,
433
- this.config.extraArgs
434
- );
452
+ script = await this.fetchScriptOnce();
435
453
  }
436
454
  return this.executeScript(script);
437
455
  } 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 v(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 E(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;(async()=>{try{typeof navigator<"u"&&navigator.mediaDevices?.enumerateDevices&&await navigator.mediaDevices.enumerateDevices()}catch{}})();var 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 v(t),await E("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 v(t),await E("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.61",
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",