@deflectbot/deflect-sdk 1.4.2 → 1.4.4

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/README.md CHANGED
@@ -31,48 +31,52 @@ Use `@deflect-sdk@latest` to always use the latest version, or specify a version
31
31
  <script src="https://cdn.jsdelivr.net/npm/@deflectbot/deflect-sdk@latest/dist/index.min.js"></script>
32
32
  ```
33
33
 
34
- ## Configuration
34
+ ## Usage (instance-based)
35
35
 
36
- Deflect works through Defense Actions™, which allows you to either setup a global configuration one-time to work across your entire application, or individual and more customized Defense Actions™ for each protected endpoint.
37
-
38
- #### One-time global initialization.
36
+ The SDK uses lightweight clients so you can create multiple independent configs (one per actionId) without global state conflicts.
39
37
 
40
38
  ```ts
41
- Deflect.configure({
39
+ import Deflect, { DeflectConfig } from "@deflectbot/deflect-sdk";
40
+
41
+ const client = Deflect.createClient({
42
42
  actionId: "<ACTION_ID>",
43
- });
43
+ // optional
44
+ scriptUrl: "https://custom-host/main.js", // overrides script origin
45
+ prefetch: true, // default
46
+ extraArgs: { debug: true }, // appended as query params => &debug=true
47
+ } satisfies DeflectConfig);
48
+
49
+ const token = await client.getToken();
44
50
  ```
45
51
 
46
- #### To get a token to pass to a protected endpoint
52
+ ### Multiple clients on the same page
47
53
 
48
54
  ```ts
49
- const token = await Deflect.getToken(); // returns token as string
50
- ```
55
+ const loginClient = Deflect.createClient({ actionId: "login" });
56
+ const signupClient = Deflect.createClient({ actionId: "signup" });
51
57
 
52
- #### Helper for form submit
58
+ const loginToken = await loginClient.getToken();
59
+ const signupToken = await signupClient.getToken();
60
+ ```
53
61
 
54
- If you're using forms like this:
62
+ ### Form helper with an instance
55
63
 
56
64
  ```html
57
- <form action="/login" method="POST">
65
+ <form action="/login" method="POST" onsubmit="return loginClient.injectToken(event)">
58
66
  <input name="username" />
59
67
  <input name="password" type="password" />
60
68
  <button type="submit">Login</button>
61
69
  </form>
70
+
71
+ <script type="module">
72
+ import Deflect from "https://cdn.jsdelivr.net/npm/@deflectbot/deflect-sdk@latest/dist/index.esm.js";
73
+ const loginClient = Deflect.createClient({ actionId: "login" });
74
+ window.loginClient = loginClient;
75
+ </script>
62
76
  ```
63
77
 
64
- You can use the helper as shown:
78
+ `injectToken(event)` fetches a token, injects it as a hidden `deflect_token` input, and submits the form.
65
79
 
66
- ```html
67
- <form
68
- action="/login"
69
- method="POST"
70
- onsubmit="return Deflect.injectToken(event)"
71
- >
72
- <input name="username" />
73
- <input name="password" type="password" />
74
- <button type="submit">Login</button>
75
- </form>
76
- ```
80
+ ### Legacy global (still available, but prefer clients)
77
81
 
