@deflectbot/deflect-sdk 1.3.8 → 1.4.0
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 +75 -6
- package/dist/index.esm.js +191 -101
- package/dist/index.js +318 -6654
- package/dist/index.min.js +2 -15
- package/dist/pulse.d.ts +99 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -12,12 +12,19 @@ declare class Deflect {
|
|
|
12
12
|
private scriptCache;
|
|
13
13
|
private isWarmupInProgress;
|
|
14
14
|
private hasWarmupError;
|
|
15
|
-
private
|
|
15
|
+
private pulseReporter;
|
|
16
16
|
constructor();
|
|
17
|
-
private initializeSentry;
|
|
18
17
|
private initializeGlobalState;
|
|
19
18
|
private setupAutomaticWarmup;
|
|
19
|
+
private resolvePulseConfig;
|
|
20
|
+
private reportError;
|
|
20
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
|
+
private validateActionId;
|
|
21
28
|
private buildScriptUrl;
|
|
22
29
|
private fetchScript;
|
|
23
30
|
private executeScript;
|
|
@@ -27,18 +34,80 @@ declare class Deflect {
|
|
|
27
34
|
private loadScriptElement;
|
|
28
35
|
private getTokenFromScript;
|
|
29
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
|
+
*/
|
|
30
51
|
configure(params: DeflectConfig): void;
|
|
31
52
|
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
|
+
*/
|
|
32
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
|
+
*/
|
|
33
75
|
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
|
+
*/
|
|
34
89
|
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
|
+
*/
|
|
35
94
|
clearCache(): void;
|
|
36
95
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
|
+
* ```
|
|
40
109
|
*/
|
|
41
|
-
injectToken(event: SubmitEvent): Promise<
|
|
110
|
+
injectToken(event: SubmitEvent): Promise<void>;
|
|
42
111
|
}
|
|
43
112
|
declare const DeflectInstance: Deflect;
|
|
44
113
|
export default DeflectInstance;
|
package/dist/index.esm.js
CHANGED
|
@@ -1,58 +1,14 @@
|
|
|
1
|
-
import
|
|
1
|
+
import PulseReporter from "./pulse";
|
|
2
2
|
class Deflect {
|
|
3
3
|
constructor() {
|
|
4
4
|
this.config = null;
|
|
5
5
|
this.scriptCache = null;
|
|
6
6
|
this.isWarmupInProgress = false;
|
|
7
7
|
this.hasWarmupError = false;
|
|
8
|
-
this.initializeSentry();
|
|
9
8
|
this.initializeGlobalState();
|
|
9
|
+
this.pulseReporter = new PulseReporter(this.resolvePulseConfig());
|
|
10
10
|
this.setupAutomaticWarmup();
|
|
11
11
|
}
|
|
12
|
-
static {
|
|
13
|
-
this.SDK_ERROR_MARKER = "__DEFLECT_SDK_ERROR__";
|
|
14
|
-
}
|
|
15
|
-
initializeSentry() {
|
|
16
|
-
try {
|
|
17
|
-
Sentry.init({
|
|
18
|
-
dsn: "https://4a62ed75ab362d4694ae4215c2b4f621@o4509968565665792.ingest.de.sentry.io/4510257986469968",
|
|
19
|
-
sendDefaultPii: true,
|
|
20
|
-
// Enable to capture IP address
|
|
21
|
-
environment: typeof window !== "undefined" ? window.location.hostname : "unknown",
|
|
22
|
-
beforeSend(event) {
|
|
23
|
-
const isSDKError = event.tags?.deflect_sdk_error === "true";
|
|
24
|
-
if (!isSDKError) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
if (typeof window !== "undefined" && typeof navigator !== "undefined") {
|
|
28
|
-
event.contexts = event.contexts || {};
|
|
29
|
-
event.contexts.browser = {
|
|
30
|
-
name: navigator.userAgent,
|
|
31
|
-
version: navigator.appVersion
|
|
32
|
-
};
|
|
33
|
-
event.request = event.request || {};
|
|
34
|
-
event.request.headers = event.request.headers || {};
|
|
35
|
-
event.request.headers["User-Agent"] = navigator.userAgent;
|
|
36
|
-
event.contexts.page = {
|
|
37
|
-
url: window.location.href,
|
|
38
|
-
referrer: document.referrer,
|
|
39
|
-
title: document.title
|
|
40
|
-
};
|
|
41
|
-
event.contexts.device = {
|
|
42
|
-
screen_width: window.screen.width,
|
|
43
|
-
screen_height: window.screen.height,
|
|
44
|
-
viewport_width: window.innerWidth,
|
|
45
|
-
viewport_height: window.innerHeight,
|
|
46
|
-
language: navigator.language,
|
|
47
|
-
platform: navigator.platform
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return event;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
} catch {
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
12
|
initializeGlobalState() {
|
|
57
13
|
if (typeof window === "undefined") return;
|
|
58
14
|
window.Deflect = window.Deflect || {};
|
|
@@ -65,6 +21,16 @@ class Deflect {
|
|
|
65
21
|
setTimeout(() => this.tryWarmup(), 100);
|
|
66
22
|
}
|
|
67
23
|
}
|
|
24
|
+
resolvePulseConfig() {
|
|
25
|
+
const runtimeConfig = typeof window !== "undefined" && typeof window.Deflect === "object" && window.Deflect?.pulseConfig && typeof window.Deflect.pulseConfig === "object" ? window.Deflect.pulseConfig : {};
|
|
26
|
+
return {
|
|
27
|
+
...runtimeConfig,
|
|
28
|
+
environment: runtimeConfig.environment || (typeof window !== "undefined" ? window.location.hostname : void 0)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
reportError(error, tags, context) {
|
|
32
|
+
this.pulseReporter.captureException(error, { tags, context });
|
|
33
|
+
}
|
|
68
34
|
async tryWarmup() {
|
|
69
35
|
if (!this.config?.actionId || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
|
|
70
36
|
return;
|
|
@@ -79,55 +45,108 @@ class Deflect {
|
|
|
79
45
|
this.isWarmupInProgress = false;
|
|
80
46
|
}
|
|
81
47
|
}
|
|
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
|
+
validateActionId(actionId) {
|
|
54
|
+
const sanitized = actionId.trim();
|
|
55
|
+
const validPattern = /^[a-zA-Z0-9/_-]+$/;
|
|
56
|
+
if (!validPattern.test(sanitized)) {
|
|
57
|
+
throw new Error("Invalid actionId format: contains disallowed characters");
|
|
58
|
+
}
|
|
59
|
+
return encodeURIComponent(sanitized);
|
|
60
|
+
}
|
|
82
61
|
buildScriptUrl(actionId) {
|
|
83
62
|
const baseUrl = this.config?.scriptUrl || "https://js.deflect.bot/main.js";
|
|
63
|
+
const sanitizedActionId = this.validateActionId(actionId);
|
|
84
64
|
const nonce = Date.now().toString();
|
|
85
|
-
return `${baseUrl}?action_id=${
|
|
65
|
+
return `${baseUrl}?action_id=${sanitizedActionId}&_=${nonce}`;
|
|
86
66
|
}
|
|
87
67
|
async fetchScript() {
|
|
88
68
|
if (!this.config?.actionId) {
|
|
89
69
|
throw new Error("actionId is required");
|
|
90
70
|
}
|
|
91
71
|
const url = this.buildScriptUrl(this.config.actionId);
|
|
92
|
-
const response = await fetch(url, { cache: "no-store" });
|
|
93
|
-
if (!response.ok) {
|
|
94
|
-
throw new Error(`Failed to fetch script: ${response.status}`);
|
|
95
|
-
}
|
|
96
|
-
const content = await response.text();
|
|
97
72
|
try {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
throw error;
|
|
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}`);
|
|
106
80
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
+
}
|
|
111
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;
|
|
112
115
|
}
|
|
113
|
-
return {
|
|
114
|
-
content,
|
|
115
|
-
sessionId: response.headers.get("session_id") || void 0
|
|
116
|
-
};
|
|
117
116
|
}
|
|
118
117
|
async executeScript(script) {
|
|
119
118
|
if (script.sessionId && typeof window !== "undefined") {
|
|
120
119
|
window.Deflect.sessionId = script.sessionId;
|
|
121
120
|
}
|
|
122
121
|
const blobUrl = this.createScriptBlob(script.content);
|
|
123
|
-
|
|
122
|
+
let scriptElement = null;
|
|
124
123
|
try {
|
|
124
|
+
scriptElement = await this.loadScriptElement(blobUrl);
|
|
125
125
|
await this.waitForGetToken();
|
|
126
126
|
const token = await this.getTokenFromScript();
|
|
127
127
|
this.prefetchNextScript();
|
|
128
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;
|
|
129
144
|
} finally {
|
|
130
|
-
|
|
145
|
+
if (scriptElement) {
|
|
146
|
+
this.cleanup(blobUrl, scriptElement);
|
|
147
|
+
} else {
|
|
148
|
+
URL.revokeObjectURL(blobUrl);
|
|
149
|
+
}
|
|
131
150
|
}
|
|
132
151
|
}
|
|
133
152
|
prefetchNextScript() {
|
|
@@ -137,16 +156,16 @@ class Deflect {
|
|
|
137
156
|
}
|
|
138
157
|
}
|
|
139
158
|
async waitForGetToken() {
|
|
140
|
-
return new Promise((resolve) => {
|
|
159
|
+
return new Promise((resolve, reject) => {
|
|
141
160
|
const checkInterval = setInterval(() => {
|
|
142
161
|
if (typeof window !== "undefined" && typeof window.Deflect?.getToken === "function") {
|
|
143
162
|
clearInterval(checkInterval);
|
|
144
163
|
resolve();
|
|
145
164
|
}
|
|
146
|
-
},
|
|
165
|
+
}, 50);
|
|
147
166
|
setTimeout(() => {
|
|
148
167
|
clearInterval(checkInterval);
|
|
149
|
-
|
|
168
|
+
reject(new Error("Timeout: getToken function did not become available within 10 seconds"));
|
|
150
169
|
}, 1e4);
|
|
151
170
|
});
|
|
152
171
|
}
|
|
@@ -181,6 +200,20 @@ class Deflect {
|
|
|
181
200
|
delete window.Deflect.getToken;
|
|
182
201
|
}
|
|
183
202
|
}
|
|
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
|
+
*/
|
|
184
217
|
configure(params) {
|
|
185
218
|
try {
|
|
186
219
|
if (!params.actionId?.trim()) {
|
|
@@ -200,25 +233,53 @@ class Deflect {
|
|
|
200
233
|
}
|
|
201
234
|
} catch (error) {
|
|
202
235
|
const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
236
|
+
this.reportError(
|
|
237
|
+
error,
|
|
238
|
+
{
|
|
239
|
+
deflect_sdk_error: "true",
|
|
240
|
+
deflect_user_error: isUserError ? "true" : "false",
|
|
241
|
+
method: "configure",
|
|
242
|
+
action_id: params?.actionId || "unknown"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
actionId: params?.actionId,
|
|
246
|
+
hasCache: this.scriptCache !== null,
|
|
247
|
+
hasWarmupError: this.hasWarmupError
|
|
248
|
+
}
|
|
249
|
+
);
|
|
212
250
|
throw error;
|
|
213
251
|
}
|
|
214
252
|
}
|
|
215
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
|
+
}
|
|
216
257
|
return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
|
|
217
258
|
}
|
|
218
|
-
|
|
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
|
+
*/
|
|
219
263
|
async solveChallenge() {
|
|
220
264
|
return this.getToken();
|
|
221
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
|
+
*/
|
|
222
283
|
async getToken() {
|
|
223
284
|
try {
|
|
224
285
|
if (!this.config?.actionId) {
|
|
@@ -227,9 +288,6 @@ class Deflect {
|
|
|
227
288
|
if (this.isTestMode()) {
|
|
228
289
|
return "TESTTOKEN";
|
|
229
290
|
}
|
|
230
|
-
if (this.config.actionId === "SENTRY_TEST") {
|
|
231
|
-
throw new Error("SENTRY_TEST: This is a test error to verify Sentry integration is working");
|
|
232
|
-
}
|
|
233
291
|
let script;
|
|
234
292
|
if (this.scriptCache && !this.isWarmupInProgress) {
|
|
235
293
|
script = this.scriptCache;
|
|
@@ -240,20 +298,39 @@ class Deflect {
|
|
|
240
298
|
return this.executeScript(script);
|
|
241
299
|
} catch (error) {
|
|
242
300
|
const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
301
|
+
this.reportError(
|
|
302
|
+
error,
|
|
303
|
+
{
|
|
304
|
+
deflect_sdk_error: "true",
|
|
305
|
+
deflect_user_error: isUserError ? "true" : "false",
|
|
306
|
+
method: "getToken",
|
|
307
|
+
action_id: this.config?.actionId || "unknown",
|
|
308
|
+
has_cache: this.scriptCache !== null ? "true" : "false",
|
|
309
|
+
has_warmup_error: this.hasWarmupError ? "true" : "false",
|
|
310
|
+
stage: "call"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
actionId: this.config?.actionId,
|
|
314
|
+
hasCache: this.scriptCache !== null,
|
|
315
|
+
hasWarmupError: this.hasWarmupError
|
|
316
|
+
}
|
|
317
|
+
);
|
|
254
318
|
throw error;
|
|
255
319
|
}
|
|
256
320
|
}
|
|
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
|
+
*/
|
|
257
334
|
async warmup() {
|
|
258
335
|
if (!this.config?.actionId) {
|
|
259
336
|
return false;
|
|
@@ -265,13 +342,27 @@ class Deflect {
|
|
|
265
342
|
return false;
|
|
266
343
|
}
|
|
267
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Clears the cached challenge script.
|
|
347
|
+
* Useful when you need to force a fresh script fetch on the next getToken() call.
|
|
348
|
+
*/
|
|
268
349
|
clearCache() {
|
|
269
350
|
this.scriptCache = null;
|
|
270
351
|
}
|
|
271
352
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
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
|
+
* ```
|
|
275
366
|
*/
|
|
276
367
|
async injectToken(event) {
|
|
277
368
|
if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
|
|
@@ -289,7 +380,6 @@ class Deflect {
|
|
|
289
380
|
hidden.value = token;
|
|
290
381
|
form.appendChild(hidden);
|
|
291
382
|
form.submit();
|
|
292
|
-
return false;
|
|
293
383
|
}
|
|
294
384
|
}
|
|
295
385
|
const DeflectInstance = new Deflect();
|