@deflectbot/deflect-sdk 1.4.0 → 1.4.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -19,94 +19,14 @@ declare class Deflect {
19
19
  private resolvePulseConfig;
20
20
  private reportError;
21
21
  private tryWarmup;
22
- /**
23
- * @param actionId - The action ID to validate
24
- * @returns The sanitized action ID
25
- * @throws Error if actionId contains invalid characters
26
- */
27
22
  private validateActionId;
28
- private buildScriptUrl;
29
- private fetchScript;
30
- private executeScript;
31
23
  private prefetchNextScript;
32
- private waitForGetToken;
33
- private createScriptBlob;
34
- private loadScriptElement;
35
- private getTokenFromScript;
36
- private cleanup;
37
- /**
38
- * Configures the Deflect SDK with the provided parameters.
39
- * Must be called before using getToken() or other SDK methods.
40
- *
41
- * @param params - Configuration options for the SDK
42
- * @param params.actionId - The unique action identifier from your Deflect dashboard (required)
43
- * @param params.scriptUrl - Optional custom script URL (defaults to Deflect CDN)
44
- * @throws Error if actionId is empty or contains invalid characters
45
- *
46
- * @example
47
- * ```typescript
48
- * Deflect.configure({ actionId: "your-action-id" });
49
- * ```
50
- */
51
- configure(params: DeflectConfig): void;
52
24
  private isTestMode;
53
- /**
54
- * @deprecated Use {@link getToken} instead. This method is kept for backward compatibility.
55
- * @returns A promise that resolves to the Deflect token string
56
- */
57
- solveChallenge(): Promise<string>;
58
- /**
59
- * Retrieves a Deflect token for the configured action.
60
- * The token should be included in requests to your protected endpoints.
61
- *
62
- * @returns A promise that resolves to the Deflect token string
63
- * @throws Error if configure() has not been called first
64
- * @throws Error if the script fails to load or execute
65
- *
66
- * @example
67
- * ```typescript
68
- * const token = await Deflect.getToken();
69
- * // Include token in your API request
70
- * fetch('/api/protected', {
71
- * headers: { 'X-Deflect-Token': token }
72
- * });
73
- * ```
74
- */
25
+ configure(params: DeflectConfig): void;
75
26
  getToken(): Promise<string>;
76
- /**
77
- * Pre-fetches the challenge script to reduce latency on the first getToken() call.
78
- * This is automatically called after configure(), but can be manually triggered.
79
- *
80
- * @returns A promise that resolves to true if warmup succeeded, false otherwise
81
- *
82
- * @example
83
- * ```typescript
84
- * Deflect.configure({ actionId: "your-action-id" });
85
- * const warmedUp = await Deflect.warmup();
86
- * console.log('Script pre-cached:', warmedUp);
87
- * ```
88
- */
27
+ private executeScript;
89
28
  warmup(): Promise<boolean>;
90
- /**
91
- * Clears the cached challenge script.
92
- * Useful when you need to force a fresh script fetch on the next getToken() call.
93
- */
94
29
  clearCache(): void;
95
- /**
96
- * Injects a Deflect token as a hidden input into a form and submits it.
97
- * Designed for use with form onsubmit handlers.
98
- *
99
- * @param event - The form submit event
100
- * @throws Error if not called from a form submit event
101
- *
102
- * @example
103
- * ```html
104
- * <form action="/login" method="POST" onsubmit="Deflect.injectToken(event)">
105
- * <input name="username" />
106
- * <button type="submit">Login</button>
107
- * </form>
108
- * ```
109
- */
110
30
  injectToken(event: SubmitEvent): Promise<void>;
111
31
  }
112
32
  declare const DeflectInstance: Deflect;