78
- `injectToken()` will fetch a token and insert it as a hidden input named `deflect_token` into your form. Use it directly in your form's onsubmit attribute:
82
+ The previous `Deflect.configure`/`Deflect.getToken` singleton still works for single-action setups, but the recommended approach is to create per-action clients as shown above.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deflectbot/deflect-sdk",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
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",
package/dist/index.d.ts DELETED
@@ -1,38 +0,0 @@
1
- interface DeflectConfig {
2
- /** The action ID to fetch the script for. */
3
- actionId: string;
4
- /** Override URL for the script. For staging/enterprise customers */
5
- scriptUrl?: string;
6
- /** Enable/disable automatic warmup/prefetch. Defaults to true. */
7
- prefetch?: boolean;
8
- }
9
- declare global {
10
- interface Window {
11
- Deflect: any;
12
- }
13
- }
14
- declare class Deflect {
15
- private config;
16
- private scriptCache;
17
- private isWarmupInProgress;
18
- private hasWarmupError;
19
- private warmupPromise;
20
- private pulseReporter;
21
- constructor();
22
- private initializeGlobalState;
23
- private setupAutomaticWarmup;
24
- private resolvePulseConfig;
25
- private reportError;
26
- private tryWarmup;
27
- private validateActionId;
28
- private prefetchNextScript;
29
- private isTestMode;
30
- configure(params: DeflectConfig): void;
31
- getToken(): Promise<string>;
32
- private executeScript;
33
- warmup(): Promise<boolean>;
34
- clearCache(): void;
35
- injectToken(event: SubmitEvent): Promise<void>;
36
- }
37
- declare const DeflectInstance: Deflect;
38
- export default DeflectInstance;
package/dist/index.esm.js DELETED
@@ -1,233 +0,0 @@
1
- import PulseReporter from "./pulse";
2
- import { fetchScript } from "./scriptClient";
3
- import { createBlobUrl, loadModuleScript, waitForGlobalFunction, getTokenFromGlobal, cleanup } from "./scriptRunner";
4
- class Deflect {
5
- constructor() {
6
- this.config = null;
7
- this.scriptCache = null;
8
- this.isWarmupInProgress = false;
9
- this.hasWarmupError = false;
10
- this.warmupPromise = null;
11
- this.initializeGlobalState();
12
- this.pulseReporter = new PulseReporter(this.resolvePulseConfig());
13
- this.setupAutomaticWarmup();
14
- }
15
- initializeGlobalState() {
16
- if (typeof window === "undefined") return;
17
- window.Deflect = window.Deflect || {};
18
- }
19
- setupAutomaticWarmup() {
20
- if (typeof window === "undefined") return;
21
- if (document.readyState === "loading") {
22
- document.addEventListener("DOMContentLoaded", () => this.tryWarmup());
23
- } else {
24
- setTimeout(() => this.tryWarmup(), 100);
25
- }
26
- }
27
- resolvePulseConfig() {
28
- const runtimeConfig = typeof window !== "undefined" && typeof window.Deflect === "object" && window.Deflect?.pulseConfig && typeof window.Deflect.pulseConfig === "object" ? window.Deflect.pulseConfig : {};
29
- return {
30
- ...runtimeConfig,
31
- environment: runtimeConfig.environment || (typeof window !== "undefined" ? window.location.hostname : void 0)
32
- };
33
- }
34
- reportError(error, tags, context) {
35
- this.pulseReporter.captureException(error, { tags, context });
36
- }
37
- async tryWarmup() {
38
- if (!this.config?.actionId || this.config.prefetch === false || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
39
- return;
40
- }
41
- this.validateActionId(this.config.actionId);
42
- this.isWarmupInProgress = true;
43
- this.warmupPromise = (async () => {
44
- try {
45
- this.scriptCache = await fetchScript(this.config.actionId, this.config.scriptUrl);
46
- this.hasWarmupError = false;
47
- } catch {
48
- this.hasWarmupError = true;
49
- } finally {
50
- this.isWarmupInProgress = false;
51
- }
52
- })();
53
- await this.warmupPromise.catch(() => {
54
- });
55
- }
56
- validateActionId(actionId) {
57
- const sanitized = actionId.trim();
58
- const validPattern = /^[a-zA-Z0-9/_-]+$/;
59
- if (!validPattern.test(sanitized)) {
60
- throw new Error("Invalid actionId format: contains disallowed characters");
61
- }
62
- return encodeURIComponent(sanitized);
63
- }
64
- prefetchNextScript() {
65
- if (!this.hasWarmupError && this.config?.prefetch !== false) {
66
- this.tryWarmup().catch(() => {
67
- });
68
- }
69
- }
70
- isTestMode() {
71
- if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
72
- throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
73
- }
74
- return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
75
- }
76
- configure(params) {
77
- try {
78
- if (!params.actionId?.trim()) {
79
- throw new Error("actionId is required and cannot be empty");
80
- }
81
- this.validateActionId(params.actionId);
82
- if (this.config?.actionId === params.actionId && this.config.prefetch === params.prefetch) {
83
- return;
84
- }
85
- this.hasWarmupError = false;
86
- this.scriptCache = null;
87
- this.config = { prefetch: true, ...params };
88
- if (typeof window !== "undefined") {
89
- window.Deflect.actionId = params.actionId;
90
- }
91
- if (!this.isTestMode() && this.config.prefetch !== false) {
92
- this.tryWarmup();
93
- }
94
- } catch (error) {
95
- const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
96
- this.reportError(
97
- error,
98
- {
99
- deflect_sdk_error: "true",
100
- deflect_user_error: isUserError ? "true" : "false",
101
- method: "configure",
102
- action_id: params?.actionId || "unknown"
103
- },
104
- {
105
- actionId: params?.actionId,
106
- hasCache: this.scriptCache !== null,
107
- hasWarmupError: this.hasWarmupError
108
- }
109
- );
110
- throw error;
111
- }
112
- }
113
- async getToken() {
114
- try {
115
- if (!this.config?.actionId) {
116
- throw new Error("Must call configure() before getToken()");
117
- }
118
- this.validateActionId(this.config.actionId);
119
- if (this.isTestMode()) {
120
- return "TESTTOKEN";
121
- }
122
- let script;
123
- if (this.warmupPromise) {
124
- await this.warmupPromise.catch(() => {
125
- });
126
- }
127
- if (this.scriptCache && !this.isWarmupInProgress) {
128
- script = this.scriptCache;
129
- this.scriptCache = null;
130
- } else {
131
- script = await fetchScript(this.config.actionId, this.config.scriptUrl);
132
- }
133
- return this.executeScript(script);
134
- } catch (error) {
135
- const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
136
- this.reportError(
137
- error,
138
- {
139
- deflect_sdk_error: "true",
140
- deflect_user_error: isUserError ? "true" : "false",
141
- method: "getToken",
142
- action_id: this.config?.actionId || "unknown",
143
- has_cache: this.scriptCache !== null ? "true" : "false",
144
- has_warmup_error: this.hasWarmupError ? "true" : "false",
145
- stage: "call"
146
- },
147
- {
148
- actionId: this.config?.actionId,
149
- hasCache: this.scriptCache !== null,
150
- hasWarmupError: this.hasWarmupError
151
- }
152
- );
153
- throw error;
154
- }
155
- }
156
- async executeScript(script) {
157
- if (script.sessionId && typeof window !== "undefined") {
158
- window.Deflect.sessionId = script.sessionId;
159
- }
160
- const blobUrl = createBlobUrl(script.content);
161
- let scriptElement = null;
162
- try {
163
- scriptElement = await loadModuleScript(blobUrl);
164
- await waitForGlobalFunction("getChallengeToken");
165
- const token = await getTokenFromGlobal("getChallengeToken");
166
- this.prefetchNextScript();
167
- return token;
168
- } catch (error) {
169
- this.reportError(
170
- error,
171
- {
172
- deflect_sdk_error: "true",
173
- method: "executeScript",
174
- stage: "load_or_execute",
175
- action_id: this.config?.actionId || "unknown"
176
- },
177
- {
178
- hasCache: this.scriptCache !== null,
179
- hasWarmupError: this.hasWarmupError
180
- }
181
- );
182
- throw error;
183
- } finally {
184
- if (scriptElement) {
185
- cleanup(blobUrl, scriptElement, "getChallengeToken");
186
- } else {
187
- URL.revokeObjectURL(blobUrl);
188
- }
189
- }
190
- }
191
- async warmup() {
192
- if (!this.config?.actionId) {
193
- return false;
194
- }
195
- try {
196
- if (this.config.prefetch === false) {
197
- return false;
198
- }
199
- await this.tryWarmup();
200
- return this.scriptCache !== null;
201
- } catch {
202
- return false;
203
- }
204
- }
205
- clearCache() {
206
- this.scriptCache = null;
207
- }
208
- async injectToken(event) {
209
- if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
210
- throw new Error("injectToken: must be called from a form submit event");
211
- }
212
- event.preventDefault();
213
- const form = event.target;
214
- const token = await this.getToken();
215
- Array.from(form.querySelectorAll('input[name="deflect_token"]')).forEach(
216
- (el) => el.remove()
217
- );
218
- const hidden = document.createElement("input");
219
- hidden.type = "hidden";
220
- hidden.name = "deflect_token";
221
- hidden.value = token;
222
- form.appendChild(hidden);
223
- form.submit();
224
- }
225
- }
226
- const DeflectInstance = new Deflect();
227
- if (typeof window !== "undefined") {
228
- window.Deflect = DeflectInstance;
229
- }
230
- var src_default = DeflectInstance;
231
- export {
232
- src_default as default
233
- };
package/dist/index.js DELETED
@@ -1,467 +0,0 @@
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) {
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
-
236
- // src/index.ts
237
- var Deflect = class {
238
- constructor() {
239
- this.config = null;
240
- this.scriptCache = null;
241
- this.isWarmupInProgress = false;
242
- this.hasWarmupError = false;
243
- this.warmupPromise = null;
244
- this.initializeGlobalState();
245
- this.pulseReporter = new pulse_default(this.resolvePulseConfig());
246
- this.setupAutomaticWarmup();
247
- }
248
- static {
249
- __name(this, "Deflect");
250
- }
251
- initializeGlobalState() {
252
- if (typeof window === "undefined") return;
253
- window.Deflect = window.Deflect || {};
254
- }
255
- setupAutomaticWarmup() {
256
- if (typeof window === "undefined") return;
257
- if (document.readyState === "loading") {
258
- document.addEventListener("DOMContentLoaded", () => this.tryWarmup());
259
- } else {
260
- setTimeout(() => this.tryWarmup(), 100);
261
- }
262
- }
263
- resolvePulseConfig() {
264
- const runtimeConfig = typeof window !== "undefined" && typeof window.Deflect === "object" && window.Deflect?.pulseConfig && typeof window.Deflect.pulseConfig === "object" ? window.Deflect.pulseConfig : {};
265
- return {
266
- ...runtimeConfig,
267
- environment: runtimeConfig.environment || (typeof window !== "undefined" ? window.location.hostname : void 0)
268
- };
269
- }
270
- reportError(error, tags, context) {
271
- this.pulseReporter.captureException(error, { tags, context });
272
- }
273
- async tryWarmup() {
274
- if (!this.config?.actionId || this.config.prefetch === false || this.isWarmupInProgress || this.scriptCache || this.hasWarmupError) {
275
- return;
276
- }
277
- this.validateActionId(this.config.actionId);
278
- this.isWarmupInProgress = true;
279
- this.warmupPromise = (async () => {
280
- try {
281
- this.scriptCache = await fetchScript(this.config.actionId, this.config.scriptUrl);
282
- this.hasWarmupError = false;
283
- } catch {
284
- this.hasWarmupError = true;
285
- } finally {
286
- this.isWarmupInProgress = false;
287
- }
288
- })();
289
- await this.warmupPromise.catch(() => {
290
- });
291
- }
292
- validateActionId(actionId) {
293
- const sanitized = actionId.trim();
294
- const validPattern = /^[a-zA-Z0-9/_-]+$/;
295
- if (!validPattern.test(sanitized)) {
296
- throw new Error("Invalid actionId format: contains disallowed characters");
297
- }
298
- return encodeURIComponent(sanitized);
299
- }
300
- prefetchNextScript() {
301
- if (!this.hasWarmupError && this.config?.prefetch !== false) {
302
- this.tryWarmup().catch(() => {
303
- });
304
- }
305
- }
306
- isTestMode() {
307
- if (this.config?.actionId === "PULSE_TEST" || this.config?.actionId === "SENTRY_TEST") {
308
- throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");
309
- }
310
- return this.config?.actionId === "t/FFFFFFFFFFFFF/111111111" || this.config?.actionId === "t/FFFFFFFFFFFFF/000000000";
311
- }
312
- configure(params) {
313
- try {
314
- if (!params.actionId?.trim()) {
315
- throw new Error("actionId is required and cannot be empty");
316
- }
317
- this.validateActionId(params.actionId);
318
- if (this.config?.actionId === params.actionId && this.config.prefetch === params.prefetch) {
319
- return;
320
- }
321
- this.hasWarmupError = false;
322
- this.scriptCache = null;
323
- this.config = { prefetch: true, ...params };
324
- if (typeof window !== "undefined") {
325
- window.Deflect.actionId = params.actionId;
326
- }
327
- if (!this.isTestMode() && this.config.prefetch !== false) {
328
- this.tryWarmup();
329
- }
330
- } catch (error) {
331
- const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
332
- this.reportError(
333
- error,
334
- {
335
- deflect_sdk_error: "true",
336
- deflect_user_error: isUserError ? "true" : "false",
337
- method: "configure",
338
- action_id: params?.actionId || "unknown"
339
- },
340
- {
341
- actionId: params?.actionId,
342
- hasCache: this.scriptCache !== null,
343
- hasWarmupError: this.hasWarmupError
344
- }
345
- );
346
- throw error;
347
- }
348
- }
349
- async getToken() {
350
- try {
351
- if (!this.config?.actionId) {
352
- throw new Error("Must call configure() before getToken()");
353
- }
354
- this.validateActionId(this.config.actionId);
355
- if (this.isTestMode()) {
356
- return "TESTTOKEN";
357
- }
358
- let script;
359
- if (this.warmupPromise) {
360
- await this.warmupPromise.catch(() => {
361
- });
362
- }
363
- if (this.scriptCache && !this.isWarmupInProgress) {
364
- script = this.scriptCache;
365
- this.scriptCache = null;
366
- } else {
367
- script = await fetchScript(this.config.actionId, this.config.scriptUrl);
368
- }
369
- return this.executeScript(script);
370
- } catch (error) {
371
- const isUserError = error && typeof error === "object" && "isUserError" in error && error.isUserError === true;
372
- this.reportError(
373
- error,
374
- {
375
- deflect_sdk_error: "true",
376
- deflect_user_error: isUserError ? "true" : "false",
377
- method: "getToken",
378
- action_id: this.config?.actionId || "unknown",
379
- has_cache: this.scriptCache !== null ? "true" : "false",
380
- has_warmup_error: this.hasWarmupError ? "true" : "false",
381
- stage: "call"
382
- },
383
- {
384
- actionId: this.config?.actionId,
385
- hasCache: this.scriptCache !== null,
386
- hasWarmupError: this.hasWarmupError
387
- }
388
- );
389
- throw error;
390
- }
391
- }
392
- async executeScript(script) {
393
- if (script.sessionId && typeof window !== "undefined") {
394
- window.Deflect.sessionId = script.sessionId;
395
- }
396
- const blobUrl = createBlobUrl(script.content);
397
- let scriptElement = null;
398
- try {
399
- scriptElement = await loadModuleScript(blobUrl);
400
- await waitForGlobalFunction("getChallengeToken");
401
- const token = await getTokenFromGlobal("getChallengeToken");
402
- this.prefetchNextScript();
403
- return token;
404
- } catch (error) {
405
- this.reportError(
406
- error,
407
- {
408
- deflect_sdk_error: "true",
409
- method: "executeScript",
410
- stage: "load_or_execute",
411
- action_id: this.config?.actionId || "unknown"
412
- },
413
- {
414
- hasCache: this.scriptCache !== null,
415
- hasWarmupError: this.hasWarmupError
416
- }
417
- );
418
- throw error;
419
- } finally {
420
- if (scriptElement) {
421
- cleanup(blobUrl, scriptElement, "getChallengeToken");
422
- } else {
423
- URL.revokeObjectURL(blobUrl);
424
- }
425
- }
426
- }
427
- async warmup() {
428
- if (!this.config?.actionId) {
429
- return false;
430
- }
431
- try {
432
- if (this.config.prefetch === false) {
433
- return false;
434
- }
435
- await this.tryWarmup();
436
- return this.scriptCache !== null;
437
- } catch {
438
- return false;
439
- }
440
- }
441
- clearCache() {
442
- this.scriptCache = null;
443
- }
444
- async injectToken(event) {
445
- if (!event || !event.target || !(event.target instanceof HTMLFormElement)) {
446
- throw new Error("injectToken: must be called from a form submit event");
447
- }
448
- event.preventDefault();
449
- const form = event.target;
450
- const token = await this.getToken();
451
- Array.from(form.querySelectorAll('input[name="deflect_token"]')).forEach(
452
- (el) => el.remove()
453
- );
454
- const hidden = document.createElement("input");
455
- hidden.type = "hidden";
456
- hidden.name = "deflect_token";
457
- hidden.value = token;
458
- form.appendChild(hidden);
459
- form.submit();
460
- }
461
- };
462
- var DeflectInstance = new Deflect();
463
- if (typeof window !== "undefined") {
464
- window.Deflect = DeflectInstance;
465
- }
466
- var src_default = DeflectInstance;
467
- })();
package/dist/index.min.js DELETED
@@ -1,2 +0,0 @@
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},h=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:h,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,g=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(g)?g: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.warmupPromise=null;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(){!this.config?.actionId||this.config.prefetch===!1||this.isWarmupInProgress||this.scriptCache||this.hasWarmupError||(this.validateActionId(this.config.actionId),this.isWarmupInProgress=!0,this.warmupPromise=(async()=>{try{this.scriptCache=await f(this.config.actionId,this.config.scriptUrl),this.hasWarmupError=!1}catch{this.hasWarmupError=!0}finally{this.isWarmupInProgress=!1}})(),await this.warmupPromise.catch(()=>{}))}validateActionId(e){let t=e.trim();if(!/^[a-zA-Z0-9/_-]+$/.test(t))throw new Error("Invalid actionId format: contains disallowed characters");return encodeURIComponent(t)}prefetchNextScript(){!this.hasWarmupError&&this.config?.prefetch!==!1&&this.tryWarmup().catch(()=>{})}isTestMode(){if(this.config?.actionId==="PULSE_TEST"||this.config?.actionId==="SENTRY_TEST")throw new Error("PULSE_TEST: This is a test error to verify Pulse integration is working");return this.config?.actionId==="t/FFFFFFFFFFFFF/111111111"||this.config?.actionId==="t/FFFFFFFFFFFFF/000000000"}configure(e){try{if(!e.actionId?.trim())throw new Error("actionId is required and cannot be empty");if(this.validateActionId(e.actionId),this.config?.actionId===e.actionId&&this.config.prefetch===e.prefetch)return;this.hasWarmupError=!1,this.scriptCache=null,this.config={prefetch:!0,...e},typeof window<"u"&&(window.Deflect.actionId=e.actionId),!this.isTestMode()&&this.config.prefetch!==!1&&this.tryWarmup()}catch(t){let 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.warmupPromise&&await this.warmupPromise.catch(()=>{}),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 this.config.prefetch===!1?!1:(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;})();
package/dist/pulse.d.ts DELETED
@@ -1,99 +0,0 @@
1
- export type PulseEventType = "error" | "span" | "transaction" | "log" | "metric";
2
- export type PulseLevel = "fatal" | "error" | "warning" | "info" | "debug";
3
- export interface PulseReporterConfig {
4
- endpoint: string;
5
- projectId: string;
6
- deploymentId: string;
7
- environment?: string;
8
- service?: string;
9
- release?: string;
10
- }
11
- export interface PulseSDKInfo {
12
- name: string;
13
- version: string;
14
- language: string;
15
- integrations?: string[];
16
- }
17
- export interface PulseStackFrame {
18
- filename?: string;
19
- abs_path?: string;
20
- function?: string;
21
- lineno?: number;
22
- colno?: number;
23
- in_app?: boolean;
24
- context_line?: string;
25
- }
26
- export interface PulseExceptionPayload {
27
- type?: string;
28
- value?: string;
29
- stacktrace?: {
30
- frames: PulseStackFrame[];
31
- };
32
- }
33
- export interface PulseRequestInfo {
34
- method?: string;
35
- url?: string;
36
- headers?: Record<string, string>;
37
- route?: string;
38
- status_code?: number;
39
- }
40
- export interface PulseRuntimeInfo {
41
- name?: string;
42
- version?: string;
43
- }
44
- export interface PulseOSInfo {
45
- name?: string;
46
- version?: string;
47
- }
48
- export interface PulseDeviceInfo {
49
- model?: string;
50
- arch?: string;
51
- }
52
- export interface PulseErrorPayload {
53
- level: PulseLevel;
54
- message?: string;
55
- exception?: PulseExceptionPayload;
56
- culprit?: string;
57
- request?: PulseRequestInfo;
58
- runtime?: PulseRuntimeInfo;
59
- os?: PulseOSInfo;
60
- device?: PulseDeviceInfo;
61
- }
62
- export interface PulseEvent {
63
- event_type: PulseEventType;
64
- event_id: string;
65
- deployment_id: string;
66
- project_id: string;
67
- environment?: string;
68
- service?: string;
69
- host?: string;
70
- sdk?: PulseSDKInfo;
71
- timestamp: string;
72
- release?: string;
73
- dist?: string;
74
- deployment?: string;
75
- tags?: Record<string, string>;
76
- context?: Record<string, unknown>;
77
- sample_rate?: number;
78
- fingerprint?: string[];
79
- ingest_key_id?: string;
80
- payload_version?: number;
81
- content_encoding?: string;
82
- error?: PulseErrorPayload;
83
- }
84
- export interface PulseCaptureOptions {
85
- tags?: Record<string, string>;
86
- context?: Record<string, unknown>;
87
- level?: PulseLevel;
88
- }
89
- export declare class PulseReporter {
90
- private readonly config;
91
- private readonly enabled;
92
- constructor(config?: Partial<PulseReporterConfig>);
93
- captureException(error: unknown, options?: PulseCaptureOptions): void;
94
- private buildErrorEvent;
95
- private normalizeError;
96
- private buildExceptionPayload;
97
- private parseStack;
98
- }
99
- export default PulseReporter;
@@ -1,7 +0,0 @@
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;
@@ -1,5 +0,0 @@
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;