blinker-sdk 1.0.0 → 2.0.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/README.md +287 -84
- package/dist/blinker.min.js +452 -1
- package/dist/capture/deduplication.d.ts +26 -0
- package/dist/capture/deduplication.d.ts.map +1 -0
- package/dist/capture/errorHandler.d.ts +5 -0
- package/dist/capture/errorHandler.d.ts.map +1 -0
- package/dist/capture/index.d.ts +4 -0
- package/dist/capture/index.d.ts.map +1 -0
- package/dist/capture/types.d.ts +13 -0
- package/dist/capture/types.d.ts.map +1 -0
- package/dist/context/SessionManager.d.ts +16 -0
- package/dist/context/SessionManager.d.ts.map +1 -0
- package/dist/context/UserContext.d.ts +19 -0
- package/dist/context/UserContext.d.ts.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/types.d.ts +18 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/core/Blinker.d.ts +42 -0
- package/dist/core/Blinker.d.ts.map +1 -0
- package/dist/core/config.d.ts +26 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/types.d.ts +50 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/filters/FilterEngine.d.ts +9 -0
- package/dist/filters/FilterEngine.d.ts.map +1 -0
- package/dist/filters/defaults.d.ts +3 -0
- package/dist/filters/defaults.d.ts.map +1 -0
- package/dist/filters/index.d.ts +4 -0
- package/dist/filters/index.d.ts.map +1 -0
- package/dist/filters/types.d.ts +8 -0
- package/dist/filters/types.d.ts.map +1 -0
- package/dist/index.cjs.js +2098 -125
- package/dist/index.d.ts +7 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +2098 -125
- package/dist/queue/EventQueue.d.ts +21 -0
- package/dist/queue/EventQueue.d.ts.map +1 -0
- package/dist/queue/index.d.ts +4 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/storage.d.ts +16 -0
- package/dist/queue/storage.d.ts.map +1 -0
- package/dist/queue/types.d.ts +19 -0
- package/dist/queue/types.d.ts.map +1 -0
- package/dist/sampling/RateLimiter.d.ts +12 -0
- package/dist/sampling/RateLimiter.d.ts.map +1 -0
- package/dist/sampling/Sampler.d.ts +16 -0
- package/dist/sampling/Sampler.d.ts.map +1 -0
- package/dist/sampling/index.d.ts +4 -0
- package/dist/sampling/index.d.ts.map +1 -0
- package/dist/sampling/types.d.ts +10 -0
- package/dist/sampling/types.d.ts.map +1 -0
- package/dist/transport/index.d.ts +3 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/sender.d.ts +4 -0
- package/dist/transport/sender.d.ts.map +1 -0
- package/dist/transport/types.d.ts +12 -0
- package/dist/transport/types.d.ts.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/hash.d.ts +4 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/widget/Avatar.d.ts +20 -0
- package/dist/widget/Avatar.d.ts.map +1 -0
- package/dist/widget/Dialog.d.ts +25 -0
- package/dist/widget/Dialog.d.ts.map +1 -0
- package/dist/widget/QuestionEngine.d.ts +7 -0
- package/dist/widget/QuestionEngine.d.ts.map +1 -0
- package/dist/widget/Widget.d.ts +28 -0
- package/dist/widget/Widget.d.ts.map +1 -0
- package/dist/widget/index.d.ts +8 -0
- package/dist/widget/index.d.ts.map +1 -0
- package/dist/widget/styles.d.ts +5 -0
- package/dist/widget/styles.d.ts.map +1 -0
- package/dist/widget/types.d.ts +46 -0
- package/dist/widget/types.d.ts.map +1 -0
- package/package.json +2 -3
- package/dist/Blinker.d.ts +0 -47
- package/dist/Blinker.d.ts.map +0 -1
- package/dist/errorHandler.d.ts +0 -19
- package/dist/errorHandler.d.ts.map +0 -1
- package/dist/sender.d.ts +0 -11
- package/dist/sender.d.ts.map +0 -1
- package/dist/types.d.ts +0 -63
- package/dist/types.d.ts.map +0 -1
package/dist/index.cjs.js
CHANGED
|
@@ -21,97 +21,847 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
Blinker: () => Blinker,
|
|
24
|
+
DEFAULT_FILTERS: () => DEFAULT_FILTERS,
|
|
25
|
+
DEFAULT_WIDGET_CONFIG: () => DEFAULT_WIDGET_CONFIG,
|
|
24
26
|
blinker: () => blinker,
|
|
25
27
|
default: () => index_default
|
|
26
28
|
});
|
|
27
29
|
module.exports = __toCommonJS(index_exports);
|
|
28
30
|
|
|
29
|
-
// src/
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// src/core/config.ts
|
|
32
|
+
var BLINKER_API = "https://api.blinker.live";
|
|
33
|
+
var STORAGE_KEYS = {
|
|
34
|
+
EVENT_QUEUE: "blinker_event_queue",
|
|
35
|
+
SESSION: "blinker_session",
|
|
36
|
+
USER_CONTEXT: "blinker_user_context"
|
|
37
|
+
};
|
|
38
|
+
var DEFAULTS = {
|
|
39
|
+
captureErrors: true,
|
|
40
|
+
captureRejections: true,
|
|
41
|
+
enableBatching: true,
|
|
42
|
+
batchSize: 10,
|
|
43
|
+
flushInterval: 5e3,
|
|
44
|
+
enableOfflineStorage: true,
|
|
45
|
+
maxStoredEvents: 100,
|
|
46
|
+
enableDeduplication: true,
|
|
47
|
+
deduplicationWindow: 6e4,
|
|
48
|
+
maxEventsPerMinute: 100,
|
|
49
|
+
sampleRate: 1,
|
|
50
|
+
debug: false
|
|
51
|
+
};
|
|
52
|
+
var TIMING = {
|
|
53
|
+
OFFLINE_RETRY_TIMEOUT: 3e4,
|
|
54
|
+
MIN_FLUSH_INTERVAL: 1e3,
|
|
55
|
+
MAX_FLUSH_INTERVAL: 6e4
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// src/filters/defaults.ts
|
|
59
|
+
var DEFAULT_FILTERS = {
|
|
60
|
+
ignoreHttpCodes: [401, 403],
|
|
61
|
+
ignoreErrorTypes: [],
|
|
62
|
+
ignoreMessagePatterns: [
|
|
63
|
+
"ResizeObserver loop",
|
|
64
|
+
"Script error",
|
|
65
|
+
"ResizeObserver loop completed"
|
|
66
|
+
],
|
|
67
|
+
captureAll: false
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/utils/logger.ts
|
|
71
|
+
var PREFIX = "[Blinker]";
|
|
72
|
+
function createLogger(debug) {
|
|
73
|
+
const noop = () => {
|
|
74
|
+
};
|
|
75
|
+
if (!debug) {
|
|
76
|
+
return {
|
|
77
|
+
log: noop,
|
|
78
|
+
warn: noop,
|
|
79
|
+
error: noop
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
log: (...args) => console.log(PREFIX, ...args),
|
|
84
|
+
warn: (...args) => console.warn(PREFIX, ...args),
|
|
85
|
+
error: (...args) => console.error(PREFIX, ...args)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var globalLogger = createLogger(false);
|
|
89
|
+
function setGlobalLogger(debug) {
|
|
90
|
+
globalLogger = createLogger(debug);
|
|
91
|
+
}
|
|
92
|
+
function getLogger() {
|
|
93
|
+
return globalLogger;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/filters/FilterEngine.ts
|
|
97
|
+
var FilterEngine = class {
|
|
98
|
+
constructor(userFilters) {
|
|
99
|
+
var _a, _b, _c, _d;
|
|
100
|
+
this.filters = {
|
|
101
|
+
ignoreHttpCodes: (_a = userFilters == null ? void 0 : userFilters.ignoreHttpCodes) != null ? _a : DEFAULT_FILTERS.ignoreHttpCodes,
|
|
102
|
+
ignoreErrorTypes: (_b = userFilters == null ? void 0 : userFilters.ignoreErrorTypes) != null ? _b : DEFAULT_FILTERS.ignoreErrorTypes,
|
|
103
|
+
ignoreMessagePatterns: (_c = userFilters == null ? void 0 : userFilters.ignoreMessagePatterns) != null ? _c : DEFAULT_FILTERS.ignoreMessagePatterns,
|
|
104
|
+
captureAll: (_d = userFilters == null ? void 0 : userFilters.captureAll) != null ? _d : DEFAULT_FILTERS.captureAll
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
shouldCapture(message, errorType, httpCode) {
|
|
108
|
+
const logger = getLogger();
|
|
109
|
+
if (this.filters.captureAll) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
if (httpCode !== void 0 && this.filters.ignoreHttpCodes.includes(httpCode)) {
|
|
113
|
+
logger.log("Event filtered out by HTTP code:", httpCode);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (errorType && this.filters.ignoreErrorTypes.includes(errorType)) {
|
|
117
|
+
logger.log("Event filtered out by error type:", errorType);
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
for (const pattern of this.filters.ignoreMessagePatterns) {
|
|
121
|
+
if (message.toLowerCase().includes(pattern.toLowerCase())) {
|
|
122
|
+
logger.log("Event filtered out by message pattern:", pattern);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (httpCode === void 0) {
|
|
127
|
+
const httpCodeMatch = message.match(/(?:HTTP\s*|status[:\s]*)?(\d{3})(?:\s|$|:)/i);
|
|
128
|
+
if (httpCodeMatch) {
|
|
129
|
+
const extractedCode = parseInt(httpCodeMatch[1], 10);
|
|
130
|
+
if (extractedCode >= 100 && extractedCode < 600) {
|
|
131
|
+
if (this.filters.ignoreHttpCodes.includes(extractedCode)) {
|
|
132
|
+
logger.log("Event filtered out by extracted HTTP code:", extractedCode);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
getFilters() {
|
|
141
|
+
return { ...this.filters };
|
|
142
|
+
}
|
|
143
|
+
updateFilters(newFilters) {
|
|
144
|
+
var _a, _b, _c, _d;
|
|
145
|
+
this.filters = {
|
|
146
|
+
ignoreHttpCodes: (_a = newFilters.ignoreHttpCodes) != null ? _a : this.filters.ignoreHttpCodes,
|
|
147
|
+
ignoreErrorTypes: (_b = newFilters.ignoreErrorTypes) != null ? _b : this.filters.ignoreErrorTypes,
|
|
148
|
+
ignoreMessagePatterns: (_c = newFilters.ignoreMessagePatterns) != null ? _c : this.filters.ignoreMessagePatterns,
|
|
149
|
+
captureAll: (_d = newFilters.captureAll) != null ? _d : this.filters.captureAll
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/utils/browser.ts
|
|
155
|
+
function isBrowser() {
|
|
156
|
+
return typeof window !== "undefined";
|
|
157
|
+
}
|
|
158
|
+
function isLocalStorageAvailable() {
|
|
159
|
+
if (!isBrowser()) return false;
|
|
160
|
+
try {
|
|
161
|
+
const testKey = "__blinker_test__";
|
|
162
|
+
localStorage.setItem(testKey, "test");
|
|
163
|
+
localStorage.removeItem(testKey);
|
|
164
|
+
return true;
|
|
165
|
+
} catch (e) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function isSessionStorageAvailable() {
|
|
170
|
+
if (!isBrowser()) return false;
|
|
171
|
+
try {
|
|
172
|
+
const testKey = "__blinker_test__";
|
|
173
|
+
sessionStorage.setItem(testKey, "test");
|
|
174
|
+
sessionStorage.removeItem(testKey);
|
|
175
|
+
return true;
|
|
176
|
+
} catch (e) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function isOnline() {
|
|
181
|
+
var _a;
|
|
182
|
+
if (!isBrowser()) return true;
|
|
183
|
+
return (_a = navigator.onLine) != null ? _a : true;
|
|
184
|
+
}
|
|
185
|
+
function getCurrentUrl() {
|
|
186
|
+
if (!isBrowser()) return void 0;
|
|
187
|
+
return window.location.href;
|
|
188
|
+
}
|
|
189
|
+
function getCurrentPath() {
|
|
190
|
+
if (!isBrowser()) return "unknown";
|
|
191
|
+
return window.location.pathname;
|
|
192
|
+
}
|
|
193
|
+
function getUserAgent() {
|
|
194
|
+
if (!isBrowser()) return void 0;
|
|
195
|
+
return navigator.userAgent;
|
|
196
|
+
}
|
|
197
|
+
function getReferrer() {
|
|
198
|
+
if (!isBrowser()) return void 0;
|
|
199
|
+
return document.referrer || void 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/queue/storage.ts
|
|
203
|
+
var OfflineStorage = class {
|
|
204
|
+
constructor(maxEvents = 100) {
|
|
205
|
+
this.storageKey = STORAGE_KEYS.EVENT_QUEUE;
|
|
206
|
+
this.maxEvents = maxEvents;
|
|
207
|
+
this.available = isLocalStorageAvailable();
|
|
208
|
+
}
|
|
209
|
+
isAvailable() {
|
|
210
|
+
return this.available;
|
|
211
|
+
}
|
|
212
|
+
store(event) {
|
|
213
|
+
if (!this.available) return false;
|
|
214
|
+
const logger = getLogger();
|
|
215
|
+
try {
|
|
216
|
+
const events = this.getAll();
|
|
217
|
+
const storedEvent = {
|
|
218
|
+
event,
|
|
219
|
+
storedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
220
|
+
retries: 0
|
|
221
|
+
};
|
|
222
|
+
events.push(storedEvent);
|
|
223
|
+
while (events.length > this.maxEvents) {
|
|
224
|
+
events.shift();
|
|
225
|
+
}
|
|
226
|
+
localStorage.setItem(this.storageKey, JSON.stringify(events));
|
|
227
|
+
logger.log(`Event stored offline. Total stored: ${events.length}`);
|
|
228
|
+
return true;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger.error("Failed to store event offline:", error);
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
storeMany(eventsToStore) {
|
|
235
|
+
if (!this.available || eventsToStore.length === 0) return 0;
|
|
236
|
+
const logger = getLogger();
|
|
237
|
+
let stored = 0;
|
|
238
|
+
try {
|
|
239
|
+
const events = this.getAll();
|
|
240
|
+
for (const event of eventsToStore) {
|
|
241
|
+
const storedEvent = {
|
|
242
|
+
event,
|
|
243
|
+
storedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
244
|
+
retries: 0
|
|
245
|
+
};
|
|
246
|
+
events.push(storedEvent);
|
|
247
|
+
stored++;
|
|
248
|
+
}
|
|
249
|
+
while (events.length > this.maxEvents) {
|
|
250
|
+
events.shift();
|
|
251
|
+
}
|
|
252
|
+
localStorage.setItem(this.storageKey, JSON.stringify(events));
|
|
253
|
+
logger.log(`Stored ${stored} events offline. Total stored: ${events.length}`);
|
|
254
|
+
return stored;
|
|
255
|
+
} catch (error) {
|
|
256
|
+
logger.error("Failed to store events offline:", error);
|
|
257
|
+
return stored;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
getAll() {
|
|
261
|
+
if (!this.available) return [];
|
|
262
|
+
try {
|
|
263
|
+
const data = localStorage.getItem(this.storageKey);
|
|
264
|
+
if (!data) return [];
|
|
265
|
+
return JSON.parse(data);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
return [];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
retrieveAll() {
|
|
271
|
+
if (!this.available) return [];
|
|
272
|
+
const logger = getLogger();
|
|
273
|
+
try {
|
|
274
|
+
const stored = this.getAll();
|
|
275
|
+
if (stored.length === 0) return [];
|
|
276
|
+
localStorage.removeItem(this.storageKey);
|
|
277
|
+
logger.log(`Retrieved ${stored.length} stored events`);
|
|
278
|
+
return stored.map((s) => s.event);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
logger.error("Failed to retrieve stored events:", error);
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
count() {
|
|
285
|
+
return this.getAll().length;
|
|
286
|
+
}
|
|
287
|
+
clear() {
|
|
288
|
+
if (!this.available) return;
|
|
289
|
+
try {
|
|
290
|
+
localStorage.removeItem(this.storageKey);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// src/transport/sender.ts
|
|
297
|
+
async function sendBatch(apiUrl, token, events) {
|
|
298
|
+
const logger = getLogger();
|
|
299
|
+
const endpoint = `${apiUrl.replace(/\/$/, "")}/events`;
|
|
300
|
+
if (events.length === 0) {
|
|
301
|
+
return { success: true, count: 0 };
|
|
302
|
+
}
|
|
32
303
|
const sendRequest = async () => {
|
|
33
304
|
try {
|
|
305
|
+
const body = events.length === 1 ? formatEventForApi(events[0]) : { events: events.map(formatEventForApi) };
|
|
34
306
|
const response = await fetch(endpoint, {
|
|
35
307
|
method: "POST",
|
|
36
308
|
headers: {
|
|
37
309
|
"Content-Type": "application/json",
|
|
38
310
|
"x-blinker-token": token
|
|
39
311
|
},
|
|
40
|
-
body: JSON.stringify(
|
|
41
|
-
type: event.type,
|
|
42
|
-
message: event.message,
|
|
43
|
-
payload: event.payload || {},
|
|
44
|
-
timestamp: event.timestamp,
|
|
45
|
-
url: event.url,
|
|
46
|
-
userAgent: event.userAgent
|
|
47
|
-
})
|
|
312
|
+
body: JSON.stringify(body)
|
|
48
313
|
});
|
|
49
314
|
if (!response.ok) {
|
|
50
315
|
const errorText = await response.text().catch(() => "Unknown error");
|
|
51
|
-
|
|
52
|
-
console.error(`[Blinker] Failed to send event: ${response.status} - ${errorText}`);
|
|
53
|
-
}
|
|
316
|
+
logger.error(`Failed to send events: ${response.status} - ${errorText}`);
|
|
54
317
|
return { success: false, error: `HTTP ${response.status}: ${errorText}` };
|
|
55
318
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return { success: true };
|
|
319
|
+
logger.log(`Events sent successfully: ${events.length} event(s)`);
|
|
320
|
+
return { success: true, count: events.length };
|
|
60
321
|
} catch (error) {
|
|
61
322
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
62
|
-
|
|
63
|
-
console.error(`[Blinker] Error sending event:`, errorMessage);
|
|
64
|
-
}
|
|
323
|
+
logger.error("Error sending events:", errorMessage);
|
|
65
324
|
return { success: false, error: errorMessage };
|
|
66
325
|
}
|
|
67
326
|
};
|
|
68
327
|
const result = await sendRequest();
|
|
69
|
-
if (!result.success &&
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
328
|
+
if (!result.success && isBrowser() && !isOnline()) {
|
|
329
|
+
logger.log("Offline, waiting for connection to retry...");
|
|
330
|
+
const isBackOnline = await waitForOnline(TIMING.OFFLINE_RETRY_TIMEOUT);
|
|
331
|
+
if (isBackOnline) {
|
|
332
|
+
logger.log("Back online, retrying...");
|
|
333
|
+
return sendRequest();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
function formatEventForApi(event) {
|
|
339
|
+
return {
|
|
340
|
+
type: event.type,
|
|
341
|
+
message: event.message,
|
|
342
|
+
payload: event.payload || {},
|
|
343
|
+
timestamp: event.timestamp,
|
|
344
|
+
url: event.url,
|
|
345
|
+
userAgent: event.userAgent,
|
|
346
|
+
sessionId: event.sessionId,
|
|
347
|
+
userId: event.userId,
|
|
348
|
+
userTraits: event.userTraits,
|
|
349
|
+
context: event.context,
|
|
350
|
+
count: event.count
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
function waitForOnline(timeout) {
|
|
354
|
+
return new Promise((resolve) => {
|
|
355
|
+
if (!isBrowser()) {
|
|
356
|
+
resolve(false);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const timeoutId = setTimeout(() => resolve(false), timeout);
|
|
360
|
+
const onlineHandler = () => {
|
|
361
|
+
clearTimeout(timeoutId);
|
|
362
|
+
window.removeEventListener("online", onlineHandler);
|
|
363
|
+
resolve(true);
|
|
364
|
+
};
|
|
365
|
+
window.addEventListener("online", onlineHandler);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// src/queue/EventQueue.ts
|
|
370
|
+
var EventQueue = class {
|
|
371
|
+
constructor(token, config) {
|
|
372
|
+
this.queue = [];
|
|
373
|
+
this.flushTimer = null;
|
|
374
|
+
this.isFlushing = false;
|
|
375
|
+
this.handleOnline = () => {
|
|
376
|
+
const logger = getLogger();
|
|
377
|
+
logger.log("Back online, flushing stored events");
|
|
378
|
+
this.flush();
|
|
379
|
+
};
|
|
380
|
+
var _a, _b, _c, _d, _e;
|
|
381
|
+
this.token = token;
|
|
382
|
+
this.config = {
|
|
383
|
+
enableBatching: (_a = config == null ? void 0 : config.enableBatching) != null ? _a : DEFAULTS.enableBatching,
|
|
384
|
+
batchSize: (_b = config == null ? void 0 : config.batchSize) != null ? _b : DEFAULTS.batchSize,
|
|
385
|
+
flushInterval: Math.max(
|
|
386
|
+
TIMING.MIN_FLUSH_INTERVAL,
|
|
387
|
+
Math.min((_c = config == null ? void 0 : config.flushInterval) != null ? _c : DEFAULTS.flushInterval, TIMING.MAX_FLUSH_INTERVAL)
|
|
388
|
+
),
|
|
389
|
+
enableOfflineStorage: (_d = config == null ? void 0 : config.enableOfflineStorage) != null ? _d : DEFAULTS.enableOfflineStorage,
|
|
390
|
+
maxStoredEvents: (_e = config == null ? void 0 : config.maxStoredEvents) != null ? _e : DEFAULTS.maxStoredEvents
|
|
391
|
+
};
|
|
392
|
+
this.storage = new OfflineStorage(this.config.maxStoredEvents);
|
|
393
|
+
if (this.config.enableBatching) {
|
|
394
|
+
this.startFlushTimer();
|
|
395
|
+
}
|
|
396
|
+
if (isBrowser() && this.config.enableOfflineStorage) {
|
|
397
|
+
window.addEventListener("online", this.handleOnline);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
async add(event) {
|
|
401
|
+
const logger = getLogger();
|
|
402
|
+
if (!this.config.enableBatching) {
|
|
403
|
+
return this.sendImmediately([event]);
|
|
404
|
+
}
|
|
405
|
+
this.queue.push(event);
|
|
406
|
+
logger.log(`Event queued. Queue size: ${this.queue.length}`);
|
|
407
|
+
if (this.queue.length >= this.config.batchSize) {
|
|
408
|
+
return this.flush();
|
|
409
|
+
}
|
|
410
|
+
return { success: true };
|
|
411
|
+
}
|
|
412
|
+
async flush() {
|
|
413
|
+
const logger = getLogger();
|
|
414
|
+
if (this.isFlushing) {
|
|
415
|
+
logger.log("Flush already in progress, skipping");
|
|
416
|
+
return { success: true };
|
|
417
|
+
}
|
|
418
|
+
const events = [...this.queue];
|
|
419
|
+
this.queue = [];
|
|
420
|
+
if (this.config.enableOfflineStorage && isOnline()) {
|
|
421
|
+
const storedEvents = this.storage.retrieveAll();
|
|
422
|
+
if (storedEvents.length > 0) {
|
|
423
|
+
events.unshift(...storedEvents);
|
|
424
|
+
logger.log(`Including ${storedEvents.length} stored offline events`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (events.length === 0) {
|
|
428
|
+
return { success: true, count: 0 };
|
|
429
|
+
}
|
|
430
|
+
this.isFlushing = true;
|
|
431
|
+
try {
|
|
432
|
+
const result = await this.sendImmediately(events);
|
|
433
|
+
if (!result.success && this.config.enableOfflineStorage && !isOnline()) {
|
|
434
|
+
logger.log("Send failed while offline, storing events");
|
|
435
|
+
this.storage.storeMany(events);
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
438
|
+
} finally {
|
|
439
|
+
this.isFlushing = false;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
async sendImmediately(events) {
|
|
443
|
+
const logger = getLogger();
|
|
444
|
+
if (!isOnline() && this.config.enableOfflineStorage) {
|
|
445
|
+
logger.log("Offline, storing events for later");
|
|
446
|
+
const stored = this.storage.storeMany(events);
|
|
447
|
+
return { success: true, count: stored };
|
|
448
|
+
}
|
|
449
|
+
return sendBatch(BLINKER_API, this.token, events);
|
|
450
|
+
}
|
|
451
|
+
getStats() {
|
|
452
|
+
const queueSize = this.queue.length;
|
|
453
|
+
const storedSize = this.storage.count();
|
|
454
|
+
return {
|
|
455
|
+
queueSize,
|
|
456
|
+
storedSize,
|
|
457
|
+
totalPending: queueSize + storedSize
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
size() {
|
|
461
|
+
return this.queue.length;
|
|
462
|
+
}
|
|
463
|
+
startFlushTimer() {
|
|
464
|
+
if (this.flushTimer) return;
|
|
465
|
+
this.flushTimer = setInterval(() => {
|
|
466
|
+
if (this.queue.length > 0) {
|
|
467
|
+
this.flush();
|
|
468
|
+
}
|
|
469
|
+
}, this.config.flushInterval);
|
|
470
|
+
}
|
|
471
|
+
stopFlushTimer() {
|
|
472
|
+
if (this.flushTimer) {
|
|
473
|
+
clearInterval(this.flushTimer);
|
|
474
|
+
this.flushTimer = null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
destroy() {
|
|
478
|
+
this.stopFlushTimer();
|
|
479
|
+
if (isBrowser()) {
|
|
480
|
+
window.removeEventListener("online", this.handleOnline);
|
|
481
|
+
}
|
|
482
|
+
if (this.queue.length > 0 && isOnline()) {
|
|
483
|
+
this.flush();
|
|
484
|
+
} else if (this.queue.length > 0 && this.config.enableOfflineStorage) {
|
|
485
|
+
this.storage.storeMany(this.queue);
|
|
486
|
+
}
|
|
487
|
+
this.queue = [];
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// src/utils/hash.ts
|
|
492
|
+
function simpleHash(str) {
|
|
493
|
+
let hash = 5381;
|
|
494
|
+
for (let i = 0; i < str.length; i++) {
|
|
495
|
+
hash = (hash << 5) + hash ^ str.charCodeAt(i);
|
|
496
|
+
}
|
|
497
|
+
return (hash >>> 0).toString(16);
|
|
498
|
+
}
|
|
499
|
+
function generateErrorFingerprint(message, stack) {
|
|
500
|
+
let input = message;
|
|
501
|
+
if (stack) {
|
|
502
|
+
const stackLines = stack.split("\n").slice(1, 4);
|
|
503
|
+
const cleanedStack = stackLines.map((line) => line.replace(/:\d+:\d+\)?$/, "").trim()).join("|");
|
|
504
|
+
input += "|" + cleanedStack;
|
|
505
|
+
}
|
|
506
|
+
return simpleHash(input);
|
|
507
|
+
}
|
|
508
|
+
function generateUniqueId() {
|
|
509
|
+
const timestamp = Date.now().toString(36);
|
|
510
|
+
const randomPart = Math.random().toString(36).substring(2, 10);
|
|
511
|
+
return `${timestamp}-${randomPart}`;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// src/context/SessionManager.ts
|
|
515
|
+
var SessionManager = class {
|
|
516
|
+
constructor() {
|
|
517
|
+
this.session = null;
|
|
518
|
+
this.storageKey = STORAGE_KEYS.SESSION;
|
|
519
|
+
this.storageAvailable = isSessionStorageAvailable();
|
|
520
|
+
this.initSession();
|
|
521
|
+
}
|
|
522
|
+
initSession() {
|
|
523
|
+
const logger = getLogger();
|
|
524
|
+
if (this.storageAvailable) {
|
|
525
|
+
try {
|
|
526
|
+
const stored = sessionStorage.getItem(this.storageKey);
|
|
527
|
+
if (stored) {
|
|
528
|
+
this.session = JSON.parse(stored);
|
|
529
|
+
logger.log("Session restored:", this.session.id);
|
|
530
|
+
return;
|
|
79
531
|
}
|
|
80
|
-
|
|
532
|
+
} catch (e) {
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
this.session = {
|
|
536
|
+
id: generateUniqueId(),
|
|
537
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
538
|
+
pageViews: 0,
|
|
539
|
+
lastActivityAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
540
|
+
};
|
|
541
|
+
this.persist();
|
|
542
|
+
logger.log("New session created:", this.session.id);
|
|
543
|
+
}
|
|
544
|
+
getSessionId() {
|
|
545
|
+
var _a, _b;
|
|
546
|
+
return (_b = (_a = this.session) == null ? void 0 : _a.id) != null ? _b : generateUniqueId();
|
|
547
|
+
}
|
|
548
|
+
getSession() {
|
|
549
|
+
if (!this.session) {
|
|
550
|
+
this.initSession();
|
|
551
|
+
}
|
|
552
|
+
return { ...this.session };
|
|
553
|
+
}
|
|
554
|
+
recordPageView() {
|
|
555
|
+
if (this.session) {
|
|
556
|
+
this.session.pageViews++;
|
|
557
|
+
this.session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
558
|
+
this.persist();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
touch() {
|
|
562
|
+
if (this.session) {
|
|
563
|
+
this.session.lastActivityAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
564
|
+
this.persist();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
getDuration() {
|
|
568
|
+
if (!this.session) return 0;
|
|
569
|
+
const start = new Date(this.session.startedAt).getTime();
|
|
570
|
+
return Date.now() - start;
|
|
571
|
+
}
|
|
572
|
+
persist() {
|
|
573
|
+
if (!this.storageAvailable || !this.session) return;
|
|
574
|
+
try {
|
|
575
|
+
sessionStorage.setItem(this.storageKey, JSON.stringify(this.session));
|
|
576
|
+
} catch (e) {
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
endSession() {
|
|
580
|
+
if (this.storageAvailable) {
|
|
581
|
+
try {
|
|
582
|
+
sessionStorage.removeItem(this.storageKey);
|
|
583
|
+
} catch (e) {
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
this.session = null;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/context/UserContext.ts
|
|
591
|
+
var UserContext = class {
|
|
592
|
+
constructor(sessionManager) {
|
|
593
|
+
this.user = null;
|
|
594
|
+
this.customContext = {};
|
|
595
|
+
this.sessionManager = sessionManager;
|
|
596
|
+
}
|
|
597
|
+
identify(userId, traits) {
|
|
598
|
+
const logger = getLogger();
|
|
599
|
+
this.user = {
|
|
600
|
+
userId,
|
|
601
|
+
traits: traits ? { ...traits } : void 0,
|
|
602
|
+
identifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
603
|
+
};
|
|
604
|
+
logger.log("User identified:", userId);
|
|
605
|
+
}
|
|
606
|
+
getUserId() {
|
|
607
|
+
var _a;
|
|
608
|
+
return (_a = this.user) == null ? void 0 : _a.userId;
|
|
609
|
+
}
|
|
610
|
+
getUserTraits() {
|
|
611
|
+
var _a;
|
|
612
|
+
return ((_a = this.user) == null ? void 0 : _a.traits) ? { ...this.user.traits } : void 0;
|
|
613
|
+
}
|
|
614
|
+
isIdentified() {
|
|
615
|
+
return this.user !== null;
|
|
616
|
+
}
|
|
617
|
+
setContext(key, value) {
|
|
618
|
+
const logger = getLogger();
|
|
619
|
+
this.customContext[key] = value;
|
|
620
|
+
logger.log("Context set:", key);
|
|
621
|
+
}
|
|
622
|
+
getContextValue(key) {
|
|
623
|
+
return this.customContext[key];
|
|
624
|
+
}
|
|
625
|
+
getCustomContext() {
|
|
626
|
+
return { ...this.customContext };
|
|
627
|
+
}
|
|
628
|
+
getEventContext() {
|
|
629
|
+
var _a, _b;
|
|
630
|
+
return {
|
|
631
|
+
sessionId: this.sessionManager.getSessionId(),
|
|
632
|
+
userId: (_a = this.user) == null ? void 0 : _a.userId,
|
|
633
|
+
userTraits: ((_b = this.user) == null ? void 0 : _b.traits) ? { ...this.user.traits } : void 0,
|
|
634
|
+
custom: { ...this.customContext }
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
clearIdentity() {
|
|
638
|
+
const logger = getLogger();
|
|
639
|
+
this.user = null;
|
|
640
|
+
logger.log("User identity cleared");
|
|
641
|
+
}
|
|
642
|
+
clearAll() {
|
|
643
|
+
const logger = getLogger();
|
|
644
|
+
this.user = null;
|
|
645
|
+
this.customContext = {};
|
|
646
|
+
logger.log("All context cleared");
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
// src/sampling/RateLimiter.ts
|
|
651
|
+
var MINUTE_MS = 60 * 1e3;
|
|
652
|
+
var RateLimiter = class {
|
|
653
|
+
constructor(maxEventsPerMinute) {
|
|
654
|
+
this.maxPerMinute = maxEventsPerMinute != null ? maxEventsPerMinute : DEFAULTS.maxEventsPerMinute;
|
|
655
|
+
this.state = {
|
|
656
|
+
count: 0,
|
|
657
|
+
windowStart: Date.now(),
|
|
658
|
+
dropped: 0
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
allow() {
|
|
662
|
+
const logger = getLogger();
|
|
663
|
+
if (this.maxPerMinute === 0) {
|
|
664
|
+
return true;
|
|
665
|
+
}
|
|
666
|
+
const now = Date.now();
|
|
667
|
+
if (now - this.state.windowStart >= MINUTE_MS) {
|
|
668
|
+
if (this.state.dropped > 0) {
|
|
669
|
+
logger.warn(`Rate limit: ${this.state.dropped} events dropped in last minute`);
|
|
670
|
+
}
|
|
671
|
+
this.state = {
|
|
672
|
+
count: 0,
|
|
673
|
+
windowStart: now,
|
|
674
|
+
dropped: 0
|
|
81
675
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
676
|
+
}
|
|
677
|
+
if (this.state.count < this.maxPerMinute) {
|
|
678
|
+
this.state.count++;
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
this.state.dropped++;
|
|
682
|
+
if (this.state.dropped === 1) {
|
|
683
|
+
logger.warn(`Rate limit reached: ${this.maxPerMinute} events/minute`);
|
|
684
|
+
}
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
getState() {
|
|
688
|
+
return { ...this.state };
|
|
689
|
+
}
|
|
690
|
+
getRemaining() {
|
|
691
|
+
if (this.maxPerMinute === 0) return Infinity;
|
|
692
|
+
if (Date.now() - this.state.windowStart >= MINUTE_MS) {
|
|
693
|
+
return this.maxPerMinute;
|
|
694
|
+
}
|
|
695
|
+
return Math.max(0, this.maxPerMinute - this.state.count);
|
|
696
|
+
}
|
|
697
|
+
reset() {
|
|
698
|
+
this.state = {
|
|
699
|
+
count: 0,
|
|
700
|
+
windowStart: Date.now(),
|
|
701
|
+
dropped: 0
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
setLimit(maxEventsPerMinute) {
|
|
705
|
+
this.maxPerMinute = maxEventsPerMinute;
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
// src/sampling/Sampler.ts
|
|
710
|
+
var Sampler = class {
|
|
711
|
+
constructor(sampleRate) {
|
|
712
|
+
this.sampled = 0;
|
|
713
|
+
this.dropped = 0;
|
|
714
|
+
this.sampleRate = Math.max(0, Math.min(1, sampleRate != null ? sampleRate : DEFAULTS.sampleRate));
|
|
715
|
+
}
|
|
716
|
+
shouldSample(isError = false) {
|
|
717
|
+
const logger = getLogger();
|
|
718
|
+
if (isError) {
|
|
719
|
+
this.sampled++;
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
if (this.sampleRate >= 1) {
|
|
723
|
+
this.sampled++;
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
if (this.sampleRate <= 0) {
|
|
727
|
+
this.dropped++;
|
|
728
|
+
return false;
|
|
729
|
+
}
|
|
730
|
+
if (Math.random() < this.sampleRate) {
|
|
731
|
+
this.sampled++;
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
this.dropped++;
|
|
735
|
+
logger.log("Event sampled out");
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
getStats() {
|
|
739
|
+
return {
|
|
740
|
+
sampled: this.sampled,
|
|
741
|
+
dropped: this.dropped,
|
|
742
|
+
rate: this.sampleRate
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
setRate(rate) {
|
|
746
|
+
this.sampleRate = Math.max(0, Math.min(1, rate));
|
|
747
|
+
}
|
|
748
|
+
getRate() {
|
|
749
|
+
return this.sampleRate;
|
|
750
|
+
}
|
|
751
|
+
resetStats() {
|
|
752
|
+
this.sampled = 0;
|
|
753
|
+
this.dropped = 0;
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
// src/capture/deduplication.ts
|
|
758
|
+
var Deduplicator = class {
|
|
759
|
+
constructor(enabled, windowMs) {
|
|
760
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
761
|
+
this.cleanupTimer = null;
|
|
762
|
+
this.enabled = enabled != null ? enabled : DEFAULTS.enableDeduplication;
|
|
763
|
+
this.windowMs = windowMs != null ? windowMs : DEFAULTS.deduplicationWindow;
|
|
764
|
+
if (this.enabled) {
|
|
765
|
+
this.startCleanup();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
check(message, stack) {
|
|
769
|
+
const fingerprint = generateErrorFingerprint(message, stack);
|
|
770
|
+
if (!this.enabled) {
|
|
771
|
+
return {
|
|
772
|
+
shouldSend: true,
|
|
773
|
+
fingerprint,
|
|
774
|
+
count: 1,
|
|
775
|
+
isDuplicate: false
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
const logger = getLogger();
|
|
779
|
+
const now = Date.now();
|
|
780
|
+
const existing = this.entries.get(fingerprint);
|
|
781
|
+
if (existing) {
|
|
782
|
+
if (now - existing.firstSeen < this.windowMs) {
|
|
783
|
+
existing.count++;
|
|
784
|
+
existing.lastSeen = now;
|
|
785
|
+
logger.log(`Duplicate error detected (count: ${existing.count}):`, message.substring(0, 50));
|
|
786
|
+
return {
|
|
787
|
+
shouldSend: false,
|
|
788
|
+
fingerprint,
|
|
789
|
+
count: existing.count,
|
|
790
|
+
isDuplicate: true
|
|
791
|
+
};
|
|
86
792
|
}
|
|
793
|
+
this.entries.delete(fingerprint);
|
|
794
|
+
}
|
|
795
|
+
this.entries.set(fingerprint, {
|
|
796
|
+
fingerprint,
|
|
797
|
+
firstSeen: now,
|
|
798
|
+
count: 1,
|
|
799
|
+
lastSeen: now
|
|
87
800
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
801
|
+
return {
|
|
802
|
+
shouldSend: true,
|
|
803
|
+
fingerprint,
|
|
804
|
+
count: 1,
|
|
805
|
+
isDuplicate: false
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
getCount(fingerprint) {
|
|
809
|
+
var _a, _b;
|
|
810
|
+
return (_b = (_a = this.entries.get(fingerprint)) == null ? void 0 : _a.count) != null ? _b : 1;
|
|
811
|
+
}
|
|
812
|
+
flush() {
|
|
813
|
+
const duplicates = Array.from(this.entries.values()).filter((e) => e.count > 1);
|
|
814
|
+
this.entries.clear();
|
|
815
|
+
return duplicates;
|
|
816
|
+
}
|
|
817
|
+
startCleanup() {
|
|
818
|
+
this.cleanupTimer = setInterval(() => {
|
|
819
|
+
this.cleanup();
|
|
820
|
+
}, this.windowMs);
|
|
821
|
+
}
|
|
822
|
+
cleanup() {
|
|
823
|
+
const now = Date.now();
|
|
824
|
+
const logger = getLogger();
|
|
825
|
+
let removed = 0;
|
|
826
|
+
for (const [fingerprint, entry] of this.entries) {
|
|
827
|
+
if (now - entry.firstSeen >= this.windowMs) {
|
|
828
|
+
this.entries.delete(fingerprint);
|
|
829
|
+
removed++;
|
|
92
830
|
}
|
|
93
|
-
|
|
831
|
+
}
|
|
832
|
+
if (removed > 0) {
|
|
833
|
+
logger.log(`Deduplication cleanup: removed ${removed} expired entries`);
|
|
94
834
|
}
|
|
95
835
|
}
|
|
96
|
-
|
|
97
|
-
|
|
836
|
+
destroy() {
|
|
837
|
+
if (this.cleanupTimer) {
|
|
838
|
+
clearInterval(this.cleanupTimer);
|
|
839
|
+
this.cleanupTimer = null;
|
|
840
|
+
}
|
|
841
|
+
this.entries.clear();
|
|
842
|
+
}
|
|
843
|
+
getStats() {
|
|
844
|
+
return {
|
|
845
|
+
entries: this.entries.size,
|
|
846
|
+
enabled: this.enabled,
|
|
847
|
+
windowMs: this.windowMs
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
};
|
|
98
851
|
|
|
99
|
-
// src/errorHandler.ts
|
|
852
|
+
// src/capture/errorHandler.ts
|
|
100
853
|
var originalOnError = null;
|
|
101
854
|
var originalOnUnhandledRejection = null;
|
|
102
855
|
var isSetup = false;
|
|
103
856
|
function setupErrorHandlers(callback, options = {}) {
|
|
104
|
-
const { captureErrors = true, captureRejections = true
|
|
857
|
+
const { captureErrors = true, captureRejections = true } = options;
|
|
858
|
+
const logger = getLogger();
|
|
105
859
|
if (isSetup) {
|
|
106
|
-
|
|
107
|
-
console.warn("[Blinker] Error handlers already set up");
|
|
108
|
-
}
|
|
860
|
+
logger.warn("Error handlers already set up");
|
|
109
861
|
return;
|
|
110
862
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
console.log("[Blinker] Not in browser environment, skipping error handlers");
|
|
114
|
-
}
|
|
863
|
+
if (!isBrowser()) {
|
|
864
|
+
logger.log("Not in browser environment, skipping error handlers");
|
|
115
865
|
return;
|
|
116
866
|
}
|
|
117
867
|
if (captureErrors) {
|
|
@@ -125,9 +875,7 @@ function setupErrorHandlers(callback, options = {}) {
|
|
|
125
875
|
stack: error == null ? void 0 : error.stack,
|
|
126
876
|
errorType: error == null ? void 0 : error.name
|
|
127
877
|
};
|
|
128
|
-
|
|
129
|
-
console.log("[Blinker] Captured error:", errorMessage);
|
|
130
|
-
}
|
|
878
|
+
logger.log("Captured error:", errorMessage);
|
|
131
879
|
callback("error", errorMessage, payload);
|
|
132
880
|
if (typeof originalOnError === "function") {
|
|
133
881
|
return originalOnError.call(window, message, source, lineno, colno, error);
|
|
@@ -154,9 +902,7 @@ function setupErrorHandlers(callback, options = {}) {
|
|
|
154
902
|
message = String(reason);
|
|
155
903
|
}
|
|
156
904
|
}
|
|
157
|
-
|
|
158
|
-
console.log("[Blinker] Captured unhandled rejection:", message);
|
|
159
|
-
}
|
|
905
|
+
logger.log("Captured unhandled rejection:", message);
|
|
160
906
|
callback("error", message, payload);
|
|
161
907
|
if (typeof originalOnUnhandledRejection === "function") {
|
|
162
908
|
originalOnUnhandledRejection.call(window, event);
|
|
@@ -164,9 +910,11 @@ function setupErrorHandlers(callback, options = {}) {
|
|
|
164
910
|
};
|
|
165
911
|
}
|
|
166
912
|
isSetup = true;
|
|
913
|
+
logger.log("Error handlers set up");
|
|
167
914
|
}
|
|
168
915
|
function teardownErrorHandlers() {
|
|
169
|
-
|
|
916
|
+
const logger = getLogger();
|
|
917
|
+
if (!isBrowser()) {
|
|
170
918
|
return;
|
|
171
919
|
}
|
|
172
920
|
if (originalOnError !== void 0) {
|
|
@@ -178,133 +926,1358 @@ function teardownErrorHandlers() {
|
|
|
178
926
|
originalOnError = null;
|
|
179
927
|
originalOnUnhandledRejection = null;
|
|
180
928
|
isSetup = false;
|
|
929
|
+
logger.log("Error handlers torn down");
|
|
181
930
|
}
|
|
182
931
|
|
|
183
|
-
// src/
|
|
932
|
+
// src/widget/types.ts
|
|
933
|
+
var DEFAULT_WIDGET_CONFIG = {
|
|
934
|
+
enabled: false,
|
|
935
|
+
theme: {
|
|
936
|
+
primary: "#6366f1",
|
|
937
|
+
background: "#ffffff",
|
|
938
|
+
text: "#1f2937",
|
|
939
|
+
accent: "#10b981"
|
|
940
|
+
},
|
|
941
|
+
messages: {
|
|
942
|
+
greeting: "Opa! Algo n\xE3o saiu como esperado.",
|
|
943
|
+
subtext: "Pode nos ajudar a entender o que aconteceu?",
|
|
944
|
+
thankYou: "Valeu! J\xE1 estamos olhando isso.",
|
|
945
|
+
buttonSend: "Enviar",
|
|
946
|
+
buttonSkip: "Agora n\xE3o"
|
|
947
|
+
},
|
|
948
|
+
position: "bottom-right",
|
|
949
|
+
delay: 2e3,
|
|
950
|
+
maxShowsPerSession: 2,
|
|
951
|
+
cooldownMinutes: 30
|
|
952
|
+
};
|
|
953
|
+
|
|
954
|
+
// src/widget/Avatar.ts
|
|
955
|
+
var Avatar = class {
|
|
956
|
+
constructor(theme, onClick) {
|
|
957
|
+
this.element = null;
|
|
958
|
+
this.pupilLeft = null;
|
|
959
|
+
this.pupilRight = null;
|
|
960
|
+
this.mouseTrackingEnabled = true;
|
|
961
|
+
this.theme = theme;
|
|
962
|
+
this.onClick = onClick;
|
|
963
|
+
this.handleMouseMove = this.handleMouseMove.bind(this);
|
|
964
|
+
}
|
|
965
|
+
create() {
|
|
966
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
967
|
+
svg.setAttribute("viewBox", "0 0 100 100");
|
|
968
|
+
svg.setAttribute("class", "blinker-avatar");
|
|
969
|
+
svg.innerHTML = this.getSVGContent();
|
|
970
|
+
svg.addEventListener("click", this.onClick);
|
|
971
|
+
svg.addEventListener("mouseenter", () => this.setState("hover"));
|
|
972
|
+
svg.addEventListener("mouseleave", () => this.setState("idle"));
|
|
973
|
+
this.element = svg;
|
|
974
|
+
this.pupilLeft = svg.querySelector(".blinker-pupil-left");
|
|
975
|
+
this.pupilRight = svg.querySelector(".blinker-pupil-right");
|
|
976
|
+
document.addEventListener("mousemove", this.handleMouseMove);
|
|
977
|
+
return svg;
|
|
978
|
+
}
|
|
979
|
+
getSVGContent() {
|
|
980
|
+
return `
|
|
981
|
+
<defs>
|
|
982
|
+
<linearGradient id="blinker-body-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
983
|
+
<stop offset="0%" style="stop-color:${this.theme.primary};stop-opacity:1" />
|
|
984
|
+
<stop offset="100%" style="stop-color:${this.adjustColor(this.theme.primary, -30)};stop-opacity:1" />
|
|
985
|
+
</linearGradient>
|
|
986
|
+
<filter id="blinker-shadow" x="-20%" y="-20%" width="140%" height="140%">
|
|
987
|
+
<feDropShadow dx="0" dy="4" stdDeviation="4" flood-opacity="0.2"/>
|
|
988
|
+
</filter>
|
|
989
|
+
</defs>
|
|
990
|
+
|
|
991
|
+
<!-- Glow effect -->
|
|
992
|
+
<ellipse class="blinker-avatar-glow" cx="50" cy="85" rx="25" ry="8" />
|
|
993
|
+
|
|
994
|
+
<!-- Body -->
|
|
995
|
+
<g class="blinker-avatar-body" filter="url(#blinker-shadow)">
|
|
996
|
+
<!-- Main body shape - rounded rectangle with personality -->
|
|
997
|
+
<path d="
|
|
998
|
+
M 25 30
|
|
999
|
+
Q 25 15, 50 15
|
|
1000
|
+
Q 75 15, 75 30
|
|
1001
|
+
L 75 65
|
|
1002
|
+
Q 75 85, 50 85
|
|
1003
|
+
Q 25 85, 25 65
|
|
1004
|
+
Z
|
|
1005
|
+
" fill="url(#blinker-body-gradient)" />
|
|
1006
|
+
|
|
1007
|
+
<!-- Antenna/Top detail -->
|
|
1008
|
+
<circle cx="50" cy="12" r="4" fill="${this.theme.primary}" />
|
|
1009
|
+
<line x1="50" y1="16" x2="50" y2="22" stroke="${this.theme.primary}" stroke-width="3" stroke-linecap="round" />
|
|
1010
|
+
</g>
|
|
1011
|
+
|
|
1012
|
+
<!-- Face -->
|
|
1013
|
+
<g class="blinker-avatar-face">
|
|
1014
|
+
<!-- Left eye container -->
|
|
1015
|
+
<ellipse class="blinker-avatar-eye blinker-avatar-eye-left" cx="38" cy="42" rx="10" ry="11" />
|
|
1016
|
+
<!-- Right eye container -->
|
|
1017
|
+
<ellipse class="blinker-avatar-eye blinker-avatar-eye-right" cx="62" cy="42" rx="10" ry="11" />
|
|
1018
|
+
|
|
1019
|
+
<!-- Left pupil -->
|
|
1020
|
+
<circle class="blinker-avatar-pupil blinker-pupil-left" cx="38" cy="43" r="5" />
|
|
1021
|
+
<!-- Right pupil -->
|
|
1022
|
+
<circle class="blinker-avatar-pupil blinker-pupil-right" cx="62" cy="43" r="5" />
|
|
1023
|
+
|
|
1024
|
+
<!-- Eye shine -->
|
|
1025
|
+
<circle cx="35" cy="40" r="2" fill="white" opacity="0.8" />
|
|
1026
|
+
<circle cx="59" cy="40" r="2" fill="white" opacity="0.8" />
|
|
1027
|
+
|
|
1028
|
+
<!-- Mouth -->
|
|
1029
|
+
<path class="blinker-avatar-mouth" d="M 40 60 Q 50 68, 60 60" />
|
|
1030
|
+
</g>
|
|
1031
|
+
|
|
1032
|
+
<!-- Decorative elements -->
|
|
1033
|
+
<circle cx="30" cy="55" r="3" fill="${this.theme.primary}" opacity="0.3" />
|
|
1034
|
+
<circle cx="70" cy="55" r="3" fill="${this.theme.primary}" opacity="0.3" />
|
|
1035
|
+
`;
|
|
1036
|
+
}
|
|
1037
|
+
handleMouseMove(e) {
|
|
1038
|
+
if (!this.mouseTrackingEnabled || !this.element || !this.pupilLeft || !this.pupilRight) return;
|
|
1039
|
+
const rect = this.element.getBoundingClientRect();
|
|
1040
|
+
const centerX = rect.left + rect.width / 2;
|
|
1041
|
+
const centerY = rect.top + rect.height / 2;
|
|
1042
|
+
const deltaX = (e.clientX - centerX) / 50;
|
|
1043
|
+
const deltaY = (e.clientY - centerY) / 50;
|
|
1044
|
+
const maxOffset = 3;
|
|
1045
|
+
const offsetX = Math.max(-maxOffset, Math.min(maxOffset, deltaX));
|
|
1046
|
+
const offsetY = Math.max(-maxOffset, Math.min(maxOffset, deltaY));
|
|
1047
|
+
this.pupilLeft.setAttribute("transform", `translate(${offsetX}, ${offsetY})`);
|
|
1048
|
+
this.pupilRight.setAttribute("transform", `translate(${offsetX}, ${offsetY})`);
|
|
1049
|
+
}
|
|
1050
|
+
setState(state) {
|
|
1051
|
+
if (!this.element) return;
|
|
1052
|
+
this.element.classList.remove("idle", "hover", "minimized", "talking");
|
|
1053
|
+
this.element.classList.add(state);
|
|
1054
|
+
}
|
|
1055
|
+
setMinimized(minimized) {
|
|
1056
|
+
if (!this.element) return;
|
|
1057
|
+
if (minimized) {
|
|
1058
|
+
this.element.classList.add("minimized");
|
|
1059
|
+
this.mouseTrackingEnabled = false;
|
|
1060
|
+
} else {
|
|
1061
|
+
this.element.classList.remove("minimized");
|
|
1062
|
+
this.mouseTrackingEnabled = true;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
updateTheme(theme) {
|
|
1066
|
+
this.theme = theme;
|
|
1067
|
+
if (this.element) {
|
|
1068
|
+
this.element.innerHTML = this.getSVGContent();
|
|
1069
|
+
this.pupilLeft = this.element.querySelector(".blinker-pupil-left");
|
|
1070
|
+
this.pupilRight = this.element.querySelector(".blinker-pupil-right");
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
adjustColor(hex, amount) {
|
|
1074
|
+
const num = parseInt(hex.replace("#", ""), 16);
|
|
1075
|
+
const r = Math.max(0, Math.min(255, (num >> 16) + amount));
|
|
1076
|
+
const g = Math.max(0, Math.min(255, (num >> 8 & 255) + amount));
|
|
1077
|
+
const b = Math.max(0, Math.min(255, (num & 255) + amount));
|
|
1078
|
+
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
1079
|
+
}
|
|
1080
|
+
getElement() {
|
|
1081
|
+
return this.element;
|
|
1082
|
+
}
|
|
1083
|
+
destroy() {
|
|
1084
|
+
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
1085
|
+
if (this.element) {
|
|
1086
|
+
this.element.removeEventListener("click", this.onClick);
|
|
1087
|
+
this.element.remove();
|
|
1088
|
+
this.element = null;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// src/widget/Dialog.ts
|
|
1094
|
+
var Dialog = class {
|
|
1095
|
+
constructor(config, onSubmit, onClose) {
|
|
1096
|
+
this.overlay = null;
|
|
1097
|
+
this.dialog = null;
|
|
1098
|
+
this.questions = [];
|
|
1099
|
+
this.answers = {};
|
|
1100
|
+
this.errorId = "";
|
|
1101
|
+
this.state = "questions";
|
|
1102
|
+
this.config = config;
|
|
1103
|
+
this.onSubmit = onSubmit;
|
|
1104
|
+
this.onClose = onClose;
|
|
1105
|
+
}
|
|
1106
|
+
open(errorId, questions) {
|
|
1107
|
+
this.errorId = errorId;
|
|
1108
|
+
this.questions = questions;
|
|
1109
|
+
this.answers = {};
|
|
1110
|
+
this.state = "questions";
|
|
1111
|
+
this.render();
|
|
1112
|
+
}
|
|
1113
|
+
close() {
|
|
1114
|
+
if (this.overlay) {
|
|
1115
|
+
this.overlay.remove();
|
|
1116
|
+
this.overlay = null;
|
|
1117
|
+
}
|
|
1118
|
+
if (this.dialog) {
|
|
1119
|
+
this.dialog.remove();
|
|
1120
|
+
this.dialog = null;
|
|
1121
|
+
}
|
|
1122
|
+
this.onClose();
|
|
1123
|
+
}
|
|
1124
|
+
render() {
|
|
1125
|
+
this.close();
|
|
1126
|
+
this.overlay = document.createElement("div");
|
|
1127
|
+
this.overlay.className = "blinker-dialog-overlay";
|
|
1128
|
+
this.overlay.addEventListener("click", () => this.close());
|
|
1129
|
+
this.dialog = document.createElement("div");
|
|
1130
|
+
this.dialog.className = `blinker-dialog ${this.config.position}`;
|
|
1131
|
+
this.dialog.innerHTML = this.state === "questions" ? this.getQuestionsHTML() : this.getThankYouHTML();
|
|
1132
|
+
document.body.appendChild(this.overlay);
|
|
1133
|
+
document.body.appendChild(this.dialog);
|
|
1134
|
+
this.attachEventListeners();
|
|
1135
|
+
}
|
|
1136
|
+
getQuestionsHTML() {
|
|
1137
|
+
const { messages } = this.config;
|
|
1138
|
+
return `
|
|
1139
|
+
<div class="blinker-dialog-header">
|
|
1140
|
+
<svg class="blinker-dialog-avatar" viewBox="0 0 100 100">
|
|
1141
|
+
<defs>
|
|
1142
|
+
<linearGradient id="dialog-gradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
1143
|
+
<stop offset="0%" style="stop-color:${this.config.theme.primary}" />
|
|
1144
|
+
<stop offset="100%" style="stop-color:${this.adjustColor(this.config.theme.primary, -30)}" />
|
|
1145
|
+
</linearGradient>
|
|
1146
|
+
</defs>
|
|
1147
|
+
<path d="M 25 30 Q 25 15, 50 15 Q 75 15, 75 30 L 75 65 Q 75 85, 50 85 Q 25 85, 25 65 Z" fill="url(#dialog-gradient)" />
|
|
1148
|
+
<ellipse cx="38" cy="42" rx="10" ry="11" fill="${this.config.theme.background}" />
|
|
1149
|
+
<ellipse cx="62" cy="42" rx="10" ry="11" fill="${this.config.theme.background}" />
|
|
1150
|
+
<circle cx="38" cy="43" r="5" fill="${this.config.theme.text}" />
|
|
1151
|
+
<circle cx="62" cy="43" r="5" fill="${this.config.theme.text}" />
|
|
1152
|
+
<path d="M 40 60 Q 50 68, 60 60" stroke="${this.config.theme.background}" stroke-width="2" fill="none" stroke-linecap="round" />
|
|
1153
|
+
</svg>
|
|
1154
|
+
<div class="blinker-dialog-title">
|
|
1155
|
+
<p class="blinker-dialog-greeting">${messages.greeting}</p>
|
|
1156
|
+
<p class="blinker-dialog-subtext">${messages.subtext}</p>
|
|
1157
|
+
</div>
|
|
1158
|
+
<button class="blinker-dialog-close" aria-label="Fechar">
|
|
1159
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1160
|
+
<path d="M18 6L6 18M6 6l12 12" />
|
|
1161
|
+
</svg>
|
|
1162
|
+
</button>
|
|
1163
|
+
</div>
|
|
1164
|
+
<div class="blinker-dialog-body">
|
|
1165
|
+
${this.questions.map((q) => this.getQuestionHTML(q)).join("")}
|
|
1166
|
+
</div>
|
|
1167
|
+
<div class="blinker-dialog-footer">
|
|
1168
|
+
<button class="blinker-btn blinker-btn-secondary" data-action="skip">
|
|
1169
|
+
${messages.buttonSkip}
|
|
1170
|
+
</button>
|
|
1171
|
+
<button class="blinker-btn blinker-btn-primary" data-action="submit">
|
|
1172
|
+
${messages.buttonSend}
|
|
1173
|
+
</button>
|
|
1174
|
+
</div>
|
|
1175
|
+
`;
|
|
1176
|
+
}
|
|
1177
|
+
getQuestionHTML(question) {
|
|
1178
|
+
var _a;
|
|
1179
|
+
const required = question.required ? " *" : "";
|
|
1180
|
+
switch (question.type) {
|
|
1181
|
+
case "text":
|
|
1182
|
+
return `
|
|
1183
|
+
<div class="blinker-question" data-question-id="${question.id}">
|
|
1184
|
+
<label class="blinker-question-label">${question.text}${required}</label>
|
|
1185
|
+
<textarea
|
|
1186
|
+
class="blinker-question-input blinker-question-textarea"
|
|
1187
|
+
placeholder="Digite sua resposta..."
|
|
1188
|
+
data-question-id="${question.id}"
|
|
1189
|
+
></textarea>
|
|
1190
|
+
</div>
|
|
1191
|
+
`;
|
|
1192
|
+
case "select":
|
|
1193
|
+
return `
|
|
1194
|
+
<div class="blinker-question" data-question-id="${question.id}">
|
|
1195
|
+
<label class="blinker-question-label">${question.text}${required}</label>
|
|
1196
|
+
<select class="blinker-question-input blinker-question-select" data-question-id="${question.id}">
|
|
1197
|
+
<option value="">Selecione...</option>
|
|
1198
|
+
${(_a = question.options) == null ? void 0 : _a.map((opt) => `<option value="${opt}">${opt}</option>`).join("")}
|
|
1199
|
+
</select>
|
|
1200
|
+
</div>
|
|
1201
|
+
`;
|
|
1202
|
+
case "boolean":
|
|
1203
|
+
return `
|
|
1204
|
+
<div class="blinker-question" data-question-id="${question.id}">
|
|
1205
|
+
<label class="blinker-question-label">${question.text}${required}</label>
|
|
1206
|
+
<div class="blinker-question-boolean">
|
|
1207
|
+
<button type="button" data-question-id="${question.id}" data-value="true">Sim</button>
|
|
1208
|
+
<button type="button" data-question-id="${question.id}" data-value="false">N\xE3o</button>
|
|
1209
|
+
</div>
|
|
1210
|
+
</div>
|
|
1211
|
+
`;
|
|
1212
|
+
default:
|
|
1213
|
+
return "";
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
getThankYouHTML() {
|
|
1217
|
+
return `
|
|
1218
|
+
<div class="blinker-thank-you">
|
|
1219
|
+
<svg class="blinker-thank-you-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1220
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
1221
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
1222
|
+
</svg>
|
|
1223
|
+
<p class="blinker-thank-you-text">${this.config.messages.thankYou}</p>
|
|
1224
|
+
</div>
|
|
1225
|
+
`;
|
|
1226
|
+
}
|
|
1227
|
+
attachEventListeners() {
|
|
1228
|
+
if (!this.dialog) return;
|
|
1229
|
+
const closeBtn = this.dialog.querySelector(".blinker-dialog-close");
|
|
1230
|
+
closeBtn == null ? void 0 : closeBtn.addEventListener("click", () => this.close());
|
|
1231
|
+
const submitBtn = this.dialog.querySelector('[data-action="submit"]');
|
|
1232
|
+
submitBtn == null ? void 0 : submitBtn.addEventListener("click", () => this.handleSubmit());
|
|
1233
|
+
const skipBtn = this.dialog.querySelector('[data-action="skip"]');
|
|
1234
|
+
skipBtn == null ? void 0 : skipBtn.addEventListener("click", () => this.close());
|
|
1235
|
+
const textareas = this.dialog.querySelectorAll("textarea");
|
|
1236
|
+
textareas.forEach((textarea) => {
|
|
1237
|
+
textarea.addEventListener("input", (e) => {
|
|
1238
|
+
const target = e.target;
|
|
1239
|
+
const questionId = target.dataset.questionId;
|
|
1240
|
+
if (questionId) {
|
|
1241
|
+
this.answers[questionId] = target.value;
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
});
|
|
1245
|
+
const selects = this.dialog.querySelectorAll("select");
|
|
1246
|
+
selects.forEach((select) => {
|
|
1247
|
+
select.addEventListener("change", (e) => {
|
|
1248
|
+
const target = e.target;
|
|
1249
|
+
const questionId = target.dataset.questionId;
|
|
1250
|
+
if (questionId) {
|
|
1251
|
+
this.answers[questionId] = target.value;
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
const booleanBtns = this.dialog.querySelectorAll(".blinker-question-boolean button");
|
|
1256
|
+
booleanBtns.forEach((btn) => {
|
|
1257
|
+
btn.addEventListener("click", (e) => {
|
|
1258
|
+
const target = e.target;
|
|
1259
|
+
const questionId = target.dataset.questionId;
|
|
1260
|
+
const value = target.dataset.value === "true";
|
|
1261
|
+
if (questionId) {
|
|
1262
|
+
this.answers[questionId] = value;
|
|
1263
|
+
const container = target.parentElement;
|
|
1264
|
+
container == null ? void 0 : container.querySelectorAll("button").forEach((b) => b.classList.remove("selected"));
|
|
1265
|
+
target.classList.add("selected");
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
handleSubmit() {
|
|
1271
|
+
var _a;
|
|
1272
|
+
const requiredQuestions = this.questions.filter((q) => q.required);
|
|
1273
|
+
const missingRequired = requiredQuestions.some((q) => {
|
|
1274
|
+
const answer = this.answers[q.id];
|
|
1275
|
+
return answer === void 0 || answer === "";
|
|
1276
|
+
});
|
|
1277
|
+
if (missingRequired) {
|
|
1278
|
+
const firstRequired = requiredQuestions.find((q) => !this.answers[q.id]);
|
|
1279
|
+
if (firstRequired) {
|
|
1280
|
+
const input = (_a = this.dialog) == null ? void 0 : _a.querySelector(`[data-question-id="${firstRequired.id}"]`);
|
|
1281
|
+
if (input instanceof HTMLElement) {
|
|
1282
|
+
input.focus();
|
|
1283
|
+
input.style.borderColor = "#ef4444";
|
|
1284
|
+
setTimeout(() => {
|
|
1285
|
+
input.style.borderColor = "";
|
|
1286
|
+
}, 2e3);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
const feedback = {
|
|
1292
|
+
errorId: this.errorId,
|
|
1293
|
+
answers: { ...this.answers },
|
|
1294
|
+
submittedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1295
|
+
};
|
|
1296
|
+
this.onSubmit(feedback);
|
|
1297
|
+
this.state = "thankyou";
|
|
1298
|
+
this.render();
|
|
1299
|
+
setTimeout(() => {
|
|
1300
|
+
this.close();
|
|
1301
|
+
}, 2500);
|
|
1302
|
+
}
|
|
1303
|
+
updateConfig(config) {
|
|
1304
|
+
this.config = config;
|
|
1305
|
+
}
|
|
1306
|
+
adjustColor(hex, amount) {
|
|
1307
|
+
const num = parseInt(hex.replace("#", ""), 16);
|
|
1308
|
+
const r = Math.max(0, Math.min(255, (num >> 16) + amount));
|
|
1309
|
+
const g = Math.max(0, Math.min(255, (num >> 8 & 255) + amount));
|
|
1310
|
+
const b = Math.max(0, Math.min(255, (num & 255) + amount));
|
|
1311
|
+
return `#${(r << 16 | g << 8 | b).toString(16).padStart(6, "0")}`;
|
|
1312
|
+
}
|
|
1313
|
+
isOpen() {
|
|
1314
|
+
return this.dialog !== null;
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/widget/QuestionEngine.ts
|
|
1319
|
+
var BASE_QUESTIONS = [
|
|
1320
|
+
{
|
|
1321
|
+
id: "what_doing",
|
|
1322
|
+
text: "O que voc\xEA estava tentando fazer?",
|
|
1323
|
+
type: "text",
|
|
1324
|
+
required: true
|
|
1325
|
+
}
|
|
1326
|
+
];
|
|
1327
|
+
var QUESTION_RULES = [
|
|
1328
|
+
{
|
|
1329
|
+
id: "server_error",
|
|
1330
|
+
condition: (e) => e.httpCode !== void 0 && e.httpCode >= 500,
|
|
1331
|
+
priority: 10,
|
|
1332
|
+
questions: [
|
|
1333
|
+
{
|
|
1334
|
+
id: "form_filled",
|
|
1335
|
+
text: "Voc\xEA preencheu algum formul\xE1rio antes disso?",
|
|
1336
|
+
type: "boolean"
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
id: "data_lost",
|
|
1340
|
+
text: "Voc\xEA perdeu algum dado importante?",
|
|
1341
|
+
type: "boolean"
|
|
1342
|
+
}
|
|
1343
|
+
]
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
id: "network_error",
|
|
1347
|
+
condition: (e) => e.message.toLowerCase().includes("network") || e.message.toLowerCase().includes("fetch") || e.message.toLowerCase().includes("failed to fetch") || e.errorType === "TypeError" && e.message.includes("fetch"),
|
|
1348
|
+
priority: 9,
|
|
1349
|
+
questions: [
|
|
1350
|
+
{
|
|
1351
|
+
id: "internet_working",
|
|
1352
|
+
text: "Sua internet est\xE1 funcionando normalmente?",
|
|
1353
|
+
type: "boolean"
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
id: "using_vpn",
|
|
1357
|
+
text: "Voc\xEA est\xE1 usando VPN ou proxy?",
|
|
1358
|
+
type: "boolean"
|
|
1359
|
+
}
|
|
1360
|
+
]
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
id: "timeout_error",
|
|
1364
|
+
condition: (e) => e.message.toLowerCase().includes("timeout") || e.message.toLowerCase().includes("timed out"),
|
|
1365
|
+
priority: 8,
|
|
1366
|
+
questions: [
|
|
1367
|
+
{
|
|
1368
|
+
id: "page_slow",
|
|
1369
|
+
text: "A p\xE1gina estava lenta antes do erro?",
|
|
1370
|
+
type: "boolean"
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
id: "first_time",
|
|
1374
|
+
text: "\xC9 a primeira vez que isso acontece?",
|
|
1375
|
+
type: "boolean"
|
|
1376
|
+
}
|
|
1377
|
+
]
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
id: "auth_error",
|
|
1381
|
+
condition: (e) => e.httpCode === 401 || e.httpCode === 403 || e.message.toLowerCase().includes("unauthorized") || e.message.toLowerCase().includes("forbidden"),
|
|
1382
|
+
priority: 7,
|
|
1383
|
+
questions: [
|
|
1384
|
+
{
|
|
1385
|
+
id: "logged_in",
|
|
1386
|
+
text: "Voc\xEA estava logado no sistema?",
|
|
1387
|
+
type: "boolean"
|
|
1388
|
+
},
|
|
1389
|
+
{
|
|
1390
|
+
id: "session_expired",
|
|
1391
|
+
text: "Ficou muito tempo sem usar o sistema?",
|
|
1392
|
+
type: "boolean"
|
|
1393
|
+
}
|
|
1394
|
+
]
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
id: "not_found",
|
|
1398
|
+
condition: (e) => e.httpCode === 404,
|
|
1399
|
+
priority: 6,
|
|
1400
|
+
questions: [
|
|
1401
|
+
{
|
|
1402
|
+
id: "how_arrived",
|
|
1403
|
+
text: "Como voc\xEA chegou nessa p\xE1gina?",
|
|
1404
|
+
type: "select",
|
|
1405
|
+
options: ["Digitei o link", "Cliquei em um bot\xE3o", "Usei o menu", "Outro"]
|
|
1406
|
+
}
|
|
1407
|
+
]
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
id: "validation_error",
|
|
1411
|
+
condition: (e) => e.httpCode === 400 || e.httpCode === 422 || e.message.toLowerCase().includes("validation") || e.message.toLowerCase().includes("invalid"),
|
|
1412
|
+
priority: 5,
|
|
1413
|
+
questions: [
|
|
1414
|
+
{
|
|
1415
|
+
id: "what_filled",
|
|
1416
|
+
text: "O que voc\xEA preencheu no formul\xE1rio?",
|
|
1417
|
+
type: "text"
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
id: "special_chars",
|
|
1421
|
+
text: "Usou caracteres especiais ou emojis?",
|
|
1422
|
+
type: "boolean"
|
|
1423
|
+
}
|
|
1424
|
+
]
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
id: "ui_error",
|
|
1428
|
+
condition: (e) => e.errorType === "TypeError" || e.message.toLowerCase().includes("undefined") || e.message.toLowerCase().includes("null"),
|
|
1429
|
+
priority: 4,
|
|
1430
|
+
questions: [
|
|
1431
|
+
{
|
|
1432
|
+
id: "clicked_what",
|
|
1433
|
+
text: "Voc\xEA clicou em algo espec\xEDfico?",
|
|
1434
|
+
type: "text"
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
id: "page_loaded",
|
|
1438
|
+
text: "A p\xE1gina tinha carregado completamente?",
|
|
1439
|
+
type: "boolean"
|
|
1440
|
+
}
|
|
1441
|
+
]
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
id: "load_error",
|
|
1445
|
+
condition: (e) => e.message.toLowerCase().includes("chunk") || e.message.toLowerCase().includes("loading") || e.message.toLowerCase().includes("script"),
|
|
1446
|
+
priority: 3,
|
|
1447
|
+
questions: [
|
|
1448
|
+
{
|
|
1449
|
+
id: "refreshed",
|
|
1450
|
+
text: "Voc\xEA tentou atualizar a p\xE1gina?",
|
|
1451
|
+
type: "boolean"
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
id: "browser",
|
|
1455
|
+
text: "Qual navegador voc\xEA est\xE1 usando?",
|
|
1456
|
+
type: "select",
|
|
1457
|
+
options: ["Chrome", "Firefox", "Safari", "Edge", "Outro"]
|
|
1458
|
+
}
|
|
1459
|
+
]
|
|
1460
|
+
}
|
|
1461
|
+
];
|
|
1462
|
+
var FALLBACK_QUESTIONS = [
|
|
1463
|
+
{
|
|
1464
|
+
id: "happened_before",
|
|
1465
|
+
text: "Isso j\xE1 aconteceu antes?",
|
|
1466
|
+
type: "boolean"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
id: "additional_info",
|
|
1470
|
+
text: "Algo mais que possa nos ajudar?",
|
|
1471
|
+
type: "text",
|
|
1472
|
+
required: false
|
|
1473
|
+
}
|
|
1474
|
+
];
|
|
1475
|
+
var QuestionEngine = class {
|
|
1476
|
+
inferQuestions(message, payload) {
|
|
1477
|
+
const context = {
|
|
1478
|
+
message,
|
|
1479
|
+
type: "error",
|
|
1480
|
+
httpCode: payload == null ? void 0 : payload.httpCode,
|
|
1481
|
+
errorType: payload == null ? void 0 : payload.errorType,
|
|
1482
|
+
stack: payload == null ? void 0 : payload.stack
|
|
1483
|
+
};
|
|
1484
|
+
const matchedRules = QUESTION_RULES.filter((rule) => rule.condition(context)).sort((a, b) => b.priority - a.priority);
|
|
1485
|
+
const questions = [...BASE_QUESTIONS];
|
|
1486
|
+
const addedIds = new Set(questions.map((q) => q.id));
|
|
1487
|
+
for (const rule of matchedRules.slice(0, 2)) {
|
|
1488
|
+
for (const question of rule.questions) {
|
|
1489
|
+
if (!addedIds.has(question.id)) {
|
|
1490
|
+
questions.push(question);
|
|
1491
|
+
addedIds.add(question.id);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (questions.length < 3) {
|
|
1496
|
+
for (const question of FALLBACK_QUESTIONS) {
|
|
1497
|
+
if (!addedIds.has(question.id) && questions.length < 4) {
|
|
1498
|
+
questions.push(question);
|
|
1499
|
+
addedIds.add(question.id);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return questions.slice(0, 4);
|
|
1504
|
+
}
|
|
1505
|
+
shouldShowWidget(message, payload, backendDecision) {
|
|
1506
|
+
if (backendDecision !== void 0) {
|
|
1507
|
+
return backendDecision;
|
|
1508
|
+
}
|
|
1509
|
+
if ((payload == null ? void 0 : payload.httpCode) !== void 0) {
|
|
1510
|
+
if (payload.httpCode >= 500) return true;
|
|
1511
|
+
if (payload.httpCode === 400 || payload.httpCode === 422) return true;
|
|
1512
|
+
}
|
|
1513
|
+
const criticalPatterns = [
|
|
1514
|
+
"unexpected",
|
|
1515
|
+
"critical",
|
|
1516
|
+
"fatal",
|
|
1517
|
+
"crash",
|
|
1518
|
+
"failed to",
|
|
1519
|
+
"cannot read",
|
|
1520
|
+
"cannot access",
|
|
1521
|
+
"undefined is not",
|
|
1522
|
+
"null is not"
|
|
1523
|
+
];
|
|
1524
|
+
const lowerMessage = message.toLowerCase();
|
|
1525
|
+
return criticalPatterns.some((pattern) => lowerMessage.includes(pattern));
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
// src/widget/styles.ts
|
|
1530
|
+
function injectStyles(theme) {
|
|
1531
|
+
if (document.getElementById("blinker-widget-styles")) return;
|
|
1532
|
+
const styles = document.createElement("style");
|
|
1533
|
+
styles.id = "blinker-widget-styles";
|
|
1534
|
+
styles.textContent = getStyles(theme);
|
|
1535
|
+
document.head.appendChild(styles);
|
|
1536
|
+
}
|
|
1537
|
+
function removeStyles() {
|
|
1538
|
+
const styles = document.getElementById("blinker-widget-styles");
|
|
1539
|
+
if (styles) styles.remove();
|
|
1540
|
+
}
|
|
1541
|
+
function updateStyles(theme) {
|
|
1542
|
+
const styles = document.getElementById("blinker-widget-styles");
|
|
1543
|
+
if (styles) {
|
|
1544
|
+
styles.textContent = getStyles(theme);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function getStyles(theme) {
|
|
1548
|
+
return `
|
|
1549
|
+
@keyframes blinker-breathe {
|
|
1550
|
+
0%, 100% { transform: scale(1); }
|
|
1551
|
+
50% { transform: scale(1.05); }
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
@keyframes blinker-pulse {
|
|
1555
|
+
0%, 100% { opacity: 1; }
|
|
1556
|
+
50% { opacity: 0.7; }
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
@keyframes blinker-float {
|
|
1560
|
+
0%, 100% { transform: translateY(0); }
|
|
1561
|
+
50% { transform: translateY(-5px); }
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
@keyframes blinker-fade-in {
|
|
1565
|
+
from { opacity: 0; transform: scale(0.8) translateY(20px); }
|
|
1566
|
+
to { opacity: 1; transform: scale(1) translateY(0); }
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
@keyframes blinker-fade-out {
|
|
1570
|
+
from { opacity: 1; transform: scale(1) translateY(0); }
|
|
1571
|
+
to { opacity: 0; transform: scale(0.8) translateY(20px); }
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
@keyframes blinker-slide-up {
|
|
1575
|
+
from { opacity: 0; transform: translateY(30px); }
|
|
1576
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
@keyframes blinker-eye-blink {
|
|
1580
|
+
0%, 45%, 55%, 100% { transform: scaleY(1); }
|
|
1581
|
+
50% { transform: scaleY(0.1); }
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
@keyframes blinker-glow {
|
|
1585
|
+
0%, 100% { filter: drop-shadow(0 0 8px ${theme.primary}40); }
|
|
1586
|
+
50% { filter: drop-shadow(0 0 15px ${theme.primary}60); }
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.blinker-widget-container {
|
|
1590
|
+
position: fixed;
|
|
1591
|
+
z-index: 999999;
|
|
1592
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.blinker-widget-container.bottom-right {
|
|
1596
|
+
bottom: 20px;
|
|
1597
|
+
right: 20px;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
.blinker-widget-container.bottom-left {
|
|
1601
|
+
bottom: 20px;
|
|
1602
|
+
left: 20px;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
.blinker-avatar {
|
|
1606
|
+
width: 60px;
|
|
1607
|
+
height: 60px;
|
|
1608
|
+
cursor: pointer;
|
|
1609
|
+
animation: blinker-float 3s ease-in-out infinite, blinker-glow 2s ease-in-out infinite;
|
|
1610
|
+
transition: transform 0.3s ease;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
.blinker-avatar:hover {
|
|
1614
|
+
transform: scale(1.1);
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
.blinker-avatar.minimized {
|
|
1618
|
+
width: 40px;
|
|
1619
|
+
height: 40px;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.blinker-avatar-body {
|
|
1623
|
+
fill: ${theme.primary};
|
|
1624
|
+
animation: blinker-breathe 4s ease-in-out infinite;
|
|
1625
|
+
transform-origin: center;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
.blinker-avatar-eye {
|
|
1629
|
+
fill: ${theme.background};
|
|
1630
|
+
animation: blinker-eye-blink 4s ease-in-out infinite;
|
|
1631
|
+
transform-origin: center;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
.blinker-avatar-eye-left {
|
|
1635
|
+
animation-delay: 0.1s;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.blinker-avatar-pupil {
|
|
1639
|
+
fill: ${theme.text};
|
|
1640
|
+
transition: transform 0.1s ease;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
.blinker-avatar-mouth {
|
|
1644
|
+
stroke: ${theme.background};
|
|
1645
|
+
stroke-width: 2;
|
|
1646
|
+
fill: none;
|
|
1647
|
+
stroke-linecap: round;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.blinker-avatar-glow {
|
|
1651
|
+
fill: ${theme.primary};
|
|
1652
|
+
opacity: 0.3;
|
|
1653
|
+
animation: blinker-pulse 2s ease-in-out infinite;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
.blinker-dialog-overlay {
|
|
1657
|
+
position: fixed;
|
|
1658
|
+
top: 0;
|
|
1659
|
+
left: 0;
|
|
1660
|
+
right: 0;
|
|
1661
|
+
bottom: 0;
|
|
1662
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1663
|
+
z-index: 999998;
|
|
1664
|
+
animation: blinker-fade-in 0.3s ease;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
.blinker-dialog {
|
|
1668
|
+
position: fixed;
|
|
1669
|
+
z-index: 999999;
|
|
1670
|
+
width: 340px;
|
|
1671
|
+
max-width: calc(100vw - 40px);
|
|
1672
|
+
background: ${theme.background};
|
|
1673
|
+
border-radius: 16px;
|
|
1674
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
1675
|
+
animation: blinker-slide-up 0.4s ease;
|
|
1676
|
+
overflow: hidden;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.blinker-dialog.bottom-right {
|
|
1680
|
+
bottom: 90px;
|
|
1681
|
+
right: 20px;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.blinker-dialog.bottom-left {
|
|
1685
|
+
bottom: 90px;
|
|
1686
|
+
left: 20px;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
.blinker-dialog-header {
|
|
1690
|
+
padding: 20px 20px 0;
|
|
1691
|
+
display: flex;
|
|
1692
|
+
align-items: flex-start;
|
|
1693
|
+
gap: 12px;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
.blinker-dialog-avatar {
|
|
1697
|
+
width: 40px;
|
|
1698
|
+
height: 40px;
|
|
1699
|
+
flex-shrink: 0;
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
.blinker-dialog-title {
|
|
1703
|
+
flex: 1;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
.blinker-dialog-greeting {
|
|
1707
|
+
font-size: 16px;
|
|
1708
|
+
font-weight: 600;
|
|
1709
|
+
color: ${theme.text};
|
|
1710
|
+
margin: 0 0 4px;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
.blinker-dialog-subtext {
|
|
1714
|
+
font-size: 13px;
|
|
1715
|
+
color: ${theme.text}99;
|
|
1716
|
+
margin: 0;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
.blinker-dialog-close {
|
|
1720
|
+
background: none;
|
|
1721
|
+
border: none;
|
|
1722
|
+
padding: 4px;
|
|
1723
|
+
cursor: pointer;
|
|
1724
|
+
color: ${theme.text}66;
|
|
1725
|
+
border-radius: 6px;
|
|
1726
|
+
transition: all 0.2s;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
.blinker-dialog-close:hover {
|
|
1730
|
+
background: ${theme.text}10;
|
|
1731
|
+
color: ${theme.text};
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
.blinker-dialog-body {
|
|
1735
|
+
padding: 20px;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
.blinker-question {
|
|
1739
|
+
margin-bottom: 16px;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
.blinker-question:last-child {
|
|
1743
|
+
margin-bottom: 0;
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
.blinker-question-label {
|
|
1747
|
+
display: block;
|
|
1748
|
+
font-size: 14px;
|
|
1749
|
+
font-weight: 500;
|
|
1750
|
+
color: ${theme.text};
|
|
1751
|
+
margin-bottom: 8px;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
.blinker-question-input {
|
|
1755
|
+
width: 100%;
|
|
1756
|
+
padding: 10px 12px;
|
|
1757
|
+
border: 1px solid ${theme.text}20;
|
|
1758
|
+
border-radius: 8px;
|
|
1759
|
+
font-size: 14px;
|
|
1760
|
+
color: ${theme.text};
|
|
1761
|
+
background: ${theme.background};
|
|
1762
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
1763
|
+
box-sizing: border-box;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
.blinker-question-input:focus {
|
|
1767
|
+
outline: none;
|
|
1768
|
+
border-color: ${theme.primary};
|
|
1769
|
+
box-shadow: 0 0 0 3px ${theme.primary}20;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
.blinker-question-input::placeholder {
|
|
1773
|
+
color: ${theme.text}50;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
.blinker-question-textarea {
|
|
1777
|
+
resize: vertical;
|
|
1778
|
+
min-height: 80px;
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
.blinker-question-select {
|
|
1782
|
+
appearance: none;
|
|
1783
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23666' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
|
|
1784
|
+
background-repeat: no-repeat;
|
|
1785
|
+
background-position: right 12px center;
|
|
1786
|
+
padding-right: 36px;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
.blinker-question-boolean {
|
|
1790
|
+
display: flex;
|
|
1791
|
+
gap: 8px;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
.blinker-question-boolean button {
|
|
1795
|
+
flex: 1;
|
|
1796
|
+
padding: 10px;
|
|
1797
|
+
border: 1px solid ${theme.text}20;
|
|
1798
|
+
border-radius: 8px;
|
|
1799
|
+
background: ${theme.background};
|
|
1800
|
+
color: ${theme.text};
|
|
1801
|
+
font-size: 14px;
|
|
1802
|
+
cursor: pointer;
|
|
1803
|
+
transition: all 0.2s;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
.blinker-question-boolean button:hover {
|
|
1807
|
+
border-color: ${theme.primary};
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
.blinker-question-boolean button.selected {
|
|
1811
|
+
background: ${theme.primary};
|
|
1812
|
+
border-color: ${theme.primary};
|
|
1813
|
+
color: white;
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
.blinker-dialog-footer {
|
|
1817
|
+
padding: 16px 20px 20px;
|
|
1818
|
+
display: flex;
|
|
1819
|
+
gap: 8px;
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
.blinker-btn {
|
|
1823
|
+
flex: 1;
|
|
1824
|
+
padding: 12px 16px;
|
|
1825
|
+
border-radius: 8px;
|
|
1826
|
+
font-size: 14px;
|
|
1827
|
+
font-weight: 500;
|
|
1828
|
+
cursor: pointer;
|
|
1829
|
+
transition: all 0.2s;
|
|
1830
|
+
border: none;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
.blinker-btn-primary {
|
|
1834
|
+
background: ${theme.accent};
|
|
1835
|
+
color: white;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
.blinker-btn-primary:hover {
|
|
1839
|
+
filter: brightness(1.1);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
.blinker-btn-primary:disabled {
|
|
1843
|
+
opacity: 0.5;
|
|
1844
|
+
cursor: not-allowed;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
.blinker-btn-secondary {
|
|
1848
|
+
background: ${theme.text}10;
|
|
1849
|
+
color: ${theme.text}99;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
.blinker-btn-secondary:hover {
|
|
1853
|
+
background: ${theme.text}20;
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
.blinker-thank-you {
|
|
1857
|
+
text-align: center;
|
|
1858
|
+
padding: 40px 20px;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
.blinker-thank-you-icon {
|
|
1862
|
+
width: 48px;
|
|
1863
|
+
height: 48px;
|
|
1864
|
+
margin: 0 auto 16px;
|
|
1865
|
+
color: ${theme.accent};
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
.blinker-thank-you-text {
|
|
1869
|
+
font-size: 16px;
|
|
1870
|
+
color: ${theme.text};
|
|
1871
|
+
margin: 0;
|
|
1872
|
+
}
|
|
1873
|
+
`;
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
// src/widget/Widget.ts
|
|
1877
|
+
var STORAGE_KEY = "blinker_widget_state";
|
|
1878
|
+
var Widget = class {
|
|
1879
|
+
constructor(config) {
|
|
1880
|
+
this.container = null;
|
|
1881
|
+
this.avatar = null;
|
|
1882
|
+
this.dialog = null;
|
|
1883
|
+
this.onFeedbackSubmit = null;
|
|
1884
|
+
this.config = { ...DEFAULT_WIDGET_CONFIG, ...config };
|
|
1885
|
+
this.state = this.loadState();
|
|
1886
|
+
this.questionEngine = new QuestionEngine();
|
|
1887
|
+
}
|
|
1888
|
+
init(onFeedbackSubmit) {
|
|
1889
|
+
if (!this.config.enabled) return;
|
|
1890
|
+
if (typeof document === "undefined") return;
|
|
1891
|
+
this.onFeedbackSubmit = onFeedbackSubmit || null;
|
|
1892
|
+
injectStyles(this.config.theme);
|
|
1893
|
+
this.createContainer();
|
|
1894
|
+
}
|
|
1895
|
+
createContainer() {
|
|
1896
|
+
if (this.container) return;
|
|
1897
|
+
this.container = document.createElement("div");
|
|
1898
|
+
this.container.className = `blinker-widget-container ${this.config.position}`;
|
|
1899
|
+
document.body.appendChild(this.container);
|
|
1900
|
+
this.avatar = new Avatar(this.config.theme, () => this.handleAvatarClick());
|
|
1901
|
+
const avatarElement = this.avatar.create();
|
|
1902
|
+
this.container.appendChild(avatarElement);
|
|
1903
|
+
this.dialog = new Dialog(
|
|
1904
|
+
this.config,
|
|
1905
|
+
(feedback) => this.handleFeedbackSubmit(feedback),
|
|
1906
|
+
() => this.handleDialogClose()
|
|
1907
|
+
);
|
|
1908
|
+
this.hide();
|
|
1909
|
+
}
|
|
1910
|
+
show(errorId, message, payload, backendDecision) {
|
|
1911
|
+
if (!this.config.enabled) return;
|
|
1912
|
+
if (!this.container || !this.avatar) return;
|
|
1913
|
+
const shouldShow = this.questionEngine.shouldShowWidget(message, payload, backendDecision);
|
|
1914
|
+
if (!shouldShow) return;
|
|
1915
|
+
if (!this.canShowWidget()) return;
|
|
1916
|
+
const questions = this.questionEngine.inferQuestions(message, payload);
|
|
1917
|
+
this.state.currentErrorId = errorId;
|
|
1918
|
+
this.state.questions = questions;
|
|
1919
|
+
this.state.isVisible = true;
|
|
1920
|
+
this.state.showCount++;
|
|
1921
|
+
this.state.lastShowTime = Date.now();
|
|
1922
|
+
this.saveState();
|
|
1923
|
+
this.container.style.display = "block";
|
|
1924
|
+
this.avatar.setMinimized(false);
|
|
1925
|
+
}
|
|
1926
|
+
hide() {
|
|
1927
|
+
if (!this.container) return;
|
|
1928
|
+
this.state.isVisible = false;
|
|
1929
|
+
this.state.isDialogOpen = false;
|
|
1930
|
+
this.container.style.display = "none";
|
|
1931
|
+
}
|
|
1932
|
+
handleAvatarClick() {
|
|
1933
|
+
var _a;
|
|
1934
|
+
if (!this.dialog || !this.state.currentErrorId) return;
|
|
1935
|
+
if (this.state.isDialogOpen) {
|
|
1936
|
+
this.dialog.close();
|
|
1937
|
+
} else {
|
|
1938
|
+
this.state.isDialogOpen = true;
|
|
1939
|
+
this.dialog.open(this.state.currentErrorId, this.state.questions);
|
|
1940
|
+
(_a = this.avatar) == null ? void 0 : _a.setMinimized(true);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
handleFeedbackSubmit(feedback) {
|
|
1944
|
+
if (this.onFeedbackSubmit) {
|
|
1945
|
+
this.onFeedbackSubmit(feedback);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
handleDialogClose() {
|
|
1949
|
+
var _a;
|
|
1950
|
+
this.state.isDialogOpen = false;
|
|
1951
|
+
(_a = this.avatar) == null ? void 0 : _a.setMinimized(false);
|
|
1952
|
+
setTimeout(() => {
|
|
1953
|
+
this.hide();
|
|
1954
|
+
}, 500);
|
|
1955
|
+
}
|
|
1956
|
+
canShowWidget() {
|
|
1957
|
+
if (this.state.showCount >= this.config.maxShowsPerSession) {
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
if (this.state.lastShowTime) {
|
|
1961
|
+
const cooldownMs = this.config.cooldownMinutes * 60 * 1e3;
|
|
1962
|
+
const timeSinceLastShow = Date.now() - this.state.lastShowTime;
|
|
1963
|
+
if (timeSinceLastShow < cooldownMs) {
|
|
1964
|
+
return false;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
return true;
|
|
1968
|
+
}
|
|
1969
|
+
loadState() {
|
|
1970
|
+
const defaultState = {
|
|
1971
|
+
isVisible: false,
|
|
1972
|
+
isMinimized: false,
|
|
1973
|
+
isDialogOpen: false,
|
|
1974
|
+
currentErrorId: null,
|
|
1975
|
+
questions: [],
|
|
1976
|
+
showCount: 0,
|
|
1977
|
+
lastShowTime: null
|
|
1978
|
+
};
|
|
1979
|
+
if (typeof sessionStorage === "undefined") return defaultState;
|
|
1980
|
+
try {
|
|
1981
|
+
const stored = sessionStorage.getItem(STORAGE_KEY);
|
|
1982
|
+
if (stored) {
|
|
1983
|
+
const parsed = JSON.parse(stored);
|
|
1984
|
+
return {
|
|
1985
|
+
...defaultState,
|
|
1986
|
+
showCount: parsed.showCount || 0,
|
|
1987
|
+
lastShowTime: parsed.lastShowTime || null
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
} catch (e) {
|
|
1991
|
+
}
|
|
1992
|
+
return defaultState;
|
|
1993
|
+
}
|
|
1994
|
+
saveState() {
|
|
1995
|
+
if (typeof sessionStorage === "undefined") return;
|
|
1996
|
+
try {
|
|
1997
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
1998
|
+
showCount: this.state.showCount,
|
|
1999
|
+
lastShowTime: this.state.lastShowTime
|
|
2000
|
+
}));
|
|
2001
|
+
} catch (e) {
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
updateConfig(config) {
|
|
2005
|
+
var _a, _b;
|
|
2006
|
+
this.config = { ...this.config, ...config };
|
|
2007
|
+
if (config.theme) {
|
|
2008
|
+
updateStyles(this.config.theme);
|
|
2009
|
+
(_a = this.avatar) == null ? void 0 : _a.updateTheme(this.config.theme);
|
|
2010
|
+
}
|
|
2011
|
+
(_b = this.dialog) == null ? void 0 : _b.updateConfig(this.config);
|
|
2012
|
+
}
|
|
2013
|
+
getConfig() {
|
|
2014
|
+
return { ...this.config };
|
|
2015
|
+
}
|
|
2016
|
+
isVisible() {
|
|
2017
|
+
return this.state.isVisible;
|
|
2018
|
+
}
|
|
2019
|
+
isDialogOpen() {
|
|
2020
|
+
return this.state.isDialogOpen;
|
|
2021
|
+
}
|
|
2022
|
+
destroy() {
|
|
2023
|
+
var _a, _b;
|
|
2024
|
+
(_a = this.avatar) == null ? void 0 : _a.destroy();
|
|
2025
|
+
(_b = this.dialog) == null ? void 0 : _b.close();
|
|
2026
|
+
if (this.container) {
|
|
2027
|
+
this.container.remove();
|
|
2028
|
+
this.container = null;
|
|
2029
|
+
}
|
|
2030
|
+
removeStyles();
|
|
2031
|
+
this.avatar = null;
|
|
2032
|
+
this.dialog = null;
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
|
|
2036
|
+
// src/core/Blinker.ts
|
|
184
2037
|
var Blinker = class {
|
|
185
2038
|
constructor() {
|
|
186
2039
|
this.config = null;
|
|
187
2040
|
this.initialized = false;
|
|
2041
|
+
this.filterEngine = null;
|
|
2042
|
+
this.queue = null;
|
|
2043
|
+
this.sessionManager = null;
|
|
2044
|
+
this.userContext = null;
|
|
2045
|
+
this.rateLimiter = null;
|
|
2046
|
+
this.sampler = null;
|
|
2047
|
+
this.deduplicator = null;
|
|
2048
|
+
this.widget = null;
|
|
188
2049
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Initialize the Blinker SDK
|
|
191
|
-
* @param config Configuration options
|
|
192
|
-
*/
|
|
193
2050
|
init(config) {
|
|
2051
|
+
var _a, _b;
|
|
194
2052
|
if (this.initialized) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
2053
|
+
const logger2 = getLogger();
|
|
2054
|
+
logger2.warn("SDK already initialized");
|
|
198
2055
|
return;
|
|
199
2056
|
}
|
|
200
2057
|
if (!config.token) {
|
|
201
2058
|
throw new Error("[Blinker] Token is required");
|
|
202
2059
|
}
|
|
203
|
-
if (!config.api) {
|
|
204
|
-
throw new Error("[Blinker] API endpoint is required");
|
|
205
|
-
}
|
|
206
2060
|
this.config = {
|
|
207
|
-
|
|
208
|
-
captureRejections: true,
|
|
209
|
-
debug: false,
|
|
2061
|
+
...DEFAULTS,
|
|
210
2062
|
...config
|
|
211
2063
|
};
|
|
2064
|
+
setGlobalLogger((_a = this.config.debug) != null ? _a : false);
|
|
2065
|
+
const logger = getLogger();
|
|
2066
|
+
this.filterEngine = new FilterEngine(config.filters);
|
|
2067
|
+
logger.log("Filter engine initialized");
|
|
2068
|
+
this.queue = new EventQueue(config.token, {
|
|
2069
|
+
enableBatching: this.config.enableBatching,
|
|
2070
|
+
batchSize: this.config.batchSize,
|
|
2071
|
+
flushInterval: this.config.flushInterval,
|
|
2072
|
+
enableOfflineStorage: this.config.enableOfflineStorage,
|
|
2073
|
+
maxStoredEvents: this.config.maxStoredEvents
|
|
2074
|
+
});
|
|
2075
|
+
logger.log("Event queue initialized");
|
|
2076
|
+
this.sessionManager = new SessionManager();
|
|
2077
|
+
this.userContext = new UserContext(this.sessionManager);
|
|
2078
|
+
logger.log("Context managers initialized");
|
|
2079
|
+
this.rateLimiter = new RateLimiter(this.config.maxEventsPerMinute);
|
|
2080
|
+
this.sampler = new Sampler(this.config.sampleRate);
|
|
2081
|
+
logger.log("Sampling modules initialized");
|
|
2082
|
+
this.deduplicator = new Deduplicator(
|
|
2083
|
+
this.config.enableDeduplication,
|
|
2084
|
+
this.config.deduplicationWindow
|
|
2085
|
+
);
|
|
2086
|
+
logger.log("Deduplicator initialized");
|
|
212
2087
|
if (this.config.captureErrors || this.config.captureRejections) {
|
|
213
2088
|
setupErrorHandlers(
|
|
214
|
-
(type, message, payload) =>
|
|
215
|
-
this.track(type, message, payload);
|
|
216
|
-
},
|
|
2089
|
+
(type, message, payload) => this.handleCapturedError(type, message, payload),
|
|
217
2090
|
{
|
|
218
2091
|
captureErrors: this.config.captureErrors,
|
|
219
|
-
captureRejections: this.config.captureRejections
|
|
220
|
-
debug: this.config.debug
|
|
2092
|
+
captureRejections: this.config.captureRejections
|
|
221
2093
|
}
|
|
222
2094
|
);
|
|
2095
|
+
logger.log("Error handlers set up");
|
|
2096
|
+
}
|
|
2097
|
+
if ((_b = this.config.widget) == null ? void 0 : _b.enabled) {
|
|
2098
|
+
this.widget = new Widget(this.config.widget);
|
|
2099
|
+
this.widget.init((feedback) => this.handleWidgetFeedback(feedback));
|
|
2100
|
+
logger.log("Widget initialized");
|
|
223
2101
|
}
|
|
224
2102
|
this.initialized = true;
|
|
225
|
-
|
|
226
|
-
|
|
2103
|
+
logger.log("SDK initialized successfully");
|
|
2104
|
+
}
|
|
2105
|
+
handleCapturedError(type, message, payload) {
|
|
2106
|
+
var _a, _b, _c, _d, _e;
|
|
2107
|
+
if (!((_a = this.filterEngine) == null ? void 0 : _a.shouldCapture(message, payload.errorType, payload.httpCode))) {
|
|
2108
|
+
return;
|
|
2109
|
+
}
|
|
2110
|
+
const dedupResult = (_b = this.deduplicator) == null ? void 0 : _b.check(message, payload.stack);
|
|
2111
|
+
if (dedupResult && !dedupResult.shouldSend) {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
const errorId = generateUniqueId();
|
|
2115
|
+
this.track(type, message, {
|
|
2116
|
+
...payload,
|
|
2117
|
+
errorId,
|
|
2118
|
+
count: dedupResult == null ? void 0 : dedupResult.count
|
|
2119
|
+
});
|
|
2120
|
+
if (this.widget && ((_d = (_c = this.config) == null ? void 0 : _c.widget) == null ? void 0 : _d.enabled)) {
|
|
2121
|
+
setTimeout(() => {
|
|
2122
|
+
var _a2;
|
|
2123
|
+
(_a2 = this.widget) == null ? void 0 : _a2.show(errorId, message, payload);
|
|
2124
|
+
}, (_e = this.config.widget.delay) != null ? _e : 2e3);
|
|
227
2125
|
}
|
|
228
2126
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
2127
|
+
handleWidgetFeedback(feedback) {
|
|
2128
|
+
const logger = getLogger();
|
|
2129
|
+
logger.log("Widget feedback received:", feedback.errorId);
|
|
2130
|
+
this.track("feedback", "User feedback submitted", {
|
|
2131
|
+
errorId: feedback.errorId,
|
|
2132
|
+
answers: feedback.answers,
|
|
2133
|
+
submittedAt: feedback.submittedAt
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
235
2136
|
track(type, message, payload) {
|
|
236
|
-
var _a;
|
|
2137
|
+
var _a, _b, _c;
|
|
237
2138
|
if (!this.initialized || !this.config) {
|
|
238
|
-
if ((_a = this.config) == null ? void 0 : _a.debug) {
|
|
239
|
-
console.warn("[Blinker] SDK not initialized. Call blinker.init() first.");
|
|
240
|
-
}
|
|
241
2139
|
return Promise.resolve({ success: false, error: "SDK not initialized" });
|
|
242
2140
|
}
|
|
2141
|
+
const logger = getLogger();
|
|
2142
|
+
const isError = type === "error";
|
|
2143
|
+
if (!((_a = this.rateLimiter) == null ? void 0 : _a.allow())) {
|
|
2144
|
+
logger.log("Event dropped due to rate limit");
|
|
2145
|
+
return Promise.resolve({ success: false, error: "Rate limited" });
|
|
2146
|
+
}
|
|
2147
|
+
if (!((_b = this.sampler) == null ? void 0 : _b.shouldSample(isError))) {
|
|
2148
|
+
return Promise.resolve({ success: true, error: "Sampled out" });
|
|
2149
|
+
}
|
|
2150
|
+
const context = (_c = this.userContext) == null ? void 0 : _c.getEventContext();
|
|
243
2151
|
const event = {
|
|
2152
|
+
id: generateUniqueId(),
|
|
244
2153
|
type,
|
|
245
2154
|
message,
|
|
246
2155
|
payload,
|
|
247
2156
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
248
|
-
url:
|
|
249
|
-
userAgent:
|
|
2157
|
+
url: getCurrentUrl(),
|
|
2158
|
+
userAgent: getUserAgent(),
|
|
2159
|
+
sessionId: context == null ? void 0 : context.sessionId,
|
|
2160
|
+
userId: context == null ? void 0 : context.userId,
|
|
2161
|
+
userTraits: context == null ? void 0 : context.userTraits,
|
|
2162
|
+
context: context == null ? void 0 : context.custom
|
|
250
2163
|
};
|
|
251
|
-
|
|
2164
|
+
logger.log("Tracking event:", type, message.substring(0, 50));
|
|
2165
|
+
return this.queue.add(event);
|
|
252
2166
|
}
|
|
253
|
-
/**
|
|
254
|
-
* Track an error manually
|
|
255
|
-
* @param error Error object or message
|
|
256
|
-
* @param additionalPayload Optional additional data
|
|
257
|
-
*/
|
|
258
2167
|
captureError(error, additionalPayload) {
|
|
2168
|
+
var _a, _b;
|
|
259
2169
|
const message = error instanceof Error ? error.message : error;
|
|
2170
|
+
const errorType = error instanceof Error ? error.name : void 0;
|
|
2171
|
+
const stack = error instanceof Error ? error.stack : void 0;
|
|
2172
|
+
const httpCode = additionalPayload == null ? void 0 : additionalPayload.httpCode;
|
|
2173
|
+
if (!((_a = this.filterEngine) == null ? void 0 : _a.shouldCapture(message, errorType, httpCode))) {
|
|
2174
|
+
return Promise.resolve({ success: true, error: "Filtered out" });
|
|
2175
|
+
}
|
|
2176
|
+
const dedupResult = (_b = this.deduplicator) == null ? void 0 : _b.check(message, stack);
|
|
2177
|
+
if (dedupResult && !dedupResult.shouldSend) {
|
|
2178
|
+
return Promise.resolve({ success: true, error: "Deduplicated" });
|
|
2179
|
+
}
|
|
260
2180
|
const payload = {
|
|
261
|
-
...additionalPayload
|
|
2181
|
+
...additionalPayload,
|
|
2182
|
+
errorType,
|
|
2183
|
+
stack,
|
|
2184
|
+
count: dedupResult == null ? void 0 : dedupResult.count
|
|
262
2185
|
};
|
|
263
|
-
if (error instanceof Error) {
|
|
264
|
-
payload.stack = error.stack;
|
|
265
|
-
payload.errorType = error.name;
|
|
266
|
-
}
|
|
267
2186
|
return this.track("error", message, payload);
|
|
268
2187
|
}
|
|
269
|
-
/**
|
|
270
|
-
* Track a page view
|
|
271
|
-
* @param pageName Optional page name (defaults to current URL path)
|
|
272
|
-
* @param additionalPayload Optional additional data
|
|
273
|
-
*/
|
|
274
2188
|
trackPageView(pageName, additionalPayload) {
|
|
275
|
-
|
|
2189
|
+
var _a;
|
|
2190
|
+
const page = pageName || getCurrentPath();
|
|
2191
|
+
(_a = this.sessionManager) == null ? void 0 : _a.recordPageView();
|
|
276
2192
|
return this.track("pageview", page, {
|
|
277
2193
|
...additionalPayload,
|
|
278
|
-
referrer:
|
|
2194
|
+
referrer: getReferrer()
|
|
279
2195
|
});
|
|
280
2196
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
2197
|
+
identify(userId, traits) {
|
|
2198
|
+
var _a;
|
|
2199
|
+
(_a = this.userContext) == null ? void 0 : _a.identify(userId, traits);
|
|
2200
|
+
}
|
|
2201
|
+
setContext(key, value) {
|
|
2202
|
+
var _a;
|
|
2203
|
+
(_a = this.userContext) == null ? void 0 : _a.setContext(key, value);
|
|
2204
|
+
}
|
|
2205
|
+
getContext() {
|
|
2206
|
+
var _a, _b;
|
|
2207
|
+
return (_b = (_a = this.userContext) == null ? void 0 : _a.getCustomContext()) != null ? _b : {};
|
|
2208
|
+
}
|
|
2209
|
+
clearContext() {
|
|
2210
|
+
var _a;
|
|
2211
|
+
(_a = this.userContext) == null ? void 0 : _a.clearAll();
|
|
2212
|
+
}
|
|
2213
|
+
getSessionId() {
|
|
2214
|
+
var _a, _b;
|
|
2215
|
+
return (_b = (_a = this.sessionManager) == null ? void 0 : _a.getSessionId()) != null ? _b : "";
|
|
2216
|
+
}
|
|
2217
|
+
getSession() {
|
|
2218
|
+
var _a, _b;
|
|
2219
|
+
return (_b = (_a = this.sessionManager) == null ? void 0 : _a.getSession()) != null ? _b : null;
|
|
2220
|
+
}
|
|
2221
|
+
async flush() {
|
|
2222
|
+
var _a;
|
|
2223
|
+
await ((_a = this.queue) == null ? void 0 : _a.flush());
|
|
2224
|
+
}
|
|
2225
|
+
getQueueSize() {
|
|
2226
|
+
var _a, _b;
|
|
2227
|
+
return (_b = (_a = this.queue) == null ? void 0 : _a.size()) != null ? _b : 0;
|
|
2228
|
+
}
|
|
284
2229
|
isInitialized() {
|
|
285
2230
|
return this.initialized;
|
|
286
2231
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Get current configuration (without token for security)
|
|
289
|
-
*/
|
|
290
2232
|
getConfig() {
|
|
291
2233
|
if (!this.config) return null;
|
|
292
2234
|
const { token: _token, ...safeConfig } = this.config;
|
|
293
2235
|
return safeConfig;
|
|
294
2236
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
2237
|
+
getFilters() {
|
|
2238
|
+
var _a, _b;
|
|
2239
|
+
return (_b = (_a = this.filterEngine) == null ? void 0 : _a.getFilters()) != null ? _b : { ...DEFAULT_FILTERS };
|
|
2240
|
+
}
|
|
2241
|
+
showWidget(errorId, message, payload) {
|
|
2242
|
+
var _a;
|
|
2243
|
+
(_a = this.widget) == null ? void 0 : _a.show(errorId, message, payload);
|
|
2244
|
+
}
|
|
2245
|
+
hideWidget() {
|
|
300
2246
|
var _a;
|
|
301
|
-
|
|
2247
|
+
(_a = this.widget) == null ? void 0 : _a.hide();
|
|
2248
|
+
}
|
|
2249
|
+
getWidgetConfig() {
|
|
2250
|
+
var _a, _b;
|
|
2251
|
+
return (_b = (_a = this.widget) == null ? void 0 : _a.getConfig()) != null ? _b : null;
|
|
2252
|
+
}
|
|
2253
|
+
updateWidgetConfig(config) {
|
|
2254
|
+
var _a;
|
|
2255
|
+
(_a = this.widget) == null ? void 0 : _a.updateConfig(config);
|
|
2256
|
+
}
|
|
2257
|
+
isWidgetVisible() {
|
|
2258
|
+
var _a, _b;
|
|
2259
|
+
return (_b = (_a = this.widget) == null ? void 0 : _a.isVisible()) != null ? _b : false;
|
|
2260
|
+
}
|
|
2261
|
+
destroy() {
|
|
2262
|
+
var _a, _b, _c, _d;
|
|
2263
|
+
const logger = getLogger();
|
|
2264
|
+
(_a = this.queue) == null ? void 0 : _a.destroy();
|
|
302
2265
|
teardownErrorHandlers();
|
|
2266
|
+
(_b = this.deduplicator) == null ? void 0 : _b.destroy();
|
|
2267
|
+
(_c = this.sessionManager) == null ? void 0 : _c.endSession();
|
|
2268
|
+
(_d = this.widget) == null ? void 0 : _d.destroy();
|
|
303
2269
|
this.config = null;
|
|
304
2270
|
this.initialized = false;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
2271
|
+
this.filterEngine = null;
|
|
2272
|
+
this.queue = null;
|
|
2273
|
+
this.sessionManager = null;
|
|
2274
|
+
this.userContext = null;
|
|
2275
|
+
this.rateLimiter = null;
|
|
2276
|
+
this.sampler = null;
|
|
2277
|
+
this.deduplicator = null;
|
|
2278
|
+
this.widget = null;
|
|
2279
|
+
logger.log("SDK destroyed");
|
|
2280
|
+
setGlobalLogger(false);
|
|
308
2281
|
}
|
|
309
2282
|
};
|
|
310
2283
|
|