package/dist/index.esm.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import PulseReporter from "./pulse";
2
+ import { fetchScript } from "./scriptClient";
3
+ import { createBlobUrl, loadModuleScript, waitForGlobalFunction, getTokenFromGlobal, cleanup } from "./scriptRunner";
2
4
  class Deflect {
3
5
  constructor() {
4
6
  this.config = null;
@@ -35,9 +37,10 @@ class Deflect {
35
37
  if (!this.config?.actionId || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
36
38
  return;
37
39
  }
40
+ this.validateActionId(this.config.actionId);
38
41
  this.isWarmupInProgress = true;
39
42
  try {
40
- this.scriptCache = await this.fetchScript();
43
+ this.scriptCache = await fetchScript(this.config.actionId, this.config.scriptUrl);
41
44
  this.hasWarmupError = false;
42
45
  } catch {
43
46
  this.hasWarmupError = true;
@@ -45,11 +48,6 @@ class Deflect {
45
48
  this.isWarmupInProgress = false;
46
49
  }
47
50
  }
48
- /**
49
- * @param actionId - The action ID to validate
50
- * @returns The sanitized action ID
51
- * @throws Error if actionId contains invalid characters
52
- */
53
51
  validateActionId(actionId) {
54
52
  const sanitized = actionId.trim();
55
53
  const validPattern = /^[a-zA-Z0-9/_-]+$/;
@@ -58,167 +56,24 @@ class Deflect {
58
56
  }
59
57
  return encodeURIComponent(sanitized);
60
58
  }
61
- buildScriptUrl(actionId) {
62
- const baseUrl = this.config?.scriptUrl || "https://js.deflect.bot/main.js";
63
- const sanitizedActionId = this.validateActionId(actionId);
64
- const nonce = Date.now().toString();
65
- return `${baseUrl}?action_id=${sanitizedActionId}&_=${nonce}`;
66
- }
67
- async fetchScript() {
68
- if (!this.config?.actionId) {
69
- throw new Error("actionId is required");
70
- }
71
- const url = this.buildScriptUrl(this.config.actionId);
72
- try {
73
- const response = await fetch(url, {
74
- cache: "no-store",
75
- mode: "cors",
76
- credentials: "omit"
77
- });
78
- if (!response.ok) {
79
- throw new Error(`Failed to fetch script: ${response.status}`);
80
- }
81
- const content = await response.text();
82
- try {
83
- const maybeError = JSON.parse(content);
84
- if (maybeError.success === false || maybeError.error) {
85
- const errorMessage = maybeError.error || "Script fetch failed";
86
- const error = new Error(errorMessage);
87
- if (errorMessage === "action_does_not_exist" || errorMessage.includes("invalid action")) {
88
- error.isUserError = true;
89
- }
90
- throw error;
91
- }
92
- } catch (e) {
93
- if (!(e instanceof SyntaxError)) {
94
- throw e;
95
- }
96
- }
97
- return {
98
- content,
99
- sessionId: response.headers.get("session_id") || void 0
100
- };
101
- } catch (error) {
102
- this.reportError(
103
- error,
104
- {
105
- deflect_sdk_error: "true",
106
- method: "fetchScript",
107
- action_id: this.config.actionId
108
- },
109
- {
110
- actionId: this.config.actionId,
111
- url
112
- }
113
- );
114
- throw error;
115
- }
116
- }
117
- async executeScript(script) {
118
- if (script.sessionId && typeof window !== "undefined") {
119
- window.Deflect.sessionId = script.sessionId;
120
- }
121
- const blobUrl = this.createScriptBlob(script.content);
122
- let scriptElement = null;
123
- try {
124
- scriptElement = await this.loadScriptElement(blobUrl);
125
- await this.waitForGetToken();
126
- const token = await this.getTokenFromScript();
127
- this.prefetchNextScript();
128
- return token;
129
- } catch (error) {
130
- this.reportError(
131
- error,
132
- {
133
- deflect_sdk_error: "true",
134
- method: "executeScript",
135
- stage: "load_or_execute",
136
- action_id: this.config?.actionId || "unknown"
137
- },
138
- {
139
- hasCache: this.scriptCache !== null,
140
- hasWarmupError: this.hasWarmupError
141
- }
142
- );
143
- throw error;
144
- } finally {
145
- if (scriptElement) {
146
- this.cleanup(blobUrl, scriptElement);
147
- } else {
148
- URL.revokeObjectURL(blobUrl);
149
- }
150
- }
151
- }
152
59
  prefetchNextScript() {
153
60
  if (!this.hasWarmupError) {
154
61
  this.tryWarmup().catch(() => {
155
62
  });
156
63
  }
157
64
  }
158
- async waitForGetToken() {
159
- return new Promise((resolve, reject) => {
160
- const checkInterval = setInterval(() => {
161
- if (typeof window !== "undefined" && typeof window.Deflect?.getToken === "function") {
162
- clearInterval(checkInterval);
163
- resolve();
164
- }
165
- }, 50);
166
- setTimeout(() => {
167
- clearInterval(checkInterval);
168
- reject(new Error("Timeout: getToken function did not become available within 10 seconds"));
169
- }, 1e4);
170
- });
171
- }
172
- createScriptBlob(content) {
173
- const blob = new Blob([content], { type: "text/javascript" });
174
- return URL.createObjectURL(blob);
175
- }
176
- async loadScriptElement(blobUrl) {
177
- return new Promise((resolve, reject) => {
178
- const script = document.createElement("script");
179
- script.type = "module";
180
- script.src = blobUrl;
181
- script.onload = () => resolve(script);
182
- script.onerror = () => reject(new Error("Script failed to load"));
183
- document.head.appendChild(script);
184
- });
185
- }
186
- async getTokenFromScript() {
187
- if (typeof window === "undefined" || typeof window.Deflect?.getToken !== "function") {
188
- throw new Error("Script did not load properly - getToken not available");
189
- }
190
- try {
191
- return await window.Deflect.getToken();
192
- } catch (error) {
193
- throw new Error(`Script execution failed: ${error}`);
194
- }
195
- }
196
- cleanup(blobUrl, scriptElement) {
197
- URL.revokeObjectURL(blobUrl);
198
- scriptElement.remove();
199
- if (typeof window !== "undefined" && window.Deflect?.getToken) {
200
- delete window.Deflect.getToken;
65
+ isTestMode() {
66
+ if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
67
+ throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
201
68
  }
69
+ return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
202
70
  }
203
- /**
204
- * Configures the Deflect SDK with the provided parameters.
205
- * Must be called before using getToken() or other SDK methods.
206
- *
207
- * @param params - Configuration options for the SDK
208
- * @param params.actionId - The unique action identifier from your Deflect dashboard (required)
209
- * @param params.scriptUrl - Optional custom script URL (defaults to Deflect CDN)
210
- * @throws Error if actionId is empty or contains invalid characters
211
- *
212
- * @example
213
- * ```typescript
214
- * Deflect.configure({ actionId: "your-action-id" });
215
- * ```
216
- */
217
71
  configure(params) {
218
72
  try {
219
73
  if (!params.actionId?.trim()) {
220
74
  throw new Error("actionId is required and cannot be empty");
221
75
  }
76
+ this.validateActionId(params.actionId);
222
77
  if (this.config?.actionId === params.actionId) {
223
78
  return;
224
79
  }
@@ -250,41 +105,12 @@ class Deflect {
250
105
  throw error;
251
106
  }
252
107
  }
253
- isTestMode() {
254
- if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
255
- throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
256
- }
257
- return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
258
- }
259
- /**
260
- * @deprecated Use {@link getToken} instead. This method is kept for backward compatibility.
261
- * @returns A promise that resolves to the Deflect token string
262
- */
263
- async solveChallenge() {
264
- return this.getToken();
265
- }
266
- /**
267
- * Retrieves a Deflect token for the configured action.
268
- * The token should be included in requests to your protected endpoints.
269
- *
270
- * @returns A promise that resolves to the Deflect token string
271
- * @throws Error if configure() has not been called first
272
- * @throws Error if the script fails to load or execute
273
- *
274
- * @example
275
- * ```typescript
276
- * const token = await Deflect.getToken();
277
- * // Include token in your API request
278
- * fetch('/api/protected', {
279
- * headers: { 'X-Deflect-Token': token }
280
- * });
281
- * ```
282
- */
283
108
  async getToken() {
284
109
  try {
285
110
  if (!this.config?.actionId) {
286
- throw new Error("Must call configure() before solveChallenge()");
111
+ throw new Error("Must call configure() before getToken()");
287
112
  }
113
+ this.validateActionId(this.config.actionId);
288
114
  if (this.isTestMode()) {
289
115
  return "TESTTOKEN";
290
116
  }
@@ -293,7 +119,7 @@ class Deflect {
293
119
  script = this.scriptCache;
294
120
  this.scriptCache = null;
295
121
  } else {
296
- script = await this.fetchScript();
122
+ script = await fetchScript(this.config.actionId, this.config.scriptUrl);
297
123
  }
298
124
  return this.executeScript(script);
299
125
  } catch (error) {
@@ -318,19 +144,41 @@ class Deflect {
318
144
  throw error;
319
145
  }
320
146
  }
321
- /**
322
- * Pre-fetches the challenge script to reduce latency on the first getToken() call.
323
- * This is automatically called after configure(), but can be manually triggered.
324
- *
325
- * @returns A promise that resolves to true if warmup succeeded, false otherwise
326
- *
327
- * @example
328
- * ```typescript
329
- * Deflect.configure({ actionId: "your-action-id" });
330
- * const warmedUp = await Deflect.warmup();
331
- * console.log('Script pre-cached:', warmedUp);
332
- * ```
333
- */
147
+ async executeScript(script) {
148
+ if (script.sessionId && typeof window !== "undefined") {
149
+ window.Deflect.sessionId = script.sessionId;
150
+ }
151
+ const blobUrl = createBlobUrl(script.content);
152
+ let scriptElement = null;
153
+ try {
154
+ scriptElement = await loadModuleScript(blobUrl);
155
+ await waitForGlobalFunction("getChallengeToken");
156
+ const token = await getTokenFromGlobal("getChallengeToken");
157
+ this.prefetchNextScript();
158
+ return token;
159
+ } catch (error) {
160
+ this.reportError(
161
+ error,
162
+ {
163
+ deflect_sdk_error: "true",
164
+ method: "executeScript",
165
+ stage: "load_or_execute",
166
+ action_id: this.config?.actionId || "unknown"
167
+ },
168
+ {
169
+ hasCache: this.scriptCache !== null,
170
+ hasWarmupError: this.hasWarmupError
171
+ }
172
+ );
173
+ throw error;
174
+ } finally {
175
+ if (scriptElement) {
176
+ cleanup(blobUrl, scriptElement, "getChallengeToken");
177
+ } else {
178
+ URL.revokeObjectURL(blobUrl);
179
+ }
180
+ }
181
+ }
334
182
  async warmup() {
335
183
  if (!this.config?.actionId) {
336
184
  return false;
@@ -342,28 +190,9 @@ class Deflect {
342
190
  return false;
343
191
  }
344
192
  }
345
- /**
346
- * Clears the cached challenge script.
347
- * Useful when you need to force a fresh script fetch on the next getToken() call.
348
- */
349
193
  clearCache() {
350
194
  this.scriptCache = null;
351
195
  }
352
- /**
353
- * Injects a Deflect token as a hidden input into a form and submits it.
354
- * Designed for use with form onsubmit handlers.
355
- *
356
- * @param event - The form submit event
357
- * @throws Error if not called from a form submit event
358
- *
359
- * @example
360
- * ```html
361
- * <form action="/login" method="POST" onsubmit="Deflect.injectToken(event)">
362
- * <input name="username" />
363
- * <button type="submit">Login</button>
364
- * </form>
365
- * ```
366
- */
367
196
  async injectToken(event) {
368
197
  if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
369
198
  throw new Error("injectToken: must be called from a form submit event");
package/dist/index.js CHANGED
@@ -142,6 +142,97 @@
142
142
  };
143
143
  var pulse_default = PulseReporter;
144
144
 
145
+ // src/scriptClient.ts
146
+ function buildScriptUrl(actionId, scriptUrl) {
147
+ const baseUrl = scriptUrl || "https://js.deflect.bot/main.js";
148
+ const nonce = Date.now().toString();
149
+ return `${baseUrl}?action_id=${encodeURIComponent(actionId)}&_=${nonce}`;
150
+ }
151
+ __name(buildScriptUrl, "buildScriptUrl");
152
+ async function fetchScript(actionId, scriptUrl) {
153
+ const url = buildScriptUrl(actionId, scriptUrl);
154
+ const response = await fetch(url, {
155
+ cache: "no-store",
156
+ mode: "cors",
157
+ credentials: "omit"
158
+ });
159
+ const content = await response.text();
160
+ const error = parseErrorJson(content);
161
+ if (error) throw error;
162
+ return {
163
+ content,
164
+ sessionId: response.headers.get("session_id") || void 0
165
+ };
166
+ }
167
+ __name(fetchScript, "fetchScript");
168
+ function parseErrorJson(content) {
169
+ try {
170
+ const maybeError = JSON.parse(content);
171
+ if (maybeError.success === false || maybeError.error) {
172
+ const errorMessage = maybeError.error || "Script fetch failed";
173
+ const error = new Error(errorMessage);
174
+ if (errorMessage === "action_does_not_exist" || errorMessage.includes("invalid action")) {
175
+ error.isUserError = true;
176
+ }
177
+ return error;
178
+ }
179
+ } catch (err) {
180
+ if (!(err instanceof SyntaxError)) {
181
+ throw err;
182
+ }
183
+ }
184
+ return null;
185
+ }
186
+ __name(parseErrorJson, "parseErrorJson");
187
+
188
+ // src/scriptRunner.ts
189
+ function createBlobUrl(content) {
190
+ const blob = new Blob([content], { type: "text/javascript" });
191
+ return URL.createObjectURL(blob);
192
+ }
193
+ __name(createBlobUrl, "createBlobUrl");
194
+ function loadModuleScript(blobUrl) {
195
+ return new Promise((resolve, reject) => {
196
+ const script = document.createElement("script");
197
+ script.type = "module";
198
+ script.src = blobUrl;
199
+ script.onload = () => resolve(script);
200
+ script.onerror = () => reject(new Error("Script failed to load"));
201
+ document.head.appendChild(script);
202
+ });
203
+ }
204
+ __name(loadModuleScript, "loadModuleScript");
205
+ function waitForGlobalFunction(fnName, timeout = 1e4) {
206
+ return new Promise((resolve, reject) => {
207
+ const checkInterval = setInterval(() => {
208
+ if (typeof window !== "undefined" && typeof window.Deflect?.[fnName] === "function") {
209
+ clearInterval(checkInterval);
210
+ resolve();
211
+ }
212
+ }, 50);
213
+ setTimeout(() => {
214
+ clearInterval(checkInterval);
215
+ reject(new Error(`Timeout: ${fnName} did not become available within ${timeout / 1e3} seconds`));
216
+ }, timeout);
217
+ });
218
+ }
219
+ __name(waitForGlobalFunction, "waitForGlobalFunction");
220
+ async function getTokenFromGlobal(fnName) {
221
+ if (typeof window === "undefined" || typeof window.Deflect?.[fnName] !== "function") {
222
+ throw new Error(`${fnName} not available on window.Deflect`);
223
+ }
224
+ return window.Deflect[fnName]();
225
+ }
226
+ __name(getTokenFromGlobal, "getTokenFromGlobal");
227
+ function cleanup(blobUrl, scriptElement, fnName) {
228
+ URL.revokeObjectURL(blobUrl);
229
+ scriptElement.remove();
230
+ if (typeof window !== "undefined" && window.Deflect?.[fnName]) {
231
+ delete window.Deflect[fnName];
232
+ }
233
+ }
234
+ __name(cleanup, "cleanup");
235
+
145
236
  // src/index.ts
146
237
  var Deflect = class {
147
238
  constructor() {
@@ -182,9 +273,10 @@
182
273
  if (!this.config?.actionId || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
183
274
  return;
184
275
  }
276
+ this.validateActionId(this.config.actionId);
185
277
  this.isWarmupInProgress = true;
186
278
  try {
187
- this.scriptCache = await this.fetchScript();
279
+ this.scriptCache = await fetchScript(this.config.actionId, this.config.scriptUrl);
188
280
  this.hasWarmupError = false;
189
281
  } catch {
190
282
  this.hasWarmupError = true;
@@ -192,11 +284,6 @@
192
284
  this.isWarmupInProgress = false;
193
285
  }
194
286
  }
195
- /**
196
- * @param actionId - The action ID to validate
197
- * @returns The sanitized action ID
198
- * @throws Error if actionId contains invalid characters
199
- */
200
287
  validateActionId(actionId) {
201
288
  const sanitized = actionId.trim();
202
289
  const validPattern = /^[a-zA-Z0-9/_-]+$/;
@@ -205,167 +292,24 @@
205
292
  }
206
293
  return encodeURIComponent(sanitized);
207
294
  }
208
- buildScriptUrl(actionId) {
209
- const baseUrl = this.config?.scriptUrl || "https://js.deflect.bot/main.js";
210
- const sanitizedActionId = this.validateActionId(actionId);
211
- const nonce = Date.now().toString();
212
- return `${baseUrl}?action_id=${sanitizedActionId}&_=${nonce}`;
213
- }
214
- async fetchScript() {
215
- if (!this.config?.actionId) {
216
- throw new Error("actionId is required");
217
- }
218
- const url = this.buildScriptUrl(this.config.actionId);
219
- try {
220
- const response = await fetch(url, {
221
- cache: "no-store",
222
- mode: "cors",
223
- credentials: "omit"
224
- });
225
- if (!response.ok) {
226
- throw new Error(`Failed to fetch script: ${response.status}`);
227
- }
228
- const content = await response.text();
229
- try {
230
- const maybeError = JSON.parse(content);
231
- if (maybeError.success === false || maybeError.error) {
232
- const errorMessage = maybeError.error || "Script fetch failed";
233
- const error = new Error(errorMessage);
234
- if (errorMessage === "action_does_not_exist" || errorMessage.includes("invalid action")) {
235
- error.isUserError = true;
236
- }
237
- throw error;
238
- }
239
- } catch (e) {
240
- if (!(e instanceof SyntaxError)) {
241
- throw e;
242
- }
243
- }
244
- return {
245
- content,
246
- sessionId: response.headers.get("session_id") || void 0
247
- };
248
- } catch (error) {
249
- this.reportError(
250
- error,
251
- {
252
- deflect_sdk_error: "true",
253
- method: "fetchScript",
254
- action_id: this.config.actionId
255
- },
256
- {
257
- actionId: this.config.actionId,
258
- url
259
- }
260
- );
261
- throw error;
262
- }
263
- }
264
- async executeScript(script) {
265
- if (script.sessionId && typeof window !== "undefined") {
266
- window.Deflect.sessionId = script.sessionId;
267
- }
268
- const blobUrl = this.createScriptBlob(script.content);
269
- let scriptElement = null;
270
- try {
271
- scriptElement = await this.loadScriptElement(blobUrl);
272
- await this.waitForGetToken();
273
- const token = await this.getTokenFromScript();
274
- this.prefetchNextScript();
275
- return token;
276
- } catch (error) {
277
- this.reportError(
278
- error,
279
- {
280
- deflect_sdk_error: "true",
281
- method: "executeScript",
282
- stage: "load_or_execute",
283
- action_id: this.config?.actionId || "unknown"
284
- },
285
- {
286
- hasCache: this.scriptCache !== null,
287
- hasWarmupError: this.hasWarmupError
288
- }
289
- );
290
- throw error;
291
- } finally {
292
- if (scriptElement) {
293
- this.cleanup(blobUrl, scriptElement);
294
- } else {
295
- URL.revokeObjectURL(blobUrl);
296
- }
297
- }
298
- }
299
295
  prefetchNextScript() {
300
296
  if (!this.hasWarmupError) {
301
297
  this.tryWarmup().catch(() => {
302
298
  });
303
299
  }
304
300
  }
305
- async waitForGetToken() {
306
- return new Promise((resolve, reject) => {
307
- const checkInterval = setInterval(() => {
308
- if (typeof window !== "undefined" && typeof window.Deflect?.getToken === "function") {
309
- clearInterval(checkInterval);
310
- resolve();
311
- }
312
- }, 50);
313
- setTimeout(() => {
314
- clearInterval(checkInterval);
315
- reject(new Error("Timeout: getToken function did not become available within 10 seconds"));
316
- }, 1e4);
317
- });
318
- }
319
- createScriptBlob(content) {
320
- const blob = new Blob([content], { type: "text/javascript" });
321
- return URL.createObjectURL(blob);
322
- }
323
- async loadScriptElement(blobUrl) {
324
- return new Promise((resolve, reject) => {
325
- const script = document.createElement("script");
326
- script.type = "module";
327
- script.src = blobUrl;
328
- script.onload = () => resolve(script);
329
- script.onerror = () => reject(new Error("Script failed to load"));
330
- document.head.appendChild(script);
331
- });
332
- }
333
- async getTokenFromScript() {
334
- if (typeof window === "undefined" || typeof window.Deflect?.getToken !== "function") {
335
- throw new Error("Script did not load properly - getToken not available");
336
- }
337
- try {
338
- return await window.Deflect.getToken();
339
- } catch (error) {
340
- throw new Error(`Script execution failed: ${error}`);
341
- }
342
- }
343
- cleanup(blobUrl, scriptElement) {
344
- URL.revokeObjectURL(blobUrl);
345
- scriptElement.remove();
346
- if (typeof window !== "undefined" && window.Deflect?.getToken) {
347
- delete window.Deflect.getToken;
301
+ isTestMode() {
302
+ if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
303
+ throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
348
304
  }
305
+ return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
349
306
  }
350
- /**
351
- * Configures the Deflect SDK with the provided parameters.
352
- * Must be called before using getToken() or other SDK methods.
353
- *
354
- * @param params - Configuration options for the SDK
355
- * @param params.actionId - The unique action identifier from your Deflect dashboard (required)
356
- * @param params.scriptUrl - Optional custom script URL (defaults to Deflect CDN)
357
- * @throws Error if actionId is empty or contains invalid characters
358
- *
359
- * @example
360
- * ```typescript
361
- * Deflect.configure({ actionId: "your-action-id" });
362
- * ```
363
- */
364
307
  configure(params) {
365
308
  try {
366
309
  if (!params.actionId?.trim()) {
367
310
  throw new Error("actionId is required and cannot be empty");
368
311
  }
312
+ this.validateActionId(params.actionId);
369
313
  if (this.config?.actionId === params.actionId) {
370
314
  return;
371
315
  }
@@ -397,41 +341,12 @@
397
341
  throw error;
398
342
  }
399
343
  }
400
- isTestMode() {
401
- if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
402
- throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
403
- }
404
- return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
405
- }
406
- /**
407
- * @deprecated Use {@link getToken} instead. This method is kept for backward compatibility.
408
- * @returns A promise that resolves to the Deflect token string
409
- */
410
- async solveChallenge() {
411
- return this.getToken();
412
- }
413
- /**
414
- * Retrieves a Deflect token for the configured action.
415
- * The token should be included in requests to your protected endpoints.
416
- *
417
- * @returns A promise that resolves to the Deflect token string
418
- * @throws Error if configure() has not been called first
419
- * @throws Error if the script fails to load or execute
420
- *
421
- * @example
422
- * ```typescript
423
- * const token = await Deflect.getToken();
424
- * // Include token in your API request
425
- * fetch('/api/protected', {
426
- * headers: { 'X-Deflect-Token': token }
427
- * });
428
- * ```
429
- */
430
344
  async getToken() {
431
345
  try {
432
346
  if (!this.config?.actionId) {
433
- throw new Error("Must call configure() before solveChallenge()");
347
+ throw new Error("Must call configure() before getToken()");
434
348
  }
349
+ this.validateActionId(this.config.actionId);
435
350
  if (this.isTestMode()) {
436
351
  return "TESTTOKEN";
437
352
  }
@@ -440,7 +355,7 @@
440
355
  script = this.scriptCache;
441
356
  this.scriptCache = null;
442
357
  } else {
443
- script = await this.fetchScript();
358
+ script = await fetchScript(this.config.actionId, this.config.scriptUrl);
444
359
  }
445
360
  return this.executeScript(script);
446
361
  } catch (error) {
@@ -465,19 +380,41 @@
465
380
  throw error;
466
381
  }
467
382
  }
468
- /**
469
- * Pre-fetches the challenge script to reduce latency on the first getToken() call.
470
- * This is automatically called after configure(), but can be manually triggered.
471
- *
472
- * @returns A promise that resolves to true if warmup succeeded, false otherwise
473
- *
474
- * @example
475
- * ```typescript
476
- * Deflect.configure({ actionId: "your-action-id" });
477
- * const warmedUp = await Deflect.warmup();
478
- * console.log('Script pre-cached:', warmedUp);
479
- * ```
480
- */
383
+ async executeScript(script) {
384
+ if (script.sessionId && typeof window !== "undefined") {
385
+ window.Deflect.sessionId = script.sessionId;
386
+ }
387
+ const blobUrl = createBlobUrl(script.content);
388
+ let scriptElement = null;
389
+ try {
390
+ scriptElement = await loadModuleScript(blobUrl);
391
+ await waitForGlobalFunction("getChallengeToken");
392
+ const token = await getTokenFromGlobal("getChallengeToken");
393
+ this.prefetchNextScript();
394
+ return token;
395
+ } catch (error) {
396
+ this.reportError(
397
+ error,
398
+ {
399
+ deflect_sdk_error: "true",
400
+ method: "executeScript",
401
+ stage: "load_or_execute",
402
+ action_id: this.config?.actionId || "unknown"
403
+ },
404
+ {
405
+ hasCache: this.scriptCache !== null,
406
+ hasWarmupError: this.hasWarmupError
407
+ }
408
+ );
409
+ throw error;
410
+ } finally {
411
+ if (scriptElement) {
412
+ cleanup(blobUrl, scriptElement, "getChallengeToken");
413
+ } else {
414
+ URL.revokeObjectURL(blobUrl);
415
+ }
416
+ }
417
+ }
481
418
  async warmup() {
482
419
  if (!this.config?.actionId) {
483
420
  return false;
@@ -489,28 +426,9 @@
489
426
  return false;
490
427
  }
491
428
  }
492
- /**
493
- * Clears the cached challenge script.
494
- * Useful when you need to force a fresh script fetch on the next getToken() call.
495
- */
496
429
  clearCache() {
497
430
  this.scriptCache = null;
498
431
  }
499
- /**
500
- * Injects a Deflect token as a hidden input into a form and submits it.
501
- * Designed for use with form onsubmit handlers.
502
- *
503
- * @param event - The form submit event
504
- * @throws Error if not called from a form submit event
505
- *
506
- * @example
507
- * ```html
508
- * <form action="/login" method="POST" onsubmit="Deflect.injectToken(event)">
509
- * <input name="username" />
510
- * <button type="submit">Login</button>
511
- * </form>
512
- * ```
513
- */
514
432
  async injectToken(event) {
515
433
  if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
516
434
  throw new Error("injectToken: must be called from a form submit event");
package/dist/index.min.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";(()=>{var d={endpoint:"https://service.yabadabado.top/pulse",projectId:"deflect-sdk",deploymentId:"688529b539803661332b3f70",service:"deflect-sdk",release:"unknown"},g={name:"deflect-js-sdk",version:d.release||"unknown",language:"javascript"},l=class{constructor(e){this.config={...d,...e,endpoint:(e?.endpoint||d.endpoint).replace(/\/$/,"")},this.enabled=!!(this.config.endpoint&&this.config.projectId&&this.config.deploymentId)}captureException(e,t={}){if(!this.enabled||typeof fetch!="function")return;let n=this.buildErrorEvent(e,t);fetch(`${this.config.endpoint}/capture`,{method:"POST",headers:{"Content-Type":"application/json",project_id:this.config.projectId},body:JSON.stringify(n),keepalive:!0}).catch(()=>{})}buildErrorEvent(e,t){let n=this.normalizeError(e),r=this.config.environment||typeof window<"u"&&window.location.hostname||"unknown",i=typeof navigator>"u"?void 0:{name:navigator.userAgent||"browser",version:navigator.appVersion},o=typeof navigator>"u"?void 0:{name:navigator.platform},c=typeof window>"u"?void 0:{model:`${window.screen.width}x${window.screen.height}`,arch:typeof navigator<"u"?navigator.platform:void 0},f=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:r,service:this.config.service,timestamp:new Date().toISOString(),release:this.config.release,sdk:{...g,version:this.config.release||g.version},tags:t.tags,context:t.context,error:{level:t.level||"error",message:n.message,exception:this.buildExceptionPayload(n),request:f,runtime:i,os:o,device:c}}}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=[],n=e.stack.split(`
2
- `).slice(1);for(let r of n){let i=r.trim().replace(/^at\s+/,""),o=i.includes("(")&&i.endsWith(")"),c=o?i.slice(0,i.indexOf("(")).trim():"",s=(o?i.slice(i.indexOf("(")+1,i.length-1):i).split(":"),a=s[0]||void 0,p=s.length>1?Number(s[1]):void 0,h=s.length>2?Number(s[2]):void 0;a&&t.push({filename:a,abs_path:a,function:c||void 0,lineno:Number.isFinite(p)?p:void 0,colno:Number.isFinite(h)?h:void 0,in_app:!a.includes("node_modules")})}return t}},m=l;var u=class{constructor(){this.config=null;this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.initializeGlobalState(),this.pulseReporter=new m(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,n){this.pulseReporter.captureException(e,{tags:t,context:n})}async tryWarmup(){if(!(!this.config?.actionId||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError)){this.isWarmupInProgress=!0;try{this.scriptCache=await this.fetchScript(),this.hasWarmupError=!1}catch{this.hasWarmupError=!0}finally{this.isWarmupInProgress=!1}}}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)}buildScriptUrl(e){let t=this.config?.scriptUrl||"https://js.deflect.bot/main.js",n=this.validateActionId(e),r=Date.now().toString();return`${t}?action_id=${n}&_=${r}`}async fetchScript(){if(!this.config?.actionId)throw new Error("actionId is required");let e=this.buildScriptUrl(this.config.actionId);try{let t=await fetch(e,{cache:"no-store",mode:"cors",credentials:"omit"});if(!t.ok)throw new Error(`Failed to fetch script: ${t.status}`);let n=await t.text();try{let r=JSON.parse(n);if(r.success===!1||r.error){let i=r.error||"Script fetch failed",o=new Error(i);throw(i==="action_does_not_exist"||i.includes("invalid action"))&&(o.isUserError=!0),o}}catch(r){if(!(r instanceof SyntaxError))throw r}return{content:n,sessionId:t.headers.get("session_id")||void 0}}catch(t){throw this.reportError(t,{deflect_sdk_error:"true",method:"fetchScript",action_id:this.config.actionId},{actionId:this.config.actionId,url:e}),t}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect.sessionId=e.sessionId);let t=this.createScriptBlob(e.content),n=null;try{n=await this.loadScriptElement(t),await this.waitForGetToken();let r=await this.getTokenFromScript();return this.prefetchNextScript(),r}catch(r){throw this.reportError(r,{deflect_sdk_error:"true",method:"executeScript",stage:"load_or_execute",action_id:this.config?.actionId||"unknown"},{hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),r}finally{n?this.cleanup(t,n):URL.revokeObjectURL(t)}}prefetchNextScript(){this.hasWarmupError||this.tryWarmup().catch(()=>{})}async waitForGetToken(){return new Promise((e,t)=>{let n=setInterval(()=>{typeof window<"u"&&typeof window.Deflect?.getToken=="function"&&(clearInterval(n),e())},50);setTimeout(()=>{clearInterval(n),t(new Error("Timeout: getToken function did not become available within 10 seconds"))},1e4)})}createScriptBlob(e){let t=new Blob([e],{type:"text/javascript"});return URL.createObjectURL(t)}async loadScriptElement(e){return new Promise((t,n)=>{let r=document.createElement("script");r.type="module",r.src=e,r.onload=()=>t(r),r.onerror=()=>n(new Error("Script failed to load")),document.head.appendChild(r)})}async getTokenFromScript(){if(typeof window>"u"||typeof window.Deflect?.getToken!="function")throw new Error("Script did not load properly - getToken not available");try{return await window.Deflect.getToken()}catch(e){throw new Error(`Script execution failed: ${e}`)}}cleanup(e,t){URL.revokeObjectURL(e),t.remove(),typeof window<"u"&&window.Deflect?.getToken&&delete window.Deflect.getToken}configure(e){try{if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");if(this.config?.actionId===e.actionId)return;this.hasWarmupError=!1,this.scriptCache=null,this.config={...e},typeof window<"u"&&(window.Deflect.actionId=e.actionId),this.isTestMode()||this.tryWarmup()}catch(t){let n=t&&typeof t=="object"&&"isUserError"in t&&t.isUserError===!0;throw this.reportError(t,{deflect_sdk_error:"true",deflect_user_error:n?"true":"false",method:"configure",action_id:e?.actionId||"unknown"},{actionId:e?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),t}}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 solveChallenge(){return this.getToken()}async getToken(){try{if(!this.config?.actionId)throw new Error("Must call configure() before solveChallenge()");if(this.isTestMode())return"TESTTOKEN";let e;return this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):e=await this.fetchScript(),this.executeScript(e)}catch(e){let t=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:t?"true":"false",method:"getToken",action_id:this.config?.actionId||"unknown",has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",stage:"call"},{actionId:this.config?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),e}}async warmup(){if(!this.config?.actionId)return!1;try{return await this.tryWarmup(),this.scriptCache!==null}catch{return!1}}clearCache(){this.scriptCache=null}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,n=await this.getToken();Array.from(t.querySelectorAll('input[name="deflect_token"]')).forEach(i=>i.remove());let r=document.createElement("input");r.type="hidden",r.name="deflect_token",r.value=n,t.appendChild(r),t.submit()}},w=new u;typeof window<"u"&&(window.Deflect=w);var P=w;})();
1
+ "use strict";(()=>{var d={endpoint:"https://service.yabadabado.top/pulse",projectId:"deflect-sdk",deploymentId:"688529b539803661332b3f70",service:"deflect-sdk",release:"unknown"},w={name:"deflect-js-sdk",version:d.release||"unknown",language:"javascript"},u=class{constructor(e){this.config={...d,...e,endpoint:(e?.endpoint||d.endpoint).replace(/\/$/,"")},this.enabled=!!(this.config.endpoint&&this.config.projectId&&this.config.deploymentId)}captureException(e,t={}){if(!this.enabled||typeof fetch!="function")return;let n=this.buildErrorEvent(e,t);fetch(`${this.config.endpoint}/capture`,{method:"POST",headers:{"Content-Type":"application/json",project_id:this.config.projectId},body:JSON.stringify(n),keepalive:!0}).catch(()=>{})}buildErrorEvent(e,t){let n=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},a=typeof navigator>"u"?void 0:{name:navigator.platform},l=typeof window>"u"?void 0:{model:`${window.screen.width}x${window.screen.height}`,arch:typeof navigator<"u"?navigator.platform:void 0},g=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:{...w,version:this.config.release||w.version},tags:t.tags,context:t.context,error:{level:t.level||"error",message:n.message,exception:this.buildExceptionPayload(n),request:g,runtime:o,os:a,device:l}}}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=[],n=e.stack.split(`
2
+ `).slice(1);for(let i of n){let o=i.trim().replace(/^at\s+/,""),a=o.includes("(")&&o.endsWith(")"),l=a?o.slice(0,o.indexOf("(")).trim():"",s=(a?o.slice(o.indexOf("(")+1,o.length-1):o).split(":"),c=s[0]||void 0,h=s.length>1?Number(s[1]):void 0,m=s.length>2?Number(s[2]):void 0;c&&t.push({filename:c,abs_path:c,function:l||void 0,lineno:Number.isFinite(h)?h:void 0,colno:Number.isFinite(m)?m:void 0,in_app:!c.includes("node_modules")})}return t}},v=u;function k(r,e){let t=e||"https://js.deflect.bot/main.js",n=Date.now().toString();return`${t}?action_id=${encodeURIComponent(r)}&_=${n}`}async function f(r,e){let t=k(r,e),n=await fetch(t,{cache:"no-store",mode:"cors",credentials:"omit"}),i=await n.text(),o=_(i);if(o)throw o;return{content:i,sessionId:n.headers.get("session_id")||void 0}}function _(r){try{let e=JSON.parse(r);if(e.success===!1||e.error){let t=e.error||"Script fetch failed",n=new Error(t);return(t==="action_does_not_exist"||t.includes("invalid action"))&&(n.isUserError=!0),n}}catch(e){if(!(e instanceof SyntaxError))throw e}return null}function y(r){let e=new Blob([r],{type:"text/javascript"});return URL.createObjectURL(e)}function E(r){return new Promise((e,t)=>{let n=document.createElement("script");n.type="module",n.src=r,n.onload=()=>e(n),n.onerror=()=>t(new Error("Script failed to load")),document.head.appendChild(n)})}function I(r,e=1e4){return new Promise((t,n)=>{let i=setInterval(()=>{typeof window<"u"&&typeof window.Deflect?.[r]=="function"&&(clearInterval(i),t())},50);setTimeout(()=>{clearInterval(i),n(new Error(`Timeout: ${r} did not become available within ${e/1e3} seconds`))},e)})}async function P(r){if(typeof window>"u"||typeof window.Deflect?.[r]!="function")throw new Error(`${r} not available on window.Deflect`);return window.Deflect[r]()}function b(r,e,t){URL.revokeObjectURL(r),e.remove(),typeof window<"u"&&window.Deflect?.[t]&&delete window.Deflect[t]}var p=class{constructor(){this.config=null;this.scriptCache=null;this.isWarmupInProgress=!1;this.hasWarmupError=!1;this.initializeGlobalState(),this.pulseReporter=new v(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,n){this.pulseReporter.captureException(e,{tags:t,context:n})}async tryWarmup(){if(!(!this.config?.actionId||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError)){this.validateActionId(this.config.actionId),this.isWarmupInProgress=!0;try{this.scriptCache=await f(this.config.actionId,this.config.scriptUrl),this.hasWarmupError=!1}catch{this.hasWarmupError=!0}finally{this.isWarmupInProgress=!1}}}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.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)return;this.hasWarmupError=!1,this.scriptCache=null,this.config={...e},typeof window<"u"&&(window.Deflect.actionId=e.actionId),this.isTestMode()||this.tryWarmup()}catch(t){let n=t&&typeof t=="object"&&"isUserError"in t&&t.isUserError===!0;throw this.reportError(t,{deflect_sdk_error:"true",deflect_user_error:n?"true":"false",method:"configure",action_id:e?.actionId||"unknown"},{actionId:e?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),t}}async getToken(){try{if(!this.config?.actionId)throw new Error("Must call configure() before getToken()");if(this.validateActionId(this.config.actionId),this.isTestMode())return"TESTTOKEN";let e;return this.scriptCache&&!this.isWarmupInProgress?(e=this.scriptCache,this.scriptCache=null):e=await f(this.config.actionId,this.config.scriptUrl),this.executeScript(e)}catch(e){let t=e&&typeof e=="object"&&"isUserError"in e&&e.isUserError===!0;throw this.reportError(e,{deflect_sdk_error:"true",deflect_user_error:t?"true":"false",method:"getToken",action_id:this.config?.actionId||"unknown",has_cache:this.scriptCache!==null?"true":"false",has_warmup_error:this.hasWarmupError?"true":"false",stage:"call"},{actionId:this.config?.actionId,hasCache:this.scriptCache!==null,hasWarmupError:this.hasWarmupError}),e}}async executeScript(e){e.sessionId&&typeof window<"u"&&(window.Deflect.sessionId=e.sessionId);let t=y(e.content),n=null;try{n=await E(t),await I("getChallengeToken");let i=await P("getChallengeToken");return 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{n?b(t,n,"getChallengeToken"):URL.revokeObjectURL(t)}}async warmup(){if(!this.config?.actionId)return!1;try{return await this.tryWarmup(),this.scriptCache!==null}catch{return!1}}clearCache(){this.scriptCache=null}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,n=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=n,t.appendChild(i),t.submit()}},S=new p;typeof window<"u"&&(window.Deflect=S);var j=S;})();
@@ -0,0 +1,7 @@
1
+ export interface DeflectScript {
2
+ sessionId?: string;
3
+ content: string;
4
+ }
5
+ export declare function buildScriptUrl(actionId: string, scriptUrl?: string): string;
6
+ export declare function fetchScript(actionId: string, scriptUrl?: string): Promise<DeflectScript>;
7
+ export declare function parseErrorJson(content: string): Error | null;
@@ -0,0 +1,5 @@
1
+ export declare function createBlobUrl(content: string): string;
2
+ export declare function loadModuleScript(blobUrl: string): Promise<HTMLScriptElement>;
3
+ export declare function waitForGlobalFunction(fnName: string, timeout?: number): Promise<void>;
4
+ export declare function getTokenFromGlobal(fnName: string): Promise<string>;
5
+ export declare function cleanup(blobUrl: string, scriptElement: HTMLScriptElement, fnName: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deflectbot/deflect-sdk",
3
- "version": "1.4.0",
3
+ "version": "1.4.1-beta.1",
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",