@deflectbot/deflect-sdk 1.4.4-beta.0 → 1.4.5
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 +138 -0
- package/dist/index.esm.js +682 -0
- package/dist/index.js +929 -0
- package/dist/index.min.js +2 -0
- package/dist/pulse.d.ts +99 -0
- package/dist/scriptClient.d.ts +8 -0
- package/dist/scriptRunner.d.ts +5 -0
- package/package.json +1 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,929 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
|
|
6
|
+
// src/pulse.ts
|
|
7
|
+
var REPORT_CONFIG = {
|
|
8
|
+
endpoint: "https://service.yabadabado.top/pulse",
|
|
9
|
+
projectId: "deflect-sdk",
|
|
10
|
+
deploymentId: "688529b539803661332b3f70",
|
|
11
|
+
service: "deflect-sdk",
|
|
12
|
+
release: "unknown"
|
|
13
|
+
};
|
|
14
|
+
var PULSE_SDK_INFO = {
|
|
15
|
+
name: "deflect-js-sdk",
|
|
16
|
+
version: REPORT_CONFIG.release || "unknown",
|
|
17
|
+
language: "javascript"
|
|
18
|
+
};
|
|
19
|
+
var PulseReporter = class {
|
|
20
|
+
static {
|
|
21
|
+
__name(this, "PulseReporter");
|
|
22
|
+
}
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = {
|
|
25
|
+
...REPORT_CONFIG,
|
|
26
|
+
...config,
|
|
27
|
+
endpoint: (config?.endpoint || REPORT_CONFIG.endpoint).replace(/\/$/, "")
|
|
28
|
+
};
|
|
29
|
+
this.enabled = Boolean(
|
|
30
|
+
this.config.endpoint && this.config.projectId && this.config.deploymentId
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
captureException(error, options = {}) {
|
|
34
|
+
if (!this.enabled || typeof fetch !== "function") {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const event = this.buildErrorEvent(error, options);
|
|
38
|
+
fetch(`${this.config.endpoint}/capture`, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
project_id: this.config.projectId
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify(event),
|
|
45
|
+
keepalive: true
|
|
46
|
+
}).catch(() => {
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
buildErrorEvent(error, options) {
|
|
50
|
+
const normalizedError = this.normalizeError(error);
|
|
51
|
+
const environment = this.config.environment || (typeof window !== "undefined" ? window.location.hostname || "unknown" : "unknown");
|
|
52
|
+
const runtimeInfo = typeof navigator === "undefined" ? void 0 : {
|
|
53
|
+
name: navigator.userAgent || "browser",
|
|
54
|
+
version: navigator.appVersion
|
|
55
|
+
};
|
|
56
|
+
const osInfo = typeof navigator === "undefined" ? void 0 : {
|
|
57
|
+
name: navigator.platform
|
|
58
|
+
};
|
|
59
|
+
const deviceInfo = typeof window === "undefined" ? void 0 : {
|
|
60
|
+
model: `${window.screen.width}x${window.screen.height}`,
|
|
61
|
+
arch: typeof navigator !== "undefined" ? navigator.platform : void 0
|
|
62
|
+
};
|
|
63
|
+
const requestInfo = typeof window === "undefined" ? void 0 : {
|
|
64
|
+
method: "GET",
|
|
65
|
+
url: window.location.href,
|
|
66
|
+
headers: typeof navigator !== "undefined" ? {
|
|
67
|
+
"User-Agent": navigator.userAgent,
|
|
68
|
+
...typeof document !== "undefined" && document.referrer ? { Referer: document.referrer } : {}
|
|
69
|
+
} : void 0
|
|
70
|
+
};
|
|
71
|
+
const eventId = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(16).slice(2) + Date.now().toString(16);
|
|
72
|
+
return {
|
|
73
|
+
event_type: "error",
|
|
74
|
+
event_id: eventId,
|
|
75
|
+
deployment_id: this.config.deploymentId,
|
|
76
|
+
project_id: this.config.projectId,
|
|
77
|
+
environment,
|
|
78
|
+
service: this.config.service,
|
|
79
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
80
|
+
release: this.config.release,
|
|
81
|
+
sdk: {
|
|
82
|
+
...PULSE_SDK_INFO,
|
|
83
|
+
version: this.config.release || PULSE_SDK_INFO.version
|
|
84
|
+
},
|
|
85
|
+
tags: options.tags,
|
|
86
|
+
context: options.context,
|
|
87
|
+
error: {
|
|
88
|
+
level: options.level || "error",
|
|
89
|
+
message: normalizedError.message,
|
|
90
|
+
exception: this.buildExceptionPayload(normalizedError),
|
|
91
|
+
request: requestInfo,
|
|
92
|
+
runtime: runtimeInfo,
|
|
93
|
+
os: osInfo,
|
|
94
|
+
device: deviceInfo
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
normalizeError(error) {
|
|
99
|
+
if (error instanceof Error) {
|
|
100
|
+
return error;
|
|
101
|
+
}
|
|
102
|
+
const message = typeof error === "string" ? error : "Unknown error";
|
|
103
|
+
return new Error(message);
|
|
104
|
+
}
|
|
105
|
+
buildExceptionPayload(error) {
|
|
106
|
+
const frames = this.parseStack(error);
|
|
107
|
+
return {
|
|
108
|
+
type: error.name || "Error",
|
|
109
|
+
value: error.message,
|
|
110
|
+
stacktrace: frames.length ? { frames } : void 0
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
parseStack(error) {
|
|
114
|
+
if (!error.stack) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const frames = [];
|
|
118
|
+
const raw = error.stack.split("\n").slice(1);
|
|
119
|
+
for (const line of raw) {
|
|
120
|
+
const cleaned = line.trim().replace(/^at\s+/, "");
|
|
121
|
+
const hasLocation = cleaned.includes("(") && cleaned.endsWith(")");
|
|
122
|
+
const functionName = hasLocation ? cleaned.slice(0, cleaned.indexOf("(")).trim() : "";
|
|
123
|
+
const location = hasLocation ? cleaned.slice(cleaned.indexOf("(") + 1, cleaned.length - 1) : cleaned;
|
|
124
|
+
const parts = location.split(":");
|
|
125
|
+
const filename = parts[0] || void 0;
|
|
126
|
+
const lineno = parts.length > 1 ? Number(parts[1]) : void 0;
|
|
127
|
+
const colno = parts.length > 2 ? Number(parts[2]) : void 0;
|
|
128
|
+
if (!filename) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
frames.push({
|
|
132
|
+
filename,
|
|
133
|
+
abs_path: filename,
|
|
134
|
+
function: functionName || void 0,
|
|
135
|
+
lineno: Number.isFinite(lineno) ? lineno : void 0,
|
|
136
|
+
colno: Number.isFinite(colno) ? colno : void 0,
|
|
137
|
+
in_app: !filename.includes("node_modules")
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return frames;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var pulse_default = PulseReporter;
|
|
144
|
+
|
|
145
|
+
// src/scriptClient.ts
|
|
146
|
+
function buildScriptUrl(actionId, scriptUrl, extraArgs) {
|
|
147
|
+
const baseUrl = scriptUrl || "https://js.deflect.bot/main.js";
|
|
148
|
+
const url = new URL(baseUrl);
|
|
149
|
+
const params = url.searchParams;
|
|
150
|
+
params.set("action_id", actionId);
|
|
151
|
+
params.set("_", Date.now().toString());
|
|
152
|
+
if (extraArgs && typeof extraArgs === "object") {
|
|
153
|
+
for (const key in extraArgs) {
|
|
154
|
+
if (Object.prototype.hasOwnProperty.call(extraArgs, key)) {
|
|
155
|
+
const value = extraArgs[key];
|
|
156
|
+
params.set(key, String(value));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return url.toString();
|
|
161
|
+
}
|
|
162
|
+
__name(buildScriptUrl, "buildScriptUrl");
|
|
163
|
+
async function fetchScript(actionId, scriptUrl, extraArgs) {
|
|
164
|
+
const url = buildScriptUrl(actionId, scriptUrl, extraArgs);
|
|
165
|
+
const response = await fetch(url, {
|
|
166
|
+
cache: "no-store",
|
|
167
|
+
mode: "cors",
|
|
168
|
+
credentials: "omit"
|
|
169
|
+
});
|
|
170
|
+
const content = await response.text();
|
|
171
|
+
const error = parseErrorJson(content);
|
|
172
|
+
if (error) throw error;
|
|
173
|
+
return {
|
|
174
|
+
content,
|
|
175
|
+
sessionId: response.headers.get("session_id") || void 0
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
__name(fetchScript, "fetchScript");
|
|
179
|
+
function parseErrorJson(content) {
|
|
180
|
+
try {
|
|
181
|
+
const maybeError = JSON.parse(content);
|
|
182
|
+
if (maybeError.success === false || maybeError.error) {
|
|
183
|
+
const errorMessage = maybeError.error || "Script fetch failed";
|
|
184
|
+
const error = new Error(errorMessage);
|
|
185
|
+
if (errorMessage === "action_does_not_exist" || errorMessage.includes("invalid action")) {
|
|
186
|
+
error.isUserError = true;
|
|
187
|
+
}
|
|
188
|
+
return error;
|
|
189
|
+
}
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (!(err instanceof SyntaxError)) {
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
__name(parseErrorJson, "parseErrorJson");
|
|
198
|
+
|
|
199
|
+
// src/scriptRunner.ts
|
|
200
|
+
function createBlobUrl(content) {
|
|
201
|
+
const blob = new Blob([content], { type: "text/javascript" });
|
|
202
|
+
return URL.createObjectURL(blob);
|
|
203
|
+
}
|
|
204
|
+
__name(createBlobUrl, "createBlobUrl");
|
|
205
|
+
function loadModuleScript(blobUrl) {
|
|
206
|
+
return new Promise((resolve, reject) => {
|
|
207
|
+
const script = document.createElement("script");
|
|
208
|
+
script.type = "module";
|
|
209
|
+
script.src = blobUrl;
|
|
210
|
+
script.onload = () => resolve(script);
|
|
211
|
+
script.onerror = () => reject(new Error("Script failed to load"));
|
|
212
|
+
document.head.appendChild(script);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
__name(loadModuleScript, "loadModuleScript");
|
|
216
|
+
function waitForGlobalFunction(fnName, timeout = 1e4) {
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
const checkInterval = setInterval(() => {
|
|
219
|
+
if (typeof window !== "undefined" && typeof window.Deflect?.[fnName] === "function") {
|
|
220
|
+
clearInterval(checkInterval);
|
|
221
|
+
resolve();
|
|
222
|
+
}
|
|
223
|
+
}, 50);
|
|
224
|
+
setTimeout(() => {
|
|
225
|
+
clearInterval(checkInterval);
|
|
226
|
+
reject(new Error(`Timeout: ${fnName} did not become available within ${timeout / 1e3} seconds`));
|
|
227
|
+
}, timeout);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
__name(waitForGlobalFunction, "waitForGlobalFunction");
|
|
231
|
+
async function getTokenFromGlobal(fnName) {
|
|
232
|
+
if (typeof window === "undefined" || typeof window.Deflect?.[fnName] !== "function") {
|
|
233
|
+
throw new Error(`${fnName} not available on window.Deflect`);
|
|
234
|
+
}
|
|
235
|
+
return window.Deflect[fnName]();
|
|
236
|
+
}
|
|
237
|
+
__name(getTokenFromGlobal, "getTokenFromGlobal");
|
|
238
|
+
function cleanup(blobUrl, scriptElement, fnName) {
|
|
239
|
+
URL.revokeObjectURL(blobUrl);
|
|
240
|
+
scriptElement.remove();
|
|
241
|
+
if (typeof window !== "undefined" && window.Deflect?.[fnName]) {
|
|
242
|
+
delete window.Deflect[fnName];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
__name(cleanup, "cleanup");
|
|
246
|
+
|
|
247
|
+
// src/index.ts
|
|
248
|
+
var ERROR_COOLDOWN_MS = 2e3;
|
|
249
|
+
var MAX_CONSECUTIVE_ERRORS = 5;
|
|
250
|
+
var MAX_FETCHES_PER_WINDOW = 15;
|
|
251
|
+
var FETCH_WINDOW_MS = 6e4;
|
|
252
|
+
var DeflectClient = class _DeflectClient {
|
|
253
|
+
constructor(config, pulseReporter) {
|
|
254
|
+
this.scriptCache = null;
|
|
255
|
+
this.isWarmupInProgress = false;
|
|
256
|
+
this.hasWarmupError = false;
|
|
257
|
+
this.warmupPromise = null;
|
|
258
|
+
// Guards against infinite loading
|
|
259
|
+
this.getTokenPromise = null;
|
|
260
|
+
this.lastErrorTime = 0;
|
|
261
|
+
this.consecutiveErrorCount = 0;
|
|
262
|
+
this.fetchCountInWindow = 0;
|
|
263
|
+
this.fetchWindowStart = 0;
|
|
264
|
+
if (!config.actionId?.trim()) {
|
|
265
|
+
throw new Error("actionId is required and cannot be empty");
|
|
266
|
+
}
|
|
267
|
+
this.validateActionId(config.actionId);
|
|
268
|
+
this.config = { prefetch: true, ...config };
|
|
269
|
+
this.pulseReporter = pulseReporter || new pulse_default(_DeflectClient.resolvePulseConfig());
|
|
270
|
+
if (this.config.prefetch !== false && !this.isTestMode()) {
|
|
271
|
+
this.scheduleWarmup();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
static {
|
|
275
|
+
__name(this, "DeflectClient");
|
|
276
|
+
}
|
|
277
|
+
/** Get the actionId this client is configured for */
|
|
278
|
+
getActionId() {
|
|
279
|
+
return this.config.actionId;
|
|
280
|
+
}
|
|
281
|
+
scheduleWarmup() {
|
|
282
|
+
if (typeof window === "undefined") return;
|
|
283
|
+
if (document.readyState === "loading") {
|
|
284
|
+
document.addEventListener("DOMContentLoaded", () => this.tryWarmup(), { once: true });
|
|
285
|
+
} else {
|
|
286
|
+
setTimeout(() => this.tryWarmup(), 100);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
static resolvePulseConfig() {
|
|
290
|
+
const runtimeConfig = typeof window !== "undefined" && typeof window.Deflect === "object" && window.Deflect?.pulseConfig && typeof window.Deflect.pulseConfig === "object" ? window.Deflect.pulseConfig : {};
|
|
291
|
+
return {
|
|
292
|
+
...runtimeConfig,
|
|
293
|
+
environment: runtimeConfig.environment || (typeof window !== "undefined" ? window.location.hostname : void 0)
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
reportError(error, tags, context) {
|
|
297
|
+
this.pulseReporter.captureException(error, { tags, context });
|
|
298
|
+
}
|
|
299
|
+
isInErrorCooldown() {
|
|
300
|
+
if (this.consecutiveErrorCount === 0) return false;
|
|
301
|
+
const backoffMs = Math.min(
|
|
302
|
+
ERROR_COOLDOWN_MS * Math.pow(2, this.consecutiveErrorCount - 1),
|
|
303
|
+
3e4
|
|
304
|
+
);
|
|
305
|
+
return Date.now() - this.lastErrorTime < backoffMs;
|
|
306
|
+
}
|
|
307
|
+
isRateLimited() {
|
|
308
|
+
const now = Date.now();
|
|
309
|
+
if (now - this.fetchWindowStart > FETCH_WINDOW_MS) {
|
|
310
|
+
this.fetchCountInWindow = 0;
|
|
311
|
+
this.fetchWindowStart = now;
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
return this.fetchCountInWindow >= MAX_FETCHES_PER_WINDOW;
|
|
315
|
+
}
|
|
316
|
+
hasExceededMaxErrors() {
|
|
317
|
+
return this.consecutiveErrorCount >= MAX_CONSECUTIVE_ERRORS;
|
|
318
|
+
}
|
|
319
|
+
recordFetchSuccess() {
|
|
320
|
+
this.consecutiveErrorCount = 0;
|
|
321
|
+
this.lastErrorTime = 0;
|
|
322
|
+
}
|
|
323
|
+
recordFetchError() {
|
|
324
|
+
this.consecutiveErrorCount++;
|
|
325
|
+
this.lastErrorTime = Date.now();
|
|
326
|
+
}
|
|
327
|
+
incrementFetchCount() {
|
|
328
|
+
const now = Date.now();
|
|
329
|
+
if (now - this.fetchWindowStart > FETCH_WINDOW_MS) {
|
|
330
|
+
this.fetchCountInWindow = 0;
|
|
331
|
+
this.fetchWindowStart = now;
|
|
332
|
+
}
|
|
333
|
+
this.fetchCountInWindow++;
|
|
334
|
+
}
|
|
335
|
+
async tryWarmup() {
|
|
336
|
+
if (this.config.prefetch === false || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (this.isRateLimited() || this.isInErrorCooldown() || this.hasExceededMaxErrors()) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
this.isWarmupInProgress = true;
|
|
343
|
+
this.incrementFetchCount();
|
|
344
|
+
this.warmupPromise = (async () => {
|
|
345
|
+
try {
|
|
346
|
+
this.scriptCache = await fetchScript(
|
|
347
|
+
this.config.actionId,
|
|
348
|
+
this.config.scriptUrl,
|
|
349
|
+
this.config.extraArgs
|
|
350
|
+
);
|
|
351
|
+
this.hasWarmupError = false;
|
|
352
|
+
this.recordFetchSuccess();
|
|
353
|
+
} catch {
|
|
354
|
+
this.hasWarmupError = true;
|
|
355
|
+
this.recordFetchError();
|
|
356
|
+
} finally {
|
|
357
|
+
this.isWarmupInProgress = false;
|
|
358
|
+
}
|
|
359
|
+
})();
|
|
360
|
+
await this.warmupPromise.catch(() => {
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
validateActionId(actionId) {
|
|
364
|
+
const sanitized = actionId.trim();
|
|
365
|
+
const validPattern = /^[a-zA-Z0-9/_-]+$/;
|
|
366
|
+
if (!validPattern.test(sanitized)) {
|
|
367
|
+
throw new Error("Invalid actionId format: contains disallowed characters");
|
|
368
|
+
}
|
|
369
|
+
return encodeURIComponent(sanitized);
|
|
370
|
+
}
|
|
371
|
+
prefetchNextScript() {
|
|
372
|
+
if (!this.hasWarmupError && this.config.prefetch !== false) {
|
|
373
|
+
this.tryWarmup().catch(() => {
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
isTestMode() {
|
|
378
|
+
if (this.config.actionId === "PULSE_TEST" || this.config.actionId === "SENTRY_TEST") {
|
|
379
|
+
throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
|
|
380
|
+
}
|
|
381
|
+
return this.config.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config.actionId === "t/FFFFFFFFFFFFF/000000000";
|
|
382
|
+
}
|
|
383
|
+
/** Get a challenge token for this client's actionId */
|
|
384
|
+
async getToken() {
|
|
385
|
+
if (this.getTokenPromise) {
|
|
386
|
+
return this.getTokenPromise;
|
|
387
|
+
}
|
|
388
|
+
this.getTokenPromise = this.getTokenInternal();
|
|
389
|
+
try {
|
|
390
|
+
return await this.getTokenPromise;
|
|
391
|
+
} finally {
|
|
392
|
+
this.getTokenPromise = null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async getTokenInternal() {
|
|
396
|
+
try {
|
|
397
|
+
if (this.isTestMode()) {
|
|
398
|
+
return "TESTTOKEN";
|
|
399
|
+
}
|
|
400
|
+
if (this.hasExceededMaxErrors()) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
`Too many consecutive errors (${this.consecutiveErrorCount}). Create a new client or wait before retrying.`
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
if (this.isRateLimited()) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Rate limit exceeded: ${MAX_FETCHES_PER_WINDOW} requests per minute. Please wait before retrying.`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (this.isInErrorCooldown()) {
|
|
411
|
+
const backoffMs = Math.min(
|
|
412
|
+
ERROR_COOLDOWN_MS * Math.pow(2, this.consecutiveErrorCount - 1),
|
|
413
|
+
3e4
|
|
414
|
+
);
|
|
415
|
+
const remainingMs = backoffMs - (Date.now() - this.lastErrorTime);
|
|
416
|
+
throw new Error(
|
|
417
|
+
`In error cooldown. Please wait ${Math.ceil(remainingMs / 1e3)} seconds before retrying.`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
let script;
|
|
421
|
+
if (this.warmupPromise) {
|
|
422
|
+
await this.warmupPromise.catch(() => {
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
if (this.scriptCache && !this.isWarmupInProgress) {
|
|
426
|
+
script = this.scriptCache;
|
|
427
|
+
this.scriptCache = null;
|
|
428
|
+
} else {
|
|
429
|
+
this.incrementFetchCount();
|
|
430
|
+
script = await fetchScript(
|
|
431
|
+
this.config.actionId,
|
|
432
|
+
this.config.scriptUrl,
|
|
433
|
+
this.config.extraArgs
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
return this.executeScript(script);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
const errorMessage = error instanceof Error ? error.message : "";
|
|
439
|
+
const isGuardError = errorMessage.includes("cooldown") || errorMessage.includes("Rate limit") || errorMessage.includes("Too many consecutive");
|
|
440
|
+
if (!isGuardError) {
|
|
441
|
+
this.recordFetchError();
|
|
442
|
+
}
|
|
443
|
+
const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
|
|
444
|
+
this.reportError(
|
|
445
|
+
error,
|
|
446
|
+
{
|
|
447
|
+
deflect_sdk_error: "true",
|
|
448
|
+
deflect_user_error: isUserError ? "true" : "false",
|
|
449
|
+
method: "getToken",
|
|
450
|
+
action_id: this.config.actionId,
|
|
451
|
+
has_cache: this.scriptCache !== null ? "true" : "false",
|
|
452
|
+
has_warmup_error: this.hasWarmupError ? "true" : "false",
|
|
453
|
+
consecutive_errors: this.consecutiveErrorCount.toString(),
|
|
454
|
+
stage: "call"
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
actionId: this.config.actionId,
|
|
458
|
+
hasCache: this.scriptCache !== null,
|
|
459
|
+
hasWarmupError: this.hasWarmupError,
|
|
460
|
+
consecutiveErrorCount: this.consecutiveErrorCount,
|
|
461
|
+
fetchCountInWindow: this.fetchCountInWindow
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async executeScript(script) {
|
|
468
|
+
if (script.sessionId && typeof window !== "undefined") {
|
|
469
|
+
window.Deflect = window.Deflect || {};
|
|
470
|
+
window.Deflect.sessionId = script.sessionId;
|
|
471
|
+
}
|
|
472
|
+
const blobUrl = createBlobUrl(script.content);
|
|
473
|
+
let scriptElement = null;
|
|
474
|
+
try {
|
|
475
|
+
scriptElement = await loadModuleScript(blobUrl);
|
|
476
|
+
await waitForGlobalFunction("getChallengeToken");
|
|
477
|
+
const token = await getTokenFromGlobal("getChallengeToken");
|
|
478
|
+
this.recordFetchSuccess();
|
|
479
|
+
this.prefetchNextScript();
|
|
480
|
+
return token;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
this.reportError(
|
|
483
|
+
error,
|
|
484
|
+
{
|
|
485
|
+
deflect_sdk_error: "true",
|
|
486
|
+
method: "executeScript",
|
|
487
|
+
stage: "load_or_execute",
|
|
488
|
+
action_id: this.config.actionId
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
hasCache: this.scriptCache !== null,
|
|
492
|
+
hasWarmupError: this.hasWarmupError
|
|
493
|
+
}
|
|
494
|
+
);
|
|
495
|
+
throw error;
|
|
496
|
+
} finally {
|
|
497
|
+
if (scriptElement) {
|
|
498
|
+
cleanup(blobUrl, scriptElement, "getChallengeToken");
|
|
499
|
+
} else {
|
|
500
|
+
URL.revokeObjectURL(blobUrl);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/** Manually trigger warmup/prefetch */
|
|
505
|
+
async warmup() {
|
|
506
|
+
try {
|
|
507
|
+
if (this.config.prefetch === false) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
await this.tryWarmup();
|
|
511
|
+
return this.scriptCache !== null;
|
|
512
|
+
} catch {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/** Clear the cached script */
|
|
517
|
+
clearCache() {
|
|
518
|
+
this.scriptCache = null;
|
|
519
|
+
}
|
|
520
|
+
/** Reset error state to allow retries */
|
|
521
|
+
resetErrorState() {
|
|
522
|
+
this.hasWarmupError = false;
|
|
523
|
+
this.consecutiveErrorCount = 0;
|
|
524
|
+
this.lastErrorTime = 0;
|
|
525
|
+
this.getTokenPromise = null;
|
|
526
|
+
}
|
|
527
|
+
/** Get current rate limit and error status for debugging */
|
|
528
|
+
getStatus() {
|
|
529
|
+
return {
|
|
530
|
+
actionId: this.config.actionId,
|
|
531
|
+
isRateLimited: this.isRateLimited(),
|
|
532
|
+
isInCooldown: this.isInErrorCooldown(),
|
|
533
|
+
consecutiveErrors: this.consecutiveErrorCount,
|
|
534
|
+
fetchesInWindow: this.fetchCountInWindow,
|
|
535
|
+
canFetch: !this.isRateLimited() && !this.isInErrorCooldown() && !this.hasExceededMaxErrors()
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
/** Inject token into a form and submit */
|
|
539
|
+
async injectToken(event) {
|
|
540
|
+
if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
|
|
541
|
+
throw new Error("injectToken: must be called from a form submit event");
|
|
542
|
+
}
|
|
543
|
+
event.preventDefault();
|
|
544
|
+
const form = event.target;
|
|
545
|
+
const token = await this.getToken();
|
|
546
|
+
Array.from(form.querySelectorAll('input[name="deflect_token"]')).forEach(
|
|
547
|
+
(el) => el.remove()
|
|
548
|
+
);
|
|
549
|
+
const hidden = document.createElement("input");
|
|
550
|
+
hidden.type = "hidden";
|
|
551
|
+
hidden.name = "deflect_token";
|
|
552
|
+
hidden.value = token;
|
|
553
|
+
form.appendChild(hidden);
|
|
554
|
+
form.submit();
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
var Deflect = class {
|
|
558
|
+
constructor() {
|
|
559
|
+
this.config = null;
|
|
560
|
+
this.scriptCache = null;
|
|
561
|
+
this.isWarmupInProgress = false;
|
|
562
|
+
this.hasWarmupError = false;
|
|
563
|
+
this.warmupPromise = null;
|
|
564
|
+
// Guards against infinite loading
|
|
565
|
+
this.getTokenPromise = null;
|
|
566
|
+
this.lastErrorTime = 0;
|
|
567
|
+
this.consecutiveErrorCount = 0;
|
|
568
|
+
this.fetchCountInWindow = 0;
|
|
569
|
+
this.fetchWindowStart = 0;
|
|
570
|
+
this.initializeGlobalState();
|
|
571
|
+
this.pulseReporter = new pulse_default(this.resolvePulseConfig());
|
|
572
|
+
this.setupAutomaticWarmup();
|
|
573
|
+
}
|
|
574
|
+
static {
|
|
575
|
+
__name(this, "Deflect");
|
|
576
|
+
}
|
|
577
|
+
initializeGlobalState() {
|
|
578
|
+
if (typeof window === "undefined") return;
|
|
579
|
+
window.Deflect = window.Deflect || {};
|
|
580
|
+
}
|
|
581
|
+
setupAutomaticWarmup() {
|
|
582
|
+
if (typeof window === "undefined") return;
|
|
583
|
+
if (document.readyState === "loading") {
|
|
584
|
+
document.addEventListener("DOMContentLoaded", () => this.tryWarmup());
|
|
585
|
+
} else {
|
|
586
|
+
setTimeout(() => this.tryWarmup(), 100);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
resolvePulseConfig() {
|
|
590
|
+
const runtimeConfig = typeof window !== "undefined" && typeof window.Deflect === "object" && window.Deflect?.pulseConfig && typeof window.Deflect.pulseConfig === "object" ? window.Deflect.pulseConfig : {};
|
|
591
|
+
return {
|
|
592
|
+
...runtimeConfig,
|
|
593
|
+
environment: runtimeConfig.environment || (typeof window !== "undefined" ? window.location.hostname : void 0)
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
reportError(error, tags, context) {
|
|
597
|
+
this.pulseReporter.captureException(error, { tags, context });
|
|
598
|
+
}
|
|
599
|
+
/** Check if we're in error cooldown period */
|
|
600
|
+
isInErrorCooldown() {
|
|
601
|
+
if (this.consecutiveErrorCount === 0) return false;
|
|
602
|
+
const backoffMs = Math.min(
|
|
603
|
+
ERROR_COOLDOWN_MS * Math.pow(2, this.consecutiveErrorCount - 1),
|
|
604
|
+
3e4
|
|
605
|
+
// Max 30 second backoff
|
|
606
|
+
);
|
|
607
|
+
return Date.now() - this.lastErrorTime < backoffMs;
|
|
608
|
+
}
|
|
609
|
+
/** Check if we've exceeded the rate limit */
|
|
610
|
+
isRateLimited() {
|
|
611
|
+
const now = Date.now();
|
|
612
|
+
if (now - this.fetchWindowStart > FETCH_WINDOW_MS) {
|
|
613
|
+
this.fetchCountInWindow = 0;
|
|
614
|
+
this.fetchWindowStart = now;
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
return this.fetchCountInWindow >= MAX_FETCHES_PER_WINDOW;
|
|
618
|
+
}
|
|
619
|
+
/** Check if we've hit max consecutive errors */
|
|
620
|
+
hasExceededMaxErrors() {
|
|
621
|
+
return this.consecutiveErrorCount >= MAX_CONSECUTIVE_ERRORS;
|
|
622
|
+
}
|
|
623
|
+
/** Record a successful fetch - resets error state */
|
|
624
|
+
recordFetchSuccess() {
|
|
625
|
+
this.consecutiveErrorCount = 0;
|
|
626
|
+
this.lastErrorTime = 0;
|
|
627
|
+
}
|
|
628
|
+
/** Record a failed fetch - increments error tracking */
|
|
629
|
+
recordFetchError() {
|
|
630
|
+
this.consecutiveErrorCount++;
|
|
631
|
+
this.lastErrorTime = Date.now();
|
|
632
|
+
}
|
|
633
|
+
/** Increment fetch counter for rate limiting */
|
|
634
|
+
incrementFetchCount() {
|
|
635
|
+
const now = Date.now();
|
|
636
|
+
if (now - this.fetchWindowStart > FETCH_WINDOW_MS) {
|
|
637
|
+
this.fetchCountInWindow = 0;
|
|
638
|
+
this.fetchWindowStart = now;
|
|
639
|
+
}
|
|
640
|
+
this.fetchCountInWindow++;
|
|
641
|
+
}
|
|
642
|
+
async tryWarmup() {
|
|
643
|
+
if (!this.config?.actionId || this.config.prefetch === false || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (this.isRateLimited() || this.isInErrorCooldown() || this.hasExceededMaxErrors()) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
this.validateActionId(this.config.actionId);
|
|
650
|
+
this.isWarmupInProgress = true;
|
|
651
|
+
this.incrementFetchCount();
|
|
652
|
+
this.warmupPromise = (async () => {
|
|
653
|
+
try {
|
|
654
|
+
this.scriptCache = await fetchScript(
|
|
655
|
+
this.config.actionId,
|
|
656
|
+
this.config.scriptUrl,
|
|
657
|
+
this.config.extraArgs
|
|
658
|
+
);
|
|
659
|
+
this.hasWarmupError = false;
|
|
660
|
+
this.recordFetchSuccess();
|
|
661
|
+
} catch {
|
|
662
|
+
this.hasWarmupError = true;
|
|
663
|
+
this.recordFetchError();
|
|
664
|
+
} finally {
|
|
665
|
+
this.isWarmupInProgress = false;
|
|
666
|
+
}
|
|
667
|
+
})();
|
|
668
|
+
await this.warmupPromise.catch(() => {
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
validateActionId(actionId) {
|
|
672
|
+
const sanitized = actionId.trim();
|
|
673
|
+
const validPattern = /^[a-zA-Z0-9/_-]+$/;
|
|
674
|
+
if (!validPattern.test(sanitized)) {
|
|
675
|
+
throw new Error("Invalid actionId format: contains disallowed characters");
|
|
676
|
+
}
|
|
677
|
+
return encodeURIComponent(sanitized);
|
|
678
|
+
}
|
|
679
|
+
prefetchNextScript() {
|
|
680
|
+
if (!this.hasWarmupError && this.config?.prefetch !== false) {
|
|
681
|
+
this.tryWarmup().catch(() => {
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
isTestMode() {
|
|
686
|
+
if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
|
|
687
|
+
throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
|
|
688
|
+
}
|
|
689
|
+
return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
|
|
690
|
+
}
|
|
691
|
+
configure(params) {
|
|
692
|
+
try {
|
|
693
|
+
if (!params.actionId?.trim()) {
|
|
694
|
+
throw new Error("actionId is required and cannot be empty");
|
|
695
|
+
}
|
|
696
|
+
this.validateActionId(params.actionId);
|
|
697
|
+
if (this.config?.actionId === params.actionId && this.config.prefetch === params.prefetch && this.config.scriptUrl === params.scriptUrl) {
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const actionIdChanged = this.config?.actionId !== params.actionId;
|
|
701
|
+
if (actionIdChanged) {
|
|
702
|
+
this.hasWarmupError = false;
|
|
703
|
+
this.consecutiveErrorCount = 0;
|
|
704
|
+
this.lastErrorTime = 0;
|
|
705
|
+
}
|
|
706
|
+
this.scriptCache = null;
|
|
707
|
+
this.getTokenPromise = null;
|
|
708
|
+
this.config = { prefetch: true, ...params };
|
|
709
|
+
if (typeof window !== "undefined") {
|
|
710
|
+
window.Deflect.actionId = params.actionId;
|
|
711
|
+
}
|
|
712
|
+
if (!this.isTestMode() && this.config.prefetch !== false) {
|
|
713
|
+
this.tryWarmup();
|
|
714
|
+
}
|
|
715
|
+
} catch (error) {
|
|
716
|
+
const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
|
|
717
|
+
this.reportError(
|
|
718
|
+
error,
|
|
719
|
+
{
|
|
720
|
+
deflect_sdk_error: "true",
|
|
721
|
+
deflect_user_error: isUserError ? "true" : "false",
|
|
722
|
+
method: "configure",
|
|
723
|
+
action_id: params?.actionId || "unknown"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
actionId: params?.actionId,
|
|
727
|
+
hasCache: this.scriptCache !== null,
|
|
728
|
+
hasWarmupError: this.hasWarmupError
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
throw error;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async getToken() {
|
|
735
|
+
if (this.getTokenPromise) {
|
|
736
|
+
return this.getTokenPromise;
|
|
737
|
+
}
|
|
738
|
+
this.getTokenPromise = this.getTokenInternal();
|
|
739
|
+
try {
|
|
740
|
+
return await this.getTokenPromise;
|
|
741
|
+
} finally {
|
|
742
|
+
this.getTokenPromise = null;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
async getTokenInternal() {
|
|
746
|
+
try {
|
|
747
|
+
if (!this.config?.actionId) {
|
|
748
|
+
throw new Error("Must call configure() before getToken()");
|
|
749
|
+
}
|
|
750
|
+
this.validateActionId(this.config.actionId);
|
|
751
|
+
if (this.isTestMode()) {
|
|
752
|
+
return "TESTTOKEN";
|
|
753
|
+
}
|
|
754
|
+
if (this.hasExceededMaxErrors()) {
|
|
755
|
+
throw new Error(
|
|
756
|
+
`Too many consecutive errors (${this.consecutiveErrorCount}). Call configure() with a new actionId or wait before retrying.`
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
if (this.isRateLimited()) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
`Rate limit exceeded: ${MAX_FETCHES_PER_WINDOW} requests per minute. Please wait before retrying.`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
if (this.isInErrorCooldown()) {
|
|
765
|
+
const backoffMs = Math.min(
|
|
766
|
+
ERROR_COOLDOWN_MS * Math.pow(2, this.consecutiveErrorCount - 1),
|
|
767
|
+
3e4
|
|
768
|
+
);
|
|
769
|
+
const remainingMs = backoffMs - (Date.now() - this.lastErrorTime);
|
|
770
|
+
throw new Error(
|
|
771
|
+
`In error cooldown. Please wait ${Math.ceil(remainingMs / 1e3)} seconds before retrying.`
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
let script;
|
|
775
|
+
if (this.warmupPromise) {
|
|
776
|
+
await this.warmupPromise.catch(() => {
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
if (this.scriptCache && !this.isWarmupInProgress) {
|
|
780
|
+
script = this.scriptCache;
|
|
781
|
+
this.scriptCache = null;
|
|
782
|
+
} else {
|
|
783
|
+
this.incrementFetchCount();
|
|
784
|
+
script = await fetchScript(
|
|
785
|
+
this.config.actionId,
|
|
786
|
+
this.config.scriptUrl,
|
|
787
|
+
this.config.extraArgs
|
|
788
|
+
);
|
|
789
|
+
}
|
|
790
|
+
return this.executeScript(script);
|
|
791
|
+
} catch (error) {
|
|
792
|
+
const errorMessage = error instanceof Error ? error.message : "";
|
|
793
|
+
const isGuardError = errorMessage.includes("cooldown") || errorMessage.includes("Rate limit") || errorMessage.includes("Too many consecutive");
|
|
794
|
+
if (!isGuardError) {
|
|
795
|
+
this.recordFetchError();
|
|
796
|
+
}
|
|
797
|
+
const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
|
|
798
|
+
this.reportError(
|
|
799
|
+
error,
|
|
800
|
+
{
|
|
801
|
+
deflect_sdk_error: "true",
|
|
802
|
+
deflect_user_error: isUserError ? "true" : "false",
|
|
803
|
+
method: "getToken",
|
|
804
|
+
action_id: this.config?.actionId || "unknown",
|
|
805
|
+
has_cache: this.scriptCache !== null ? "true" : "false",
|
|
806
|
+
has_warmup_error: this.hasWarmupError ? "true" : "false",
|
|
807
|
+
consecutive_errors: this.consecutiveErrorCount.toString(),
|
|
808
|
+
stage: "call"
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
actionId: this.config?.actionId,
|
|
812
|
+
hasCache: this.scriptCache !== null,
|
|
813
|
+
hasWarmupError: this.hasWarmupError,
|
|
814
|
+
consecutiveErrorCount: this.consecutiveErrorCount,
|
|
815
|
+
fetchCountInWindow: this.fetchCountInWindow
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
throw error;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
async executeScript(script) {
|
|
822
|
+
if (script.sessionId && typeof window !== "undefined") {
|
|
823
|
+
window.Deflect.sessionId = script.sessionId;
|
|
824
|
+
}
|
|
825
|
+
const blobUrl = createBlobUrl(script.content);
|
|
826
|
+
let scriptElement = null;
|
|
827
|
+
try {
|
|
828
|
+
scriptElement = await loadModuleScript(blobUrl);
|
|
829
|
+
await waitForGlobalFunction("getChallengeToken");
|
|
830
|
+
const token = await getTokenFromGlobal("getChallengeToken");
|
|
831
|
+
this.recordFetchSuccess();
|
|
832
|
+
this.prefetchNextScript();
|
|
833
|
+
return token;
|
|
834
|
+
} catch (error) {
|
|
835
|
+
this.reportError(
|
|
836
|
+
error,
|
|
837
|
+
{
|
|
838
|
+
deflect_sdk_error: "true",
|
|
839
|
+
method: "executeScript",
|
|
840
|
+
stage: "load_or_execute",
|
|
841
|
+
action_id: this.config?.actionId || "unknown"
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
hasCache: this.scriptCache !== null,
|
|
845
|
+
hasWarmupError: this.hasWarmupError
|
|
846
|
+
}
|
|
847
|
+
);
|
|
848
|
+
throw error;
|
|
849
|
+
} finally {
|
|
850
|
+
if (scriptElement) {
|
|
851
|
+
cleanup(blobUrl, scriptElement, "getChallengeToken");
|
|
852
|
+
} else {
|
|
853
|
+
URL.revokeObjectURL(blobUrl);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
async warmup() {
|
|
858
|
+
if (!this.config?.actionId) {
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
try {
|
|
862
|
+
if (this.config.prefetch === false) {
|
|
863
|
+
return false;
|
|
864
|
+
}
|
|
865
|
+
await this.tryWarmup();
|
|
866
|
+
return this.scriptCache !== null;
|
|
867
|
+
} catch {
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
clearCache() {
|
|
872
|
+
this.scriptCache = null;
|
|
873
|
+
}
|
|
874
|
+
/** Reset error state to allow retries. Use after fixing configuration issues. */
|
|
875
|
+
resetErrorState() {
|
|
876
|
+
this.hasWarmupError = false;
|
|
877
|
+
this.consecutiveErrorCount = 0;
|
|
878
|
+
this.lastErrorTime = 0;
|
|
879
|
+
this.getTokenPromise = null;
|
|
880
|
+
}
|
|
881
|
+
/** Get current rate limit and error status for debugging */
|
|
882
|
+
getStatus() {
|
|
883
|
+
return {
|
|
884
|
+
isRateLimited: this.isRateLimited(),
|
|
885
|
+
isInCooldown: this.isInErrorCooldown(),
|
|
886
|
+
consecutiveErrors: this.consecutiveErrorCount,
|
|
887
|
+
fetchesInWindow: this.fetchCountInWindow,
|
|
888
|
+
canFetch: !this.isRateLimited() && !this.isInErrorCooldown() && !this.hasExceededMaxErrors()
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
async injectToken(event) {
|
|
892
|
+
if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
|
|
893
|
+
throw new Error("injectToken: must be called from a form submit event");
|
|
894
|
+
}
|
|
895
|
+
event.preventDefault();
|
|
896
|
+
const form = event.target;
|
|
897
|
+
const token = await this.getToken();
|
|
898
|
+
Array.from(form.querySelectorAll('input[name="deflect_token"]')).forEach(
|
|
899
|
+
(el) => el.remove()
|
|
900
|
+
);
|
|
901
|
+
const hidden = document.createElement("input");
|
|
902
|
+
hidden.type = "hidden";
|
|
903
|
+
hidden.name = "deflect_token";
|
|
904
|
+
hidden.value = token;
|
|
905
|
+
form.appendChild(hidden);
|
|
906
|
+
form.submit();
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Create a new Deflect client instance with its own configuration.
|
|
910
|
+
* Use this when you need multiple actionIds on the same page.
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* const loginClient = Deflect.createClient({ actionId: 'login-form' });
|
|
914
|
+
* const signupClient = Deflect.createClient({ actionId: 'signup-form' });
|
|
915
|
+
*
|
|
916
|
+
* // Each client manages its own state
|
|
917
|
+
* const loginToken = await loginClient.getToken();
|
|
918
|
+
* const signupToken = await signupClient.getToken();
|
|
919
|
+
*/
|
|
920
|
+
createClient(config) {
|
|
921
|
+
return new DeflectClient(config, this.pulseReporter);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
var DeflectInstance = new Deflect();
|
|
925
|
+
if (typeof window !== "undefined") {
|
|
926
|
+
window.Deflect = DeflectInstance;
|
|
927
|
+
}
|
|
928
|
+
var src_default = DeflectInstance;
|
|
929
|
+
})();
|