@bentolabs/sdk 1.2.2 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.js +14450 -0
- package/dist/index.browser.js.map +7 -0
- package/dist/index.browser.min.js +74 -0
- package/dist/index.browser.min.js.map +7 -0
- package/dist/index.d.ts +189 -27
- package/dist/index.js +1580 -181
- package/dist/index.js.map +3 -3
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -51,7 +51,9 @@ var index_exports = {};
|
|
|
51
51
|
__export(index_exports, {
|
|
52
52
|
BentoLabsSDK: () => BentoLabsSDK,
|
|
53
53
|
default: () => index_default,
|
|
54
|
-
generateSelector: () => generateSelector
|
|
54
|
+
generateSelector: () => generateSelector,
|
|
55
|
+
generateSelectorFromElement: () => generateSelectorFromElement,
|
|
56
|
+
generateSelectorFromHTML: () => generateSelectorFromHTML
|
|
55
57
|
});
|
|
56
58
|
module.exports = __toCommonJS(index_exports);
|
|
57
59
|
|
|
@@ -9149,10 +9151,145 @@ function quickHash(str) {
|
|
|
9149
9151
|
}
|
|
9150
9152
|
return (hash >>> 0).toString(16);
|
|
9151
9153
|
}
|
|
9152
|
-
function
|
|
9154
|
+
function getParentAcrossShadow(element) {
|
|
9155
|
+
if (element.parentElement) {
|
|
9156
|
+
return element.parentElement;
|
|
9157
|
+
}
|
|
9158
|
+
const root2 = element.getRootNode();
|
|
9159
|
+
if (root2 instanceof ShadowRoot) {
|
|
9160
|
+
return root2.host;
|
|
9161
|
+
}
|
|
9162
|
+
return null;
|
|
9163
|
+
}
|
|
9164
|
+
function isPortalContainer(element) {
|
|
9165
|
+
return element.hasAttribute("data-radix-portal") || element.hasAttribute("data-headlessui-portal") || element.hasAttribute("data-floating-ui-portal") || element.hasAttribute("data-portal") || element.id !== "" && element.id.toLowerCase().includes("portal") || element.classList.contains("portal") || element.getAttribute("role") === "dialog";
|
|
9166
|
+
}
|
|
9167
|
+
function findUniqueAncestor(element, maxDepth = 5) {
|
|
9168
|
+
let current = getParentAcrossShadow(element);
|
|
9169
|
+
let depth = 0;
|
|
9170
|
+
while (current && depth < maxDepth) {
|
|
9171
|
+
if (isPortalContainer(current)) {
|
|
9172
|
+
current = getParentAcrossShadow(current);
|
|
9173
|
+
continue;
|
|
9174
|
+
}
|
|
9175
|
+
const tag = current.tagName.toLowerCase();
|
|
9176
|
+
if (current.id && isStableId(current.id)) {
|
|
9177
|
+
return `${tag}#${current.id}`;
|
|
9178
|
+
}
|
|
9179
|
+
const testId = current.getAttribute("data-testid");
|
|
9180
|
+
if (testId) {
|
|
9181
|
+
return `${tag}[data-testid=${testId}]`;
|
|
9182
|
+
}
|
|
9183
|
+
const dataId = current.getAttribute("data-id");
|
|
9184
|
+
if (dataId) {
|
|
9185
|
+
return `${tag}[data-id=${dataId}]`;
|
|
9186
|
+
}
|
|
9187
|
+
const name = current.getAttribute("name");
|
|
9188
|
+
if (name) {
|
|
9189
|
+
return `${tag}[name=${name}]`;
|
|
9190
|
+
}
|
|
9191
|
+
current = getParentAcrossShadow(current);
|
|
9192
|
+
depth++;
|
|
9193
|
+
}
|
|
9194
|
+
return null;
|
|
9195
|
+
}
|
|
9196
|
+
function findSection(element, maxDepth = 15) {
|
|
9197
|
+
const sectionTags = [
|
|
9198
|
+
"header",
|
|
9199
|
+
"nav",
|
|
9200
|
+
"main",
|
|
9201
|
+
"aside",
|
|
9202
|
+
"footer",
|
|
9203
|
+
"section",
|
|
9204
|
+
"article"
|
|
9205
|
+
];
|
|
9206
|
+
let current = getParentAcrossShadow(element);
|
|
9207
|
+
let depth = 0;
|
|
9208
|
+
while (current && depth < maxDepth) {
|
|
9209
|
+
if (isPortalContainer(current)) {
|
|
9210
|
+
current = getParentAcrossShadow(current);
|
|
9211
|
+
continue;
|
|
9212
|
+
}
|
|
9213
|
+
const tag = current.tagName.toLowerCase();
|
|
9214
|
+
if (sectionTags.includes(tag)) {
|
|
9215
|
+
const label = current.getAttribute("aria-label");
|
|
9216
|
+
return label ? `${tag}[${label}]` : tag;
|
|
9217
|
+
}
|
|
9218
|
+
current = getParentAcrossShadow(current);
|
|
9219
|
+
depth++;
|
|
9220
|
+
}
|
|
9221
|
+
return null;
|
|
9222
|
+
}
|
|
9223
|
+
function findForm(element) {
|
|
9224
|
+
const form = element.closest("form");
|
|
9225
|
+
if (!form) return null;
|
|
9226
|
+
if (form.id && isStableId(form.id)) return form.id;
|
|
9227
|
+
if (form.name) return form.name;
|
|
9228
|
+
return null;
|
|
9229
|
+
}
|
|
9230
|
+
function findRegion(element, maxDepth = 15) {
|
|
9231
|
+
let current = getParentAcrossShadow(element);
|
|
9232
|
+
let depth = 0;
|
|
9233
|
+
while (current && depth < maxDepth) {
|
|
9234
|
+
if (isPortalContainer(current)) {
|
|
9235
|
+
current = getParentAcrossShadow(current);
|
|
9236
|
+
continue;
|
|
9237
|
+
}
|
|
9238
|
+
const role = current.getAttribute("role");
|
|
9239
|
+
if (role === "region" || role === "navigation" || role === "banner" || role === "main" || role === "contentinfo" || role === "complementary") {
|
|
9240
|
+
const label = current.getAttribute("aria-label");
|
|
9241
|
+
return label ? `${role}[${label}]` : role;
|
|
9242
|
+
}
|
|
9243
|
+
current = getParentAcrossShadow(current);
|
|
9244
|
+
depth++;
|
|
9245
|
+
}
|
|
9246
|
+
return null;
|
|
9247
|
+
}
|
|
9248
|
+
function getNthPosition(element) {
|
|
9249
|
+
const parent = element.parentElement;
|
|
9250
|
+
if (!parent) return 1;
|
|
9251
|
+
const tag = element.tagName;
|
|
9252
|
+
const text = getFullText$1(element);
|
|
9253
|
+
let position = 0;
|
|
9254
|
+
for (const sibling of Array.from(parent.children)) {
|
|
9255
|
+
if (sibling.tagName === tag && getFullText$1(sibling) === text) {
|
|
9256
|
+
position++;
|
|
9257
|
+
if (sibling === element) return position;
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
return 1;
|
|
9261
|
+
}
|
|
9262
|
+
function buildBentoSelector(element, pathname) {
|
|
9153
9263
|
const parts = [];
|
|
9154
9264
|
const tag = element.tagName.toLowerCase();
|
|
9155
9265
|
parts.push(tag);
|
|
9266
|
+
if (pathname) {
|
|
9267
|
+
parts.push(`page=${escapeValue(pathname)}`);
|
|
9268
|
+
}
|
|
9269
|
+
let hasContext = false;
|
|
9270
|
+
const ancestor = findUniqueAncestor(element, 5);
|
|
9271
|
+
if (ancestor) {
|
|
9272
|
+
parts.push(`ancestor=${escapeValue(ancestor)}`);
|
|
9273
|
+
hasContext = true;
|
|
9274
|
+
} else {
|
|
9275
|
+
const section = findSection(element, 15);
|
|
9276
|
+
if (section) {
|
|
9277
|
+
parts.push(`section=${escapeValue(section)}`);
|
|
9278
|
+
hasContext = true;
|
|
9279
|
+
} else {
|
|
9280
|
+
const form = findForm(element);
|
|
9281
|
+
if (form) {
|
|
9282
|
+
parts.push(`form=${escapeValue(form)}`);
|
|
9283
|
+
hasContext = true;
|
|
9284
|
+
} else {
|
|
9285
|
+
const region = findRegion(element, 15);
|
|
9286
|
+
if (region) {
|
|
9287
|
+
parts.push(`region=${escapeValue(region)}`);
|
|
9288
|
+
hasContext = true;
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
}
|
|
9292
|
+
}
|
|
9156
9293
|
const testId = element.getAttribute("data-testid");
|
|
9157
9294
|
if (testId) {
|
|
9158
9295
|
parts.push(`testid=${escapeValue(truncate(testId, 50))}`);
|
|
@@ -9227,7 +9364,13 @@ function buildBentoSelector(element) {
|
|
|
9227
9364
|
parts.push(`text=${escapeValue(truncate(text, 50))}`);
|
|
9228
9365
|
}
|
|
9229
9366
|
}
|
|
9230
|
-
if (
|
|
9367
|
+
if (!hasContext) {
|
|
9368
|
+
const nth = getNthPosition(element);
|
|
9369
|
+
if (nth > 1) {
|
|
9370
|
+
parts.push(`nth=${nth}`);
|
|
9371
|
+
}
|
|
9372
|
+
}
|
|
9373
|
+
if (parts.length === 1 || parts.length === 2 && pathname) {
|
|
9231
9374
|
const childSig = getChildSignature(element);
|
|
9232
9375
|
if (childSig) {
|
|
9233
9376
|
parts.push(`children=${childSig}`);
|
|
@@ -9238,7 +9381,11 @@ function buildBentoSelector(element) {
|
|
|
9238
9381
|
}
|
|
9239
9382
|
return parts.join("|");
|
|
9240
9383
|
}
|
|
9241
|
-
function
|
|
9384
|
+
function generateSelectorFromElement(element, pathname) {
|
|
9385
|
+
const path = pathname != null ? pathname : typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
9386
|
+
return buildBentoSelector(element, path);
|
|
9387
|
+
}
|
|
9388
|
+
function generateSelectorFromHTML(outerHTML) {
|
|
9242
9389
|
const parser2 = new DOMParser();
|
|
9243
9390
|
const doc = parser2.parseFromString(outerHTML, "text/html");
|
|
9244
9391
|
const element = doc.body.firstElementChild;
|
|
@@ -9247,6 +9394,9 @@ function generateSelector(outerHTML) {
|
|
|
9247
9394
|
}
|
|
9248
9395
|
return buildBentoSelector(element);
|
|
9249
9396
|
}
|
|
9397
|
+
function generateSelector(outerHTML) {
|
|
9398
|
+
return generateSelectorFromHTML(outerHTML);
|
|
9399
|
+
}
|
|
9250
9400
|
var DEFAULT_OPTIONS = {
|
|
9251
9401
|
maxTextLength: 500,
|
|
9252
9402
|
includeOuterHTML: true,
|
|
@@ -9276,6 +9426,7 @@ function isInputElement(element) {
|
|
|
9276
9426
|
return tagName === "INPUT" || tagName === "TEXTAREA" || tagName === "SELECT";
|
|
9277
9427
|
}
|
|
9278
9428
|
function extractElementContext(element, options = {}) {
|
|
9429
|
+
var _a2;
|
|
9279
9430
|
const opts = __spreadValues(__spreadValues({}, DEFAULT_OPTIONS), options.elementContextOptions);
|
|
9280
9431
|
const {
|
|
9281
9432
|
isClickEvent = false,
|
|
@@ -9339,7 +9490,8 @@ function extractElementContext(element, options = {}) {
|
|
|
9339
9490
|
}
|
|
9340
9491
|
if (isClickEvent) {
|
|
9341
9492
|
try {
|
|
9342
|
-
|
|
9493
|
+
const path = (_a2 = options.pathname) != null ? _a2 : typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
9494
|
+
context.bentoSelector = buildBentoSelector(element, path);
|
|
9343
9495
|
} catch (e) {
|
|
9344
9496
|
}
|
|
9345
9497
|
}
|
|
@@ -10175,6 +10327,7 @@ function initMouseInteractionObserver({
|
|
|
10175
10327
|
let currentPointerType = null;
|
|
10176
10328
|
const getHandler = (eventKey) => {
|
|
10177
10329
|
return (event) => {
|
|
10330
|
+
const pathname = typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
10178
10331
|
const target = getEventTarget(event);
|
|
10179
10332
|
if (isBlocked(target, blockClass, blockSelector, true)) {
|
|
10180
10333
|
return;
|
|
@@ -10225,7 +10378,9 @@ function initMouseInteractionObserver({
|
|
|
10225
10378
|
elementContextOptions,
|
|
10226
10379
|
isClickEvent,
|
|
10227
10380
|
maskInputOptions,
|
|
10228
|
-
maskInputFn
|
|
10381
|
+
maskInputFn,
|
|
10382
|
+
pathname
|
|
10383
|
+
// Pass pathname captured at event time
|
|
10229
10384
|
});
|
|
10230
10385
|
}
|
|
10231
10386
|
callbackWrapper(mouseInteractionCb)(__spreadValues(__spreadValues({
|
|
@@ -12620,6 +12775,641 @@ var { freezePage } = record;
|
|
|
12620
12775
|
var { takeFullSnapshot } = record;
|
|
12621
12776
|
|
|
12622
12777
|
// src/index.ts
|
|
12778
|
+
var PERSISTENCE_KEYS = {
|
|
12779
|
+
DISTINCT_ID: "bentolabs_distinct_id",
|
|
12780
|
+
SUPER_PROPERTIES: "bentolabs_super_properties",
|
|
12781
|
+
OPTED_OUT: "bentolabs_opted_out",
|
|
12782
|
+
USER_PROPERTIES: "bentolabs_user_properties",
|
|
12783
|
+
IS_IDENTIFIED: "bentolabs_is_identified"
|
|
12784
|
+
};
|
|
12785
|
+
var Persistence = class {
|
|
12786
|
+
constructor(mode) {
|
|
12787
|
+
this.mode = mode;
|
|
12788
|
+
this.memoryStorage = /* @__PURE__ */ new Map();
|
|
12789
|
+
}
|
|
12790
|
+
get(key) {
|
|
12791
|
+
if (this.mode === "none") return null;
|
|
12792
|
+
if (this.mode === "memory") {
|
|
12793
|
+
return this.memoryStorage.get(key) || null;
|
|
12794
|
+
}
|
|
12795
|
+
if (this.mode === "localStorage" || this.mode === "localStorage+cookie") {
|
|
12796
|
+
try {
|
|
12797
|
+
const value = localStorage.getItem(key);
|
|
12798
|
+
if (value !== null) return value;
|
|
12799
|
+
} catch (e) {
|
|
12800
|
+
}
|
|
12801
|
+
}
|
|
12802
|
+
if (this.mode === "cookie" || this.mode === "localStorage+cookie") {
|
|
12803
|
+
try {
|
|
12804
|
+
const cookies = document.cookie.split(";");
|
|
12805
|
+
for (const cookie of cookies) {
|
|
12806
|
+
const [cookieKey, cookieValue] = cookie.trim().split("=");
|
|
12807
|
+
if (cookieKey === key) {
|
|
12808
|
+
return decodeURIComponent(cookieValue);
|
|
12809
|
+
}
|
|
12810
|
+
}
|
|
12811
|
+
} catch (e) {
|
|
12812
|
+
}
|
|
12813
|
+
}
|
|
12814
|
+
return null;
|
|
12815
|
+
}
|
|
12816
|
+
set(key, value) {
|
|
12817
|
+
if (this.mode === "none") return;
|
|
12818
|
+
if (this.mode === "memory") {
|
|
12819
|
+
this.memoryStorage.set(key, value);
|
|
12820
|
+
return;
|
|
12821
|
+
}
|
|
12822
|
+
if (this.mode === "localStorage" || this.mode === "localStorage+cookie") {
|
|
12823
|
+
try {
|
|
12824
|
+
localStorage.setItem(key, value);
|
|
12825
|
+
} catch (e) {
|
|
12826
|
+
}
|
|
12827
|
+
}
|
|
12828
|
+
if (this.mode === "cookie" || this.mode === "localStorage+cookie") {
|
|
12829
|
+
try {
|
|
12830
|
+
const expires = /* @__PURE__ */ new Date();
|
|
12831
|
+
expires.setFullYear(expires.getFullYear() + 1);
|
|
12832
|
+
document.cookie = `${key}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=/;SameSite=Lax`;
|
|
12833
|
+
} catch (e) {
|
|
12834
|
+
}
|
|
12835
|
+
}
|
|
12836
|
+
}
|
|
12837
|
+
remove(key) {
|
|
12838
|
+
if (this.mode === "none") return;
|
|
12839
|
+
if (this.mode === "memory") {
|
|
12840
|
+
this.memoryStorage.delete(key);
|
|
12841
|
+
return;
|
|
12842
|
+
}
|
|
12843
|
+
if (this.mode === "localStorage" || this.mode === "localStorage+cookie") {
|
|
12844
|
+
try {
|
|
12845
|
+
localStorage.removeItem(key);
|
|
12846
|
+
} catch (e) {
|
|
12847
|
+
}
|
|
12848
|
+
}
|
|
12849
|
+
if (this.mode === "cookie" || this.mode === "localStorage+cookie") {
|
|
12850
|
+
try {
|
|
12851
|
+
document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/`;
|
|
12852
|
+
} catch (e) {
|
|
12853
|
+
}
|
|
12854
|
+
}
|
|
12855
|
+
}
|
|
12856
|
+
clear() {
|
|
12857
|
+
if (this.mode === "memory") {
|
|
12858
|
+
this.memoryStorage.clear();
|
|
12859
|
+
return;
|
|
12860
|
+
}
|
|
12861
|
+
Object.values(PERSISTENCE_KEYS).forEach((key) => this.remove(key));
|
|
12862
|
+
}
|
|
12863
|
+
};
|
|
12864
|
+
var NetworkMonitor = class {
|
|
12865
|
+
constructor(options, onCapture) {
|
|
12866
|
+
this.logs = [];
|
|
12867
|
+
this.originalFetch = null;
|
|
12868
|
+
this.originalXHROpen = null;
|
|
12869
|
+
this.originalXHRSend = null;
|
|
12870
|
+
this.isMonitoring = false;
|
|
12871
|
+
var _a2, _b, _c, _d;
|
|
12872
|
+
this.options = {
|
|
12873
|
+
skipUrls: options.skipUrls || ["api.bentolabs.ai"],
|
|
12874
|
+
captureHeaders: (_a2 = options.captureHeaders) != null ? _a2 : false,
|
|
12875
|
+
captureBody: (_b = options.captureBody) != null ? _b : false,
|
|
12876
|
+
maxBodySize: (_c = options.maxBodySize) != null ? _c : 5e3,
|
|
12877
|
+
sampleRate: (_d = options.sampleRate) != null ? _d : 1
|
|
12878
|
+
};
|
|
12879
|
+
this.onCapture = onCapture;
|
|
12880
|
+
}
|
|
12881
|
+
start() {
|
|
12882
|
+
if (this.isMonitoring || typeof window === "undefined") return;
|
|
12883
|
+
this.isMonitoring = true;
|
|
12884
|
+
this.interceptFetch();
|
|
12885
|
+
this.interceptXHR();
|
|
12886
|
+
}
|
|
12887
|
+
stop() {
|
|
12888
|
+
if (!this.isMonitoring) return;
|
|
12889
|
+
this.isMonitoring = false;
|
|
12890
|
+
this.restoreFetch();
|
|
12891
|
+
this.restoreXHR();
|
|
12892
|
+
}
|
|
12893
|
+
getLogs() {
|
|
12894
|
+
return [...this.logs];
|
|
12895
|
+
}
|
|
12896
|
+
clearLogs() {
|
|
12897
|
+
this.logs = [];
|
|
12898
|
+
}
|
|
12899
|
+
shouldSkip(url) {
|
|
12900
|
+
return this.options.skipUrls.some((skipUrl) => url.includes(skipUrl));
|
|
12901
|
+
}
|
|
12902
|
+
shouldSample() {
|
|
12903
|
+
return Math.random() < this.options.sampleRate;
|
|
12904
|
+
}
|
|
12905
|
+
sanitizeUrl(url) {
|
|
12906
|
+
try {
|
|
12907
|
+
const urlObj = new URL(url, window.location.origin);
|
|
12908
|
+
const sensitiveParams = ["token", "key", "secret", "password", "auth", "apikey", "api_key"];
|
|
12909
|
+
sensitiveParams.forEach((param) => {
|
|
12910
|
+
if (urlObj.searchParams.has(param)) {
|
|
12911
|
+
urlObj.searchParams.set(param, "[REDACTED]");
|
|
12912
|
+
}
|
|
12913
|
+
});
|
|
12914
|
+
return urlObj.toString();
|
|
12915
|
+
} catch (e) {
|
|
12916
|
+
return url;
|
|
12917
|
+
}
|
|
12918
|
+
}
|
|
12919
|
+
interceptFetch() {
|
|
12920
|
+
if (typeof fetch === "undefined") return;
|
|
12921
|
+
this.originalFetch = fetch.bind(window);
|
|
12922
|
+
const self = this;
|
|
12923
|
+
window.fetch = async function(input2, init) {
|
|
12924
|
+
const url = typeof input2 === "string" ? input2 : input2 instanceof URL ? input2.toString() : input2.url;
|
|
12925
|
+
if (self.shouldSkip(url) || !self.shouldSample()) {
|
|
12926
|
+
return self.originalFetch(input2, init);
|
|
12927
|
+
}
|
|
12928
|
+
const startTime = Date.now();
|
|
12929
|
+
const method = (init == null ? void 0 : init.method) || "GET";
|
|
12930
|
+
try {
|
|
12931
|
+
const response = await self.originalFetch(input2, init);
|
|
12932
|
+
const duration = Date.now() - startTime;
|
|
12933
|
+
const log = {
|
|
12934
|
+
url: self.sanitizeUrl(url),
|
|
12935
|
+
method: method.toUpperCase(),
|
|
12936
|
+
status: response.status,
|
|
12937
|
+
duration,
|
|
12938
|
+
responseSize: parseInt(response.headers.get("content-length") || "0", 10),
|
|
12939
|
+
initiator: "fetch",
|
|
12940
|
+
timestamp: Date.now()
|
|
12941
|
+
};
|
|
12942
|
+
self.logs.push(log);
|
|
12943
|
+
if (self.logs.length > 500) self.logs.shift();
|
|
12944
|
+
self.onCapture(log);
|
|
12945
|
+
return response;
|
|
12946
|
+
} catch (error) {
|
|
12947
|
+
const duration = Date.now() - startTime;
|
|
12948
|
+
const log = {
|
|
12949
|
+
url: self.sanitizeUrl(url),
|
|
12950
|
+
method: method.toUpperCase(),
|
|
12951
|
+
status: 0,
|
|
12952
|
+
duration,
|
|
12953
|
+
responseSize: 0,
|
|
12954
|
+
initiator: "fetch",
|
|
12955
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
12956
|
+
timestamp: Date.now()
|
|
12957
|
+
};
|
|
12958
|
+
self.logs.push(log);
|
|
12959
|
+
if (self.logs.length > 500) self.logs.shift();
|
|
12960
|
+
self.onCapture(log);
|
|
12961
|
+
throw error;
|
|
12962
|
+
}
|
|
12963
|
+
};
|
|
12964
|
+
}
|
|
12965
|
+
restoreFetch() {
|
|
12966
|
+
if (this.originalFetch && typeof window !== "undefined") {
|
|
12967
|
+
window.fetch = this.originalFetch;
|
|
12968
|
+
this.originalFetch = null;
|
|
12969
|
+
}
|
|
12970
|
+
}
|
|
12971
|
+
interceptXHR() {
|
|
12972
|
+
if (typeof XMLHttpRequest === "undefined") return;
|
|
12973
|
+
this.originalXHROpen = XMLHttpRequest.prototype.open;
|
|
12974
|
+
this.originalXHRSend = XMLHttpRequest.prototype.send;
|
|
12975
|
+
const self = this;
|
|
12976
|
+
XMLHttpRequest.prototype.open = function(method, url, async, username, password) {
|
|
12977
|
+
this._bentoUrl = url.toString();
|
|
12978
|
+
this._bentoMethod = method;
|
|
12979
|
+
this._bentoStartTime = 0;
|
|
12980
|
+
return self.originalXHROpen.call(this, method, url, async != null ? async : true, username, password);
|
|
12981
|
+
};
|
|
12982
|
+
XMLHttpRequest.prototype.send = function(body) {
|
|
12983
|
+
const xhr = this;
|
|
12984
|
+
const url = xhr._bentoUrl;
|
|
12985
|
+
if (self.shouldSkip(url) || !self.shouldSample()) {
|
|
12986
|
+
return self.originalXHRSend.call(this, body);
|
|
12987
|
+
}
|
|
12988
|
+
xhr._bentoStartTime = Date.now();
|
|
12989
|
+
const handleLoadEnd = () => {
|
|
12990
|
+
const duration = Date.now() - xhr._bentoStartTime;
|
|
12991
|
+
const log = {
|
|
12992
|
+
url: self.sanitizeUrl(url),
|
|
12993
|
+
method: xhr._bentoMethod.toUpperCase(),
|
|
12994
|
+
status: xhr.status,
|
|
12995
|
+
duration,
|
|
12996
|
+
responseSize: parseInt(xhr.getResponseHeader("content-length") || "0", 10),
|
|
12997
|
+
initiator: "xhr",
|
|
12998
|
+
timestamp: Date.now()
|
|
12999
|
+
};
|
|
13000
|
+
if (xhr.status === 0) {
|
|
13001
|
+
log.error = "Request failed or was aborted";
|
|
13002
|
+
}
|
|
13003
|
+
self.logs.push(log);
|
|
13004
|
+
if (self.logs.length > 500) self.logs.shift();
|
|
13005
|
+
self.onCapture(log);
|
|
13006
|
+
};
|
|
13007
|
+
xhr.addEventListener("loadend", handleLoadEnd);
|
|
13008
|
+
return self.originalXHRSend.call(this, body);
|
|
13009
|
+
};
|
|
13010
|
+
}
|
|
13011
|
+
restoreXHR() {
|
|
13012
|
+
if (this.originalXHROpen) {
|
|
13013
|
+
XMLHttpRequest.prototype.open = this.originalXHROpen;
|
|
13014
|
+
this.originalXHROpen = null;
|
|
13015
|
+
}
|
|
13016
|
+
if (this.originalXHRSend) {
|
|
13017
|
+
XMLHttpRequest.prototype.send = this.originalXHRSend;
|
|
13018
|
+
this.originalXHRSend = null;
|
|
13019
|
+
}
|
|
13020
|
+
}
|
|
13021
|
+
};
|
|
13022
|
+
var StorageMonitor = class {
|
|
13023
|
+
constructor(options, onCapture) {
|
|
13024
|
+
this.logs = [];
|
|
13025
|
+
this.isMonitoring = false;
|
|
13026
|
+
this.originalLocalStorage = null;
|
|
13027
|
+
this.originalSessionStorage = null;
|
|
13028
|
+
this.originalCookieDescriptor = null;
|
|
13029
|
+
var _a2, _b, _c;
|
|
13030
|
+
this.options = {
|
|
13031
|
+
localStorage: (_a2 = options.localStorage) != null ? _a2 : true,
|
|
13032
|
+
sessionStorage: (_b = options.sessionStorage) != null ? _b : true,
|
|
13033
|
+
cookies: (_c = options.cookies) != null ? _c : true,
|
|
13034
|
+
sensitiveKeys: options.sensitiveKeys || ["password", "token", "secret", "api_key", "apikey", "auth", "credential"]
|
|
13035
|
+
};
|
|
13036
|
+
this.onCapture = onCapture;
|
|
13037
|
+
}
|
|
13038
|
+
start() {
|
|
13039
|
+
if (this.isMonitoring || typeof window === "undefined") return;
|
|
13040
|
+
this.isMonitoring = true;
|
|
13041
|
+
if (this.options.localStorage) this.interceptLocalStorage();
|
|
13042
|
+
if (this.options.sessionStorage) this.interceptSessionStorage();
|
|
13043
|
+
if (this.options.cookies) this.interceptCookies();
|
|
13044
|
+
}
|
|
13045
|
+
stop() {
|
|
13046
|
+
if (!this.isMonitoring) return;
|
|
13047
|
+
this.isMonitoring = false;
|
|
13048
|
+
this.restoreLocalStorage();
|
|
13049
|
+
this.restoreSessionStorage();
|
|
13050
|
+
this.restoreCookies();
|
|
13051
|
+
}
|
|
13052
|
+
getLogs() {
|
|
13053
|
+
return [...this.logs];
|
|
13054
|
+
}
|
|
13055
|
+
clearLogs() {
|
|
13056
|
+
this.logs = [];
|
|
13057
|
+
}
|
|
13058
|
+
isSensitiveKey(key) {
|
|
13059
|
+
const lowerKey = key.toLowerCase();
|
|
13060
|
+
return this.options.sensitiveKeys.some((sensitive) => lowerKey.includes(sensitive.toLowerCase()));
|
|
13061
|
+
}
|
|
13062
|
+
addLog(log) {
|
|
13063
|
+
if (this.isSensitiveKey(log.key) || log.key.startsWith("bentolabs_")) return;
|
|
13064
|
+
this.logs.push(log);
|
|
13065
|
+
if (this.logs.length > 500) this.logs.shift();
|
|
13066
|
+
this.onCapture(log);
|
|
13067
|
+
}
|
|
13068
|
+
interceptLocalStorage() {
|
|
13069
|
+
if (typeof localStorage === "undefined") return;
|
|
13070
|
+
const self = this;
|
|
13071
|
+
this.originalLocalStorage = {
|
|
13072
|
+
setItem: localStorage.setItem.bind(localStorage),
|
|
13073
|
+
getItem: localStorage.getItem.bind(localStorage),
|
|
13074
|
+
removeItem: localStorage.removeItem.bind(localStorage),
|
|
13075
|
+
clear: localStorage.clear.bind(localStorage)
|
|
13076
|
+
};
|
|
13077
|
+
localStorage.setItem = function(key, value) {
|
|
13078
|
+
self.addLog({
|
|
13079
|
+
storageType: "localStorage",
|
|
13080
|
+
operation: "set",
|
|
13081
|
+
key,
|
|
13082
|
+
valueType: typeof value,
|
|
13083
|
+
timestamp: Date.now()
|
|
13084
|
+
});
|
|
13085
|
+
return self.originalLocalStorage.setItem(key, value);
|
|
13086
|
+
};
|
|
13087
|
+
localStorage.getItem = function(key) {
|
|
13088
|
+
self.addLog({
|
|
13089
|
+
storageType: "localStorage",
|
|
13090
|
+
operation: "get",
|
|
13091
|
+
key,
|
|
13092
|
+
timestamp: Date.now()
|
|
13093
|
+
});
|
|
13094
|
+
return self.originalLocalStorage.getItem(key);
|
|
13095
|
+
};
|
|
13096
|
+
localStorage.removeItem = function(key) {
|
|
13097
|
+
self.addLog({
|
|
13098
|
+
storageType: "localStorage",
|
|
13099
|
+
operation: "remove",
|
|
13100
|
+
key,
|
|
13101
|
+
timestamp: Date.now()
|
|
13102
|
+
});
|
|
13103
|
+
return self.originalLocalStorage.removeItem(key);
|
|
13104
|
+
};
|
|
13105
|
+
localStorage.clear = function() {
|
|
13106
|
+
self.addLog({
|
|
13107
|
+
storageType: "localStorage",
|
|
13108
|
+
operation: "clear",
|
|
13109
|
+
key: "*",
|
|
13110
|
+
timestamp: Date.now()
|
|
13111
|
+
});
|
|
13112
|
+
return self.originalLocalStorage.clear();
|
|
13113
|
+
};
|
|
13114
|
+
}
|
|
13115
|
+
restoreLocalStorage() {
|
|
13116
|
+
if (this.originalLocalStorage && typeof localStorage !== "undefined") {
|
|
13117
|
+
localStorage.setItem = this.originalLocalStorage.setItem;
|
|
13118
|
+
localStorage.getItem = this.originalLocalStorage.getItem;
|
|
13119
|
+
localStorage.removeItem = this.originalLocalStorage.removeItem;
|
|
13120
|
+
localStorage.clear = this.originalLocalStorage.clear;
|
|
13121
|
+
this.originalLocalStorage = null;
|
|
13122
|
+
}
|
|
13123
|
+
}
|
|
13124
|
+
interceptSessionStorage() {
|
|
13125
|
+
if (typeof sessionStorage === "undefined") return;
|
|
13126
|
+
const self = this;
|
|
13127
|
+
this.originalSessionStorage = {
|
|
13128
|
+
setItem: sessionStorage.setItem.bind(sessionStorage),
|
|
13129
|
+
getItem: sessionStorage.getItem.bind(sessionStorage),
|
|
13130
|
+
removeItem: sessionStorage.removeItem.bind(sessionStorage),
|
|
13131
|
+
clear: sessionStorage.clear.bind(sessionStorage)
|
|
13132
|
+
};
|
|
13133
|
+
sessionStorage.setItem = function(key, value) {
|
|
13134
|
+
self.addLog({
|
|
13135
|
+
storageType: "sessionStorage",
|
|
13136
|
+
operation: "set",
|
|
13137
|
+
key,
|
|
13138
|
+
valueType: typeof value,
|
|
13139
|
+
timestamp: Date.now()
|
|
13140
|
+
});
|
|
13141
|
+
return self.originalSessionStorage.setItem(key, value);
|
|
13142
|
+
};
|
|
13143
|
+
sessionStorage.getItem = function(key) {
|
|
13144
|
+
self.addLog({
|
|
13145
|
+
storageType: "sessionStorage",
|
|
13146
|
+
operation: "get",
|
|
13147
|
+
key,
|
|
13148
|
+
timestamp: Date.now()
|
|
13149
|
+
});
|
|
13150
|
+
return self.originalSessionStorage.getItem(key);
|
|
13151
|
+
};
|
|
13152
|
+
sessionStorage.removeItem = function(key) {
|
|
13153
|
+
self.addLog({
|
|
13154
|
+
storageType: "sessionStorage",
|
|
13155
|
+
operation: "remove",
|
|
13156
|
+
key,
|
|
13157
|
+
timestamp: Date.now()
|
|
13158
|
+
});
|
|
13159
|
+
return self.originalSessionStorage.removeItem(key);
|
|
13160
|
+
};
|
|
13161
|
+
sessionStorage.clear = function() {
|
|
13162
|
+
self.addLog({
|
|
13163
|
+
storageType: "sessionStorage",
|
|
13164
|
+
operation: "clear",
|
|
13165
|
+
key: "*",
|
|
13166
|
+
timestamp: Date.now()
|
|
13167
|
+
});
|
|
13168
|
+
return self.originalSessionStorage.clear();
|
|
13169
|
+
};
|
|
13170
|
+
}
|
|
13171
|
+
restoreSessionStorage() {
|
|
13172
|
+
if (this.originalSessionStorage && typeof sessionStorage !== "undefined") {
|
|
13173
|
+
sessionStorage.setItem = this.originalSessionStorage.setItem;
|
|
13174
|
+
sessionStorage.getItem = this.originalSessionStorage.getItem;
|
|
13175
|
+
sessionStorage.removeItem = this.originalSessionStorage.removeItem;
|
|
13176
|
+
sessionStorage.clear = this.originalSessionStorage.clear;
|
|
13177
|
+
this.originalSessionStorage = null;
|
|
13178
|
+
}
|
|
13179
|
+
}
|
|
13180
|
+
interceptCookies() {
|
|
13181
|
+
if (typeof document === "undefined") return;
|
|
13182
|
+
const self = this;
|
|
13183
|
+
const descriptor = Object.getOwnPropertyDescriptor(Document.prototype, "cookie");
|
|
13184
|
+
if (!descriptor) return;
|
|
13185
|
+
this.originalCookieDescriptor = descriptor;
|
|
13186
|
+
Object.defineProperty(document, "cookie", {
|
|
13187
|
+
get: function() {
|
|
13188
|
+
return self.originalCookieDescriptor.get.call(this);
|
|
13189
|
+
},
|
|
13190
|
+
set: function(value) {
|
|
13191
|
+
const name = value.split("=")[0].trim();
|
|
13192
|
+
self.addLog({
|
|
13193
|
+
storageType: "cookie",
|
|
13194
|
+
operation: "set",
|
|
13195
|
+
key: name,
|
|
13196
|
+
timestamp: Date.now()
|
|
13197
|
+
});
|
|
13198
|
+
return self.originalCookieDescriptor.set.call(this, value);
|
|
13199
|
+
},
|
|
13200
|
+
configurable: true
|
|
13201
|
+
});
|
|
13202
|
+
}
|
|
13203
|
+
restoreCookies() {
|
|
13204
|
+
if (this.originalCookieDescriptor && typeof document !== "undefined") {
|
|
13205
|
+
Object.defineProperty(document, "cookie", this.originalCookieDescriptor);
|
|
13206
|
+
this.originalCookieDescriptor = null;
|
|
13207
|
+
}
|
|
13208
|
+
}
|
|
13209
|
+
};
|
|
13210
|
+
var ConsoleMonitor = class {
|
|
13211
|
+
constructor(options, onCapture) {
|
|
13212
|
+
this.logs = [];
|
|
13213
|
+
this.isMonitoring = false;
|
|
13214
|
+
this.inStack = false;
|
|
13215
|
+
this.originalConsole = null;
|
|
13216
|
+
this.originalOnError = null;
|
|
13217
|
+
this.originalOnUnhandledRejection = null;
|
|
13218
|
+
var _a2, _b, _c;
|
|
13219
|
+
this.options = {
|
|
13220
|
+
levels: options.levels || ["log", "warn", "error", "info"],
|
|
13221
|
+
maxLogs: (_a2 = options.maxLogs) != null ? _a2 : 500,
|
|
13222
|
+
maxPayloadSize: (_b = options.maxPayloadSize) != null ? _b : 5e3,
|
|
13223
|
+
captureStackTrace: (_c = options.captureStackTrace) != null ? _c : true
|
|
13224
|
+
};
|
|
13225
|
+
this.onCapture = onCapture;
|
|
13226
|
+
}
|
|
13227
|
+
start() {
|
|
13228
|
+
if (this.isMonitoring || typeof console === "undefined") return;
|
|
13229
|
+
this.isMonitoring = true;
|
|
13230
|
+
this.interceptConsole();
|
|
13231
|
+
this.interceptGlobalErrors();
|
|
13232
|
+
}
|
|
13233
|
+
stop() {
|
|
13234
|
+
if (!this.isMonitoring) return;
|
|
13235
|
+
this.isMonitoring = false;
|
|
13236
|
+
this.restoreConsole();
|
|
13237
|
+
this.restoreGlobalErrors();
|
|
13238
|
+
}
|
|
13239
|
+
getLogs() {
|
|
13240
|
+
return [...this.logs];
|
|
13241
|
+
}
|
|
13242
|
+
clearLogs() {
|
|
13243
|
+
this.logs = [];
|
|
13244
|
+
}
|
|
13245
|
+
stringifyArg(arg, seen = /* @__PURE__ */ new WeakSet()) {
|
|
13246
|
+
if (arg === null) return "null";
|
|
13247
|
+
if (arg === void 0) return "undefined";
|
|
13248
|
+
const type = typeof arg;
|
|
13249
|
+
if (type === "string") return arg;
|
|
13250
|
+
if (type === "number" || type === "boolean") return String(arg);
|
|
13251
|
+
if (type === "function") return `[Function: ${arg.name || "anonymous"}]`;
|
|
13252
|
+
if (type === "symbol") return arg.toString();
|
|
13253
|
+
if (type === "object") {
|
|
13254
|
+
if (seen.has(arg)) return "[Circular]";
|
|
13255
|
+
seen.add(arg);
|
|
13256
|
+
if (arg instanceof Error) {
|
|
13257
|
+
return `${arg.name}: ${arg.message}`;
|
|
13258
|
+
}
|
|
13259
|
+
try {
|
|
13260
|
+
const str = JSON.stringify(arg, (key, value) => {
|
|
13261
|
+
if (typeof value === "object" && value !== null) {
|
|
13262
|
+
if (seen.has(value)) return "[Circular]";
|
|
13263
|
+
seen.add(value);
|
|
13264
|
+
}
|
|
13265
|
+
if (typeof value === "function") return `[Function: ${value.name || "anonymous"}]`;
|
|
13266
|
+
if (typeof value === "symbol") return value.toString();
|
|
13267
|
+
return value;
|
|
13268
|
+
});
|
|
13269
|
+
return str.length > this.options.maxPayloadSize ? str.slice(0, this.options.maxPayloadSize) + "..." : str;
|
|
13270
|
+
} catch (e) {
|
|
13271
|
+
return "[Object]";
|
|
13272
|
+
}
|
|
13273
|
+
}
|
|
13274
|
+
return String(arg);
|
|
13275
|
+
}
|
|
13276
|
+
getStackTrace() {
|
|
13277
|
+
if (!this.options.captureStackTrace) return [];
|
|
13278
|
+
try {
|
|
13279
|
+
const stack = new Error().stack || "";
|
|
13280
|
+
const lines = stack.split("\n").slice(3);
|
|
13281
|
+
return lines.slice(0, 5).map((line) => line.trim());
|
|
13282
|
+
} catch (e) {
|
|
13283
|
+
return [];
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
addLog(level, args) {
|
|
13287
|
+
if (this.inStack) return;
|
|
13288
|
+
this.inStack = true;
|
|
13289
|
+
try {
|
|
13290
|
+
const payload = args.map((arg) => this.stringifyArg(arg));
|
|
13291
|
+
const log = {
|
|
13292
|
+
level,
|
|
13293
|
+
payload,
|
|
13294
|
+
trace: this.getStackTrace(),
|
|
13295
|
+
timestamp: Date.now()
|
|
13296
|
+
};
|
|
13297
|
+
this.logs.push(log);
|
|
13298
|
+
if (this.logs.length > this.options.maxLogs) this.logs.shift();
|
|
13299
|
+
this.onCapture(log);
|
|
13300
|
+
} finally {
|
|
13301
|
+
this.inStack = false;
|
|
13302
|
+
}
|
|
13303
|
+
}
|
|
13304
|
+
interceptConsole() {
|
|
13305
|
+
const self = this;
|
|
13306
|
+
this.originalConsole = {
|
|
13307
|
+
log: console.log.bind(console),
|
|
13308
|
+
warn: console.warn.bind(console),
|
|
13309
|
+
error: console.error.bind(console),
|
|
13310
|
+
info: console.info.bind(console),
|
|
13311
|
+
debug: console.debug.bind(console)
|
|
13312
|
+
};
|
|
13313
|
+
const levels = this.options.levels;
|
|
13314
|
+
if (levels.includes("log")) {
|
|
13315
|
+
console.log = function(...args) {
|
|
13316
|
+
self.addLog("log", args);
|
|
13317
|
+
return self.originalConsole.log(...args);
|
|
13318
|
+
};
|
|
13319
|
+
}
|
|
13320
|
+
if (levels.includes("warn")) {
|
|
13321
|
+
console.warn = function(...args) {
|
|
13322
|
+
self.addLog("warn", args);
|
|
13323
|
+
return self.originalConsole.warn(...args);
|
|
13324
|
+
};
|
|
13325
|
+
}
|
|
13326
|
+
if (levels.includes("error")) {
|
|
13327
|
+
console.error = function(...args) {
|
|
13328
|
+
self.addLog("error", args);
|
|
13329
|
+
return self.originalConsole.error(...args);
|
|
13330
|
+
};
|
|
13331
|
+
}
|
|
13332
|
+
if (levels.includes("info")) {
|
|
13333
|
+
console.info = function(...args) {
|
|
13334
|
+
self.addLog("info", args);
|
|
13335
|
+
return self.originalConsole.info(...args);
|
|
13336
|
+
};
|
|
13337
|
+
}
|
|
13338
|
+
if (levels.includes("debug")) {
|
|
13339
|
+
console.debug = function(...args) {
|
|
13340
|
+
self.addLog("debug", args);
|
|
13341
|
+
return self.originalConsole.debug(...args);
|
|
13342
|
+
};
|
|
13343
|
+
}
|
|
13344
|
+
}
|
|
13345
|
+
restoreConsole() {
|
|
13346
|
+
if (this.originalConsole) {
|
|
13347
|
+
console.log = this.originalConsole.log;
|
|
13348
|
+
console.warn = this.originalConsole.warn;
|
|
13349
|
+
console.error = this.originalConsole.error;
|
|
13350
|
+
console.info = this.originalConsole.info;
|
|
13351
|
+
console.debug = this.originalConsole.debug;
|
|
13352
|
+
this.originalConsole = null;
|
|
13353
|
+
}
|
|
13354
|
+
}
|
|
13355
|
+
interceptGlobalErrors() {
|
|
13356
|
+
if (typeof window === "undefined") return;
|
|
13357
|
+
const self = this;
|
|
13358
|
+
this.originalOnError = window.onerror;
|
|
13359
|
+
window.onerror = function(message, source, lineno, colno, error) {
|
|
13360
|
+
self.addLog("error", [
|
|
13361
|
+
`Uncaught error: ${message}`,
|
|
13362
|
+
`at ${source}:${lineno}:${colno}`,
|
|
13363
|
+
(error == null ? void 0 : error.stack) || ""
|
|
13364
|
+
]);
|
|
13365
|
+
if (self.originalOnError && typeof self.originalOnError === "function") {
|
|
13366
|
+
return self.originalOnError(message, source, lineno, colno, error);
|
|
13367
|
+
}
|
|
13368
|
+
return false;
|
|
13369
|
+
};
|
|
13370
|
+
this.originalOnUnhandledRejection = window.onunhandledrejection;
|
|
13371
|
+
window.onunhandledrejection = function(event) {
|
|
13372
|
+
self.addLog("error", [`Unhandled promise rejection: ${self.stringifyArg(event.reason)}`]);
|
|
13373
|
+
if (self.originalOnUnhandledRejection) {
|
|
13374
|
+
self.originalOnUnhandledRejection(event);
|
|
13375
|
+
}
|
|
13376
|
+
};
|
|
13377
|
+
}
|
|
13378
|
+
restoreGlobalErrors() {
|
|
13379
|
+
if (typeof window === "undefined") return;
|
|
13380
|
+
if (this.originalOnError !== null) {
|
|
13381
|
+
window.onerror = this.originalOnError;
|
|
13382
|
+
this.originalOnError = null;
|
|
13383
|
+
}
|
|
13384
|
+
if (this.originalOnUnhandledRejection !== null) {
|
|
13385
|
+
window.onunhandledrejection = this.originalOnUnhandledRejection;
|
|
13386
|
+
this.originalOnUnhandledRejection = null;
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13389
|
+
};
|
|
13390
|
+
var PeopleAPI = class {
|
|
13391
|
+
constructor(sdk2) {
|
|
13392
|
+
this.sdk = sdk2;
|
|
13393
|
+
}
|
|
13394
|
+
/**
|
|
13395
|
+
* Set properties on the current user
|
|
13396
|
+
*/
|
|
13397
|
+
set(properties) {
|
|
13398
|
+
this.sdk.capture("$set", { $set: properties });
|
|
13399
|
+
}
|
|
13400
|
+
/**
|
|
13401
|
+
* Set properties on the current user only if they haven't been set already
|
|
13402
|
+
*/
|
|
13403
|
+
setOnce(properties) {
|
|
13404
|
+
this.sdk.capture("$set", { $set_once: properties });
|
|
13405
|
+
}
|
|
13406
|
+
/**
|
|
13407
|
+
* Remove properties from the current user
|
|
13408
|
+
*/
|
|
13409
|
+
unset(propertyNames) {
|
|
13410
|
+
this.sdk.capture("$set", { $unset: propertyNames });
|
|
13411
|
+
}
|
|
13412
|
+
};
|
|
12623
13413
|
var BentoLabsSDK = class {
|
|
12624
13414
|
constructor() {
|
|
12625
13415
|
this.config = {
|
|
@@ -12628,43 +13418,115 @@ var BentoLabsSDK = class {
|
|
|
12628
13418
|
debug: false,
|
|
12629
13419
|
batchSize: 50,
|
|
12630
13420
|
batchInterval: 1e4,
|
|
12631
|
-
// 10 seconds
|
|
12632
13421
|
enableRecording: true,
|
|
12633
13422
|
maxRetries: 3,
|
|
12634
|
-
baseRetryDelay: 1e3
|
|
13423
|
+
baseRetryDelay: 1e3,
|
|
13424
|
+
enableCompression: true,
|
|
13425
|
+
// PostHog-like defaults
|
|
13426
|
+
persistence: "localStorage+cookie",
|
|
13427
|
+
respect_dnt: false,
|
|
13428
|
+
autocapture: true,
|
|
13429
|
+
capture_pageview: true,
|
|
13430
|
+
capture_pageleave: true,
|
|
13431
|
+
// Monitoring defaults
|
|
13432
|
+
capture_network: true,
|
|
13433
|
+
networkOptions: {
|
|
13434
|
+
skipUrls: ["api.bentolabs.ai"],
|
|
13435
|
+
captureHeaders: false,
|
|
13436
|
+
captureBody: false,
|
|
13437
|
+
maxBodySize: 5e3,
|
|
13438
|
+
sampleRate: 1
|
|
13439
|
+
},
|
|
13440
|
+
capture_storage: true,
|
|
13441
|
+
storageOptions: {
|
|
13442
|
+
localStorage: true,
|
|
13443
|
+
sessionStorage: true,
|
|
13444
|
+
cookies: true,
|
|
13445
|
+
sensitiveKeys: ["password", "token", "secret", "api_key", "apikey", "auth", "credential"]
|
|
13446
|
+
},
|
|
13447
|
+
capture_console: true,
|
|
13448
|
+
consoleOptions: {
|
|
13449
|
+
levels: ["log", "warn", "error", "info"],
|
|
13450
|
+
maxLogs: 500,
|
|
13451
|
+
maxPayloadSize: 5e3,
|
|
13452
|
+
captureStackTrace: true
|
|
13453
|
+
}
|
|
12635
13454
|
};
|
|
12636
13455
|
this.sessionId = "";
|
|
12637
13456
|
this.events = [];
|
|
12638
13457
|
this.isRecording = false;
|
|
12639
13458
|
this.batchTimer = null;
|
|
12640
13459
|
this.retryTimer = null;
|
|
12641
|
-
this.
|
|
13460
|
+
this.isSending = false;
|
|
13461
|
+
this.stopRecordingFn = null;
|
|
13462
|
+
this.distinctId = "";
|
|
13463
|
+
this.identity = {
|
|
13464
|
+
distinctId: null,
|
|
13465
|
+
properties: {},
|
|
13466
|
+
isIdentified: false
|
|
13467
|
+
};
|
|
13468
|
+
this.superProperties = {};
|
|
13469
|
+
this.persistence = null;
|
|
13470
|
+
this.initialized = false;
|
|
13471
|
+
this.optedOut = false;
|
|
13472
|
+
this.networkMonitor = null;
|
|
13473
|
+
this.storageMonitor = null;
|
|
13474
|
+
this.consoleMonitor = null;
|
|
13475
|
+
this.people = new PeopleAPI(this);
|
|
13476
|
+
this.autocaptureCleanup = null;
|
|
12642
13477
|
}
|
|
12643
13478
|
/**
|
|
12644
13479
|
* Initialize the BentoLabs SDK
|
|
12645
|
-
* @param apiKey - Your API key for authentication
|
|
12646
|
-
* @param options - Optional configuration options
|
|
12647
13480
|
*/
|
|
12648
13481
|
init(apiKey, options) {
|
|
12649
|
-
var _a2;
|
|
12650
|
-
if (this.isRecording || this.
|
|
13482
|
+
var _a2, _b, _c;
|
|
13483
|
+
if (this.isRecording || this.stopRecordingFn) {
|
|
12651
13484
|
this.stopRecordingInternal();
|
|
12652
13485
|
}
|
|
12653
13486
|
const defaultOptions = {
|
|
12654
13487
|
endpoint: "https://api.bentolabs.ai",
|
|
12655
13488
|
debug: false,
|
|
12656
13489
|
batchSize: 100,
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
// 30 seconds (increased from 10s)
|
|
13490
|
+
batchInterval: 5e3,
|
|
13491
|
+
// 5 seconds (PostHog-style)
|
|
12660
13492
|
enableRecording: true,
|
|
12661
13493
|
maxRetries: 3,
|
|
12662
|
-
baseRetryDelay: 1e3
|
|
12663
|
-
|
|
13494
|
+
baseRetryDelay: 1e3,
|
|
13495
|
+
enableCompression: true,
|
|
13496
|
+
// PostHog-like defaults
|
|
13497
|
+
persistence: "localStorage+cookie",
|
|
13498
|
+
respect_dnt: false,
|
|
13499
|
+
autocapture: true,
|
|
13500
|
+
capture_pageview: true,
|
|
13501
|
+
capture_pageleave: true,
|
|
13502
|
+
// Monitoring defaults
|
|
13503
|
+
capture_network: true,
|
|
13504
|
+
networkOptions: {
|
|
13505
|
+
skipUrls: ["api.bentolabs.ai"],
|
|
13506
|
+
captureHeaders: false,
|
|
13507
|
+
captureBody: false,
|
|
13508
|
+
maxBodySize: 5e3,
|
|
13509
|
+
sampleRate: 1
|
|
13510
|
+
},
|
|
13511
|
+
capture_storage: true,
|
|
13512
|
+
storageOptions: {
|
|
13513
|
+
localStorage: true,
|
|
13514
|
+
sessionStorage: true,
|
|
13515
|
+
cookies: true,
|
|
13516
|
+
sensitiveKeys: ["password", "token", "secret", "api_key", "apikey", "auth", "credential"]
|
|
13517
|
+
},
|
|
13518
|
+
capture_console: true,
|
|
13519
|
+
consoleOptions: {
|
|
13520
|
+
levels: ["log", "warn", "error", "info"],
|
|
13521
|
+
maxLogs: 500,
|
|
13522
|
+
maxPayloadSize: 5e3,
|
|
13523
|
+
captureStackTrace: true
|
|
13524
|
+
}
|
|
12664
13525
|
};
|
|
12665
13526
|
const mergedOptions = __spreadProps(__spreadValues(__spreadValues({}, defaultOptions), options), {
|
|
12666
|
-
|
|
12667
|
-
|
|
13527
|
+
networkOptions: __spreadValues(__spreadValues({}, defaultOptions.networkOptions), options == null ? void 0 : options.networkOptions),
|
|
13528
|
+
storageOptions: __spreadValues(__spreadValues({}, defaultOptions.storageOptions), options == null ? void 0 : options.storageOptions),
|
|
13529
|
+
consoleOptions: __spreadValues(__spreadValues({}, defaultOptions.consoleOptions), options == null ? void 0 : options.consoleOptions)
|
|
12668
13530
|
});
|
|
12669
13531
|
this.config = {
|
|
12670
13532
|
apiKey,
|
|
@@ -12674,57 +13536,523 @@ var BentoLabsSDK = class {
|
|
|
12674
13536
|
batchInterval: mergedOptions.batchInterval,
|
|
12675
13537
|
enableRecording: mergedOptions.enableRecording,
|
|
12676
13538
|
maxRetries: mergedOptions.maxRetries,
|
|
12677
|
-
baseRetryDelay: mergedOptions.baseRetryDelay
|
|
13539
|
+
baseRetryDelay: mergedOptions.baseRetryDelay,
|
|
13540
|
+
enableCompression: (_a2 = mergedOptions.enableCompression) != null ? _a2 : true,
|
|
13541
|
+
persistence: mergedOptions.persistence,
|
|
13542
|
+
respect_dnt: mergedOptions.respect_dnt,
|
|
13543
|
+
autocapture: mergedOptions.autocapture,
|
|
13544
|
+
capture_pageview: mergedOptions.capture_pageview,
|
|
13545
|
+
capture_pageleave: mergedOptions.capture_pageleave,
|
|
13546
|
+
capture_network: mergedOptions.capture_network,
|
|
13547
|
+
networkOptions: mergedOptions.networkOptions,
|
|
13548
|
+
capture_storage: mergedOptions.capture_storage,
|
|
13549
|
+
storageOptions: mergedOptions.storageOptions,
|
|
13550
|
+
capture_console: mergedOptions.capture_console,
|
|
13551
|
+
consoleOptions: mergedOptions.consoleOptions,
|
|
13552
|
+
bootstrap: mergedOptions.bootstrap
|
|
12678
13553
|
};
|
|
13554
|
+
if (this.config.respect_dnt && this.isDNTEnabled()) {
|
|
13555
|
+
if (this.config.debug) {
|
|
13556
|
+
console.log("[BentoLabsSDK] Do Not Track is enabled, opting out");
|
|
13557
|
+
}
|
|
13558
|
+
this.optedOut = true;
|
|
13559
|
+
}
|
|
13560
|
+
this.persistence = new Persistence(this.config.persistence);
|
|
13561
|
+
this.loadPersistedState();
|
|
13562
|
+
if ((_b = this.config.bootstrap) == null ? void 0 : _b.distinctID) {
|
|
13563
|
+
this.distinctId = this.config.bootstrap.distinctID;
|
|
13564
|
+
this.identity.distinctId = this.config.bootstrap.distinctID;
|
|
13565
|
+
this.identity.isIdentified = (_c = this.config.bootstrap.isIdentifiedID) != null ? _c : false;
|
|
13566
|
+
this.persistence.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
|
|
13567
|
+
if (this.identity.isIdentified) {
|
|
13568
|
+
this.persistence.set(PERSISTENCE_KEYS.IS_IDENTIFIED, "true");
|
|
13569
|
+
}
|
|
13570
|
+
}
|
|
13571
|
+
if (!this.distinctId) {
|
|
13572
|
+
this.distinctId = this.generateAnonymousId();
|
|
13573
|
+
this.persistence.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
|
|
13574
|
+
}
|
|
12679
13575
|
this.sessionId = this.generateSessionId();
|
|
13576
|
+
this.initialized = true;
|
|
12680
13577
|
if (this.config.debug) {
|
|
12681
13578
|
console.log("[BentoLabsSDK] Initialized with config:", {
|
|
12682
13579
|
apiKey: apiKey.substring(0, 8) + "...",
|
|
12683
13580
|
endpoint: this.config.endpoint,
|
|
12684
13581
|
debug: this.config.debug,
|
|
12685
|
-
sessionId: this.sessionId
|
|
13582
|
+
sessionId: this.sessionId,
|
|
13583
|
+
distinctId: this.distinctId
|
|
12686
13584
|
});
|
|
12687
13585
|
}
|
|
12688
13586
|
this.startRecording();
|
|
12689
13587
|
this.startBatching();
|
|
13588
|
+
if (this.config.autocapture) {
|
|
13589
|
+
this.setupAutocapture();
|
|
13590
|
+
}
|
|
13591
|
+
if (this.config.capture_pageview) {
|
|
13592
|
+
this.capture("$pageview", {
|
|
13593
|
+
$current_url: typeof window !== "undefined" ? window.location.href : "",
|
|
13594
|
+
$pathname: typeof window !== "undefined" ? window.location.pathname : "",
|
|
13595
|
+
$referrer: typeof document !== "undefined" ? document.referrer : ""
|
|
13596
|
+
});
|
|
13597
|
+
}
|
|
13598
|
+
if (this.config.capture_pageleave && typeof window !== "undefined") {
|
|
13599
|
+
window.addEventListener("beforeunload", this.handlePageLeave.bind(this));
|
|
13600
|
+
}
|
|
13601
|
+
if (this.config.capture_network) {
|
|
13602
|
+
this.startNetworkCapture();
|
|
13603
|
+
}
|
|
13604
|
+
if (this.config.capture_storage) {
|
|
13605
|
+
this.startStorageCapture();
|
|
13606
|
+
}
|
|
13607
|
+
if (this.config.capture_console) {
|
|
13608
|
+
this.startConsoleCapture();
|
|
13609
|
+
}
|
|
12690
13610
|
}
|
|
13611
|
+
// ============================================================================
|
|
13612
|
+
// Identity Methods
|
|
13613
|
+
// ============================================================================
|
|
12691
13614
|
/**
|
|
12692
|
-
*
|
|
13615
|
+
* Identify a user with a distinct ID and optional properties
|
|
12693
13616
|
*/
|
|
12694
|
-
|
|
12695
|
-
|
|
12696
|
-
|
|
13617
|
+
identify(distinctId, properties) {
|
|
13618
|
+
var _a2, _b, _c;
|
|
13619
|
+
if (!this.initialized) {
|
|
13620
|
+
console.warn("[BentoLabsSDK] Cannot identify: SDK not initialized");
|
|
13621
|
+
return;
|
|
13622
|
+
}
|
|
13623
|
+
const previousId = this.distinctId;
|
|
13624
|
+
this.distinctId = distinctId;
|
|
13625
|
+
this.identity.distinctId = distinctId;
|
|
13626
|
+
this.identity.isIdentified = true;
|
|
13627
|
+
if (properties) {
|
|
13628
|
+
this.identity.properties = __spreadValues(__spreadValues({}, this.identity.properties), properties);
|
|
13629
|
+
}
|
|
13630
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.DISTINCT_ID, distinctId);
|
|
13631
|
+
(_b = this.persistence) == null ? void 0 : _b.set(PERSISTENCE_KEYS.IS_IDENTIFIED, "true");
|
|
13632
|
+
if (properties) {
|
|
13633
|
+
(_c = this.persistence) == null ? void 0 : _c.set(PERSISTENCE_KEYS.USER_PROPERTIES, JSON.stringify(this.identity.properties));
|
|
13634
|
+
}
|
|
13635
|
+
this.capture("$identify", __spreadValues({
|
|
13636
|
+
$anon_distinct_id: previousId
|
|
13637
|
+
}, properties));
|
|
13638
|
+
if (this.config.debug) {
|
|
13639
|
+
console.log("[BentoLabsSDK] Identified user:", distinctId);
|
|
13640
|
+
}
|
|
12697
13641
|
}
|
|
12698
13642
|
/**
|
|
12699
|
-
*
|
|
13643
|
+
* Reset the user to an anonymous state
|
|
12700
13644
|
*/
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
13645
|
+
reset() {
|
|
13646
|
+
var _a2, _b;
|
|
13647
|
+
if (!this.initialized) return;
|
|
13648
|
+
this.distinctId = this.generateAnonymousId();
|
|
13649
|
+
this.identity = {
|
|
13650
|
+
distinctId: null,
|
|
13651
|
+
properties: {},
|
|
13652
|
+
isIdentified: false
|
|
13653
|
+
};
|
|
13654
|
+
this.superProperties = {};
|
|
13655
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.clear();
|
|
13656
|
+
(_b = this.persistence) == null ? void 0 : _b.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
|
|
13657
|
+
if (this.config.debug) {
|
|
13658
|
+
console.log("[BentoLabsSDK] Reset to anonymous:", this.distinctId);
|
|
13659
|
+
}
|
|
13660
|
+
}
|
|
13661
|
+
/**
|
|
13662
|
+
* Get the current distinct ID
|
|
13663
|
+
*/
|
|
13664
|
+
getDistinctId() {
|
|
13665
|
+
return this.distinctId;
|
|
13666
|
+
}
|
|
13667
|
+
// ============================================================================
|
|
13668
|
+
// Super Properties Methods
|
|
13669
|
+
// ============================================================================
|
|
13670
|
+
/**
|
|
13671
|
+
* Register super properties to be sent with every event
|
|
13672
|
+
*/
|
|
13673
|
+
register(properties) {
|
|
13674
|
+
var _a2;
|
|
13675
|
+
this.superProperties = __spreadValues(__spreadValues({}, this.superProperties), properties);
|
|
13676
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.SUPER_PROPERTIES, JSON.stringify(this.superProperties));
|
|
13677
|
+
if (this.config.debug) {
|
|
13678
|
+
console.log("[BentoLabsSDK] Registered super properties:", properties);
|
|
13679
|
+
}
|
|
13680
|
+
}
|
|
13681
|
+
/**
|
|
13682
|
+
* Register super properties only if they haven't been set
|
|
13683
|
+
*/
|
|
13684
|
+
registerOnce(properties) {
|
|
13685
|
+
const newProps = {};
|
|
13686
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
13687
|
+
if (!(key in this.superProperties)) {
|
|
13688
|
+
newProps[key] = value;
|
|
12708
13689
|
}
|
|
12709
|
-
|
|
13690
|
+
}
|
|
13691
|
+
if (Object.keys(newProps).length > 0) {
|
|
13692
|
+
this.register(newProps);
|
|
13693
|
+
}
|
|
12710
13694
|
}
|
|
12711
13695
|
/**
|
|
12712
|
-
*
|
|
13696
|
+
* Remove a super property
|
|
12713
13697
|
*/
|
|
12714
|
-
|
|
12715
|
-
|
|
13698
|
+
unregister(propertyName) {
|
|
13699
|
+
var _a2;
|
|
13700
|
+
delete this.superProperties[propertyName];
|
|
13701
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.SUPER_PROPERTIES, JSON.stringify(this.superProperties));
|
|
13702
|
+
if (this.config.debug) {
|
|
13703
|
+
console.log("[BentoLabsSDK] Unregistered super property:", propertyName);
|
|
13704
|
+
}
|
|
13705
|
+
}
|
|
13706
|
+
// ============================================================================
|
|
13707
|
+
// Event Capture
|
|
13708
|
+
// ============================================================================
|
|
13709
|
+
/**
|
|
13710
|
+
* Capture an event
|
|
13711
|
+
*/
|
|
13712
|
+
capture(eventName, properties) {
|
|
13713
|
+
if (!this.initialized) {
|
|
13714
|
+
console.warn("[BentoLabsSDK] Cannot capture event: SDK not initialized");
|
|
13715
|
+
return;
|
|
13716
|
+
}
|
|
13717
|
+
if (this.optedOut) {
|
|
12716
13718
|
if (this.config.debug) {
|
|
12717
|
-
console.log("[BentoLabsSDK]
|
|
13719
|
+
console.log("[BentoLabsSDK] Event not captured (opted out):", eventName);
|
|
12718
13720
|
}
|
|
12719
13721
|
return;
|
|
12720
13722
|
}
|
|
13723
|
+
const event = {
|
|
13724
|
+
event: eventName,
|
|
13725
|
+
properties: __spreadProps(__spreadValues(__spreadValues({}, this.superProperties), properties), {
|
|
13726
|
+
$current_url: typeof window !== "undefined" ? window.location.href : "",
|
|
13727
|
+
$pathname: typeof window !== "undefined" ? window.location.pathname : ""
|
|
13728
|
+
}),
|
|
13729
|
+
$timestamp: Date.now(),
|
|
13730
|
+
$session_id: this.sessionId,
|
|
13731
|
+
$distinct_id: this.distinctId
|
|
13732
|
+
};
|
|
13733
|
+
this.addEvent({
|
|
13734
|
+
timestamp: event.$timestamp,
|
|
13735
|
+
type: `capture:${eventName}`,
|
|
13736
|
+
data: event,
|
|
13737
|
+
sessionId: this.sessionId
|
|
13738
|
+
});
|
|
12721
13739
|
if (this.config.debug) {
|
|
12722
|
-
console.log(
|
|
12723
|
-
"[BentoLabsSDK] Starting recording for session:",
|
|
12724
|
-
this.sessionId
|
|
12725
|
-
);
|
|
13740
|
+
console.log("[BentoLabsSDK] Captured event:", eventName, properties);
|
|
12726
13741
|
}
|
|
12727
|
-
|
|
13742
|
+
}
|
|
13743
|
+
/**
|
|
13744
|
+
* Track a custom event (legacy method, delegates to capture)
|
|
13745
|
+
*/
|
|
13746
|
+
trackCustomEvent(eventName, data) {
|
|
13747
|
+
if (!this.sessionId) {
|
|
13748
|
+
console.warn("[BentoLabsSDK] Cannot track event: SDK not initialized");
|
|
13749
|
+
return;
|
|
13750
|
+
}
|
|
13751
|
+
this.capture(`custom:${eventName}`, data);
|
|
13752
|
+
}
|
|
13753
|
+
// ============================================================================
|
|
13754
|
+
// Opt-in/Opt-out
|
|
13755
|
+
// ============================================================================
|
|
13756
|
+
/**
|
|
13757
|
+
* Opt out of tracking
|
|
13758
|
+
*/
|
|
13759
|
+
opt_out_capturing() {
|
|
13760
|
+
var _a2;
|
|
13761
|
+
this.optedOut = true;
|
|
13762
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.OPTED_OUT, "true");
|
|
13763
|
+
if (this.isRecording) {
|
|
13764
|
+
this.stopRecordingInternal();
|
|
13765
|
+
}
|
|
13766
|
+
this.stopNetworkCapture();
|
|
13767
|
+
this.stopStorageCapture();
|
|
13768
|
+
this.stopConsoleCapture();
|
|
13769
|
+
if (this.config.debug) {
|
|
13770
|
+
console.log("[BentoLabsSDK] Opted out of capturing");
|
|
13771
|
+
}
|
|
13772
|
+
}
|
|
13773
|
+
/**
|
|
13774
|
+
* Opt back into tracking
|
|
13775
|
+
*/
|
|
13776
|
+
opt_in_capturing() {
|
|
13777
|
+
var _a2;
|
|
13778
|
+
this.optedOut = false;
|
|
13779
|
+
(_a2 = this.persistence) == null ? void 0 : _a2.remove(PERSISTENCE_KEYS.OPTED_OUT);
|
|
13780
|
+
if (this.initialized && this.config.enableRecording) {
|
|
13781
|
+
this.startRecording();
|
|
13782
|
+
}
|
|
13783
|
+
if (this.config.capture_network) {
|
|
13784
|
+
this.startNetworkCapture();
|
|
13785
|
+
}
|
|
13786
|
+
if (this.config.capture_storage) {
|
|
13787
|
+
this.startStorageCapture();
|
|
13788
|
+
}
|
|
13789
|
+
if (this.config.capture_console) {
|
|
13790
|
+
this.startConsoleCapture();
|
|
13791
|
+
}
|
|
13792
|
+
if (this.config.debug) {
|
|
13793
|
+
console.log("[BentoLabsSDK] Opted back into capturing");
|
|
13794
|
+
}
|
|
13795
|
+
}
|
|
13796
|
+
/**
|
|
13797
|
+
* Check if user has opted out
|
|
13798
|
+
*/
|
|
13799
|
+
has_opted_out_capturing() {
|
|
13800
|
+
return this.optedOut;
|
|
13801
|
+
}
|
|
13802
|
+
/**
|
|
13803
|
+
* Check if user has opted in (not opted out)
|
|
13804
|
+
*/
|
|
13805
|
+
has_opted_in_capturing() {
|
|
13806
|
+
return !this.optedOut;
|
|
13807
|
+
}
|
|
13808
|
+
// ============================================================================
|
|
13809
|
+
// Monitoring Control
|
|
13810
|
+
// ============================================================================
|
|
13811
|
+
/**
|
|
13812
|
+
* Start network request monitoring
|
|
13813
|
+
*/
|
|
13814
|
+
startNetworkCapture() {
|
|
13815
|
+
if (this.networkMonitor) return;
|
|
13816
|
+
this.networkMonitor = new NetworkMonitor(this.config.networkOptions, (log) => {
|
|
13817
|
+
if (!this.optedOut) {
|
|
13818
|
+
this.capture("$network_request", log);
|
|
13819
|
+
}
|
|
13820
|
+
});
|
|
13821
|
+
this.networkMonitor.start();
|
|
13822
|
+
if (this.config.debug) {
|
|
13823
|
+
console.log("[BentoLabsSDK] Network capture started");
|
|
13824
|
+
}
|
|
13825
|
+
}
|
|
13826
|
+
/**
|
|
13827
|
+
* Stop network request monitoring
|
|
13828
|
+
*/
|
|
13829
|
+
stopNetworkCapture() {
|
|
13830
|
+
if (this.networkMonitor) {
|
|
13831
|
+
this.networkMonitor.stop();
|
|
13832
|
+
this.networkMonitor = null;
|
|
13833
|
+
if (this.config.debug) {
|
|
13834
|
+
console.log("[BentoLabsSDK] Network capture stopped");
|
|
13835
|
+
}
|
|
13836
|
+
}
|
|
13837
|
+
}
|
|
13838
|
+
/**
|
|
13839
|
+
* Start storage monitoring
|
|
13840
|
+
*/
|
|
13841
|
+
startStorageCapture() {
|
|
13842
|
+
if (this.storageMonitor) return;
|
|
13843
|
+
this.storageMonitor = new StorageMonitor(this.config.storageOptions, (log) => {
|
|
13844
|
+
if (!this.optedOut) {
|
|
13845
|
+
this.capture("$storage_event", log);
|
|
13846
|
+
}
|
|
13847
|
+
});
|
|
13848
|
+
this.storageMonitor.start();
|
|
13849
|
+
if (this.config.debug) {
|
|
13850
|
+
console.log("[BentoLabsSDK] Storage capture started");
|
|
13851
|
+
}
|
|
13852
|
+
}
|
|
13853
|
+
/**
|
|
13854
|
+
* Stop storage monitoring
|
|
13855
|
+
*/
|
|
13856
|
+
stopStorageCapture() {
|
|
13857
|
+
if (this.storageMonitor) {
|
|
13858
|
+
this.storageMonitor.stop();
|
|
13859
|
+
this.storageMonitor = null;
|
|
13860
|
+
if (this.config.debug) {
|
|
13861
|
+
console.log("[BentoLabsSDK] Storage capture stopped");
|
|
13862
|
+
}
|
|
13863
|
+
}
|
|
13864
|
+
}
|
|
13865
|
+
/**
|
|
13866
|
+
* Start console monitoring
|
|
13867
|
+
*/
|
|
13868
|
+
startConsoleCapture() {
|
|
13869
|
+
if (this.consoleMonitor) return;
|
|
13870
|
+
this.consoleMonitor = new ConsoleMonitor(this.config.consoleOptions, (log) => {
|
|
13871
|
+
if (!this.optedOut) {
|
|
13872
|
+
this.capture("$console_log", log);
|
|
13873
|
+
}
|
|
13874
|
+
});
|
|
13875
|
+
this.consoleMonitor.start();
|
|
13876
|
+
if (this.config.debug) {
|
|
13877
|
+
console.log("[BentoLabsSDK] Console capture started");
|
|
13878
|
+
}
|
|
13879
|
+
}
|
|
13880
|
+
/**
|
|
13881
|
+
* Stop console monitoring
|
|
13882
|
+
*/
|
|
13883
|
+
stopConsoleCapture() {
|
|
13884
|
+
if (this.consoleMonitor) {
|
|
13885
|
+
this.consoleMonitor.stop();
|
|
13886
|
+
this.consoleMonitor = null;
|
|
13887
|
+
if (this.config.debug) {
|
|
13888
|
+
console.log("[BentoLabsSDK] Console capture stopped");
|
|
13889
|
+
}
|
|
13890
|
+
}
|
|
13891
|
+
}
|
|
13892
|
+
/**
|
|
13893
|
+
* Get network logs
|
|
13894
|
+
*/
|
|
13895
|
+
getNetworkLogs() {
|
|
13896
|
+
var _a2;
|
|
13897
|
+
return ((_a2 = this.networkMonitor) == null ? void 0 : _a2.getLogs()) || [];
|
|
13898
|
+
}
|
|
13899
|
+
/**
|
|
13900
|
+
* Get storage logs
|
|
13901
|
+
*/
|
|
13902
|
+
getStorageLogs() {
|
|
13903
|
+
var _a2;
|
|
13904
|
+
return ((_a2 = this.storageMonitor) == null ? void 0 : _a2.getLogs()) || [];
|
|
13905
|
+
}
|
|
13906
|
+
/**
|
|
13907
|
+
* Get console logs
|
|
13908
|
+
*/
|
|
13909
|
+
getConsoleLogs() {
|
|
13910
|
+
var _a2;
|
|
13911
|
+
return ((_a2 = this.consoleMonitor) == null ? void 0 : _a2.getLogs()) || [];
|
|
13912
|
+
}
|
|
13913
|
+
// ============================================================================
|
|
13914
|
+
// Existing Public Methods (Preserved)
|
|
13915
|
+
// ============================================================================
|
|
13916
|
+
/**
|
|
13917
|
+
* Stop recording and clean up resources
|
|
13918
|
+
*/
|
|
13919
|
+
stop() {
|
|
13920
|
+
if (this.config.debug) {
|
|
13921
|
+
console.log("[BentoLabsSDK] Stopping recording and batching");
|
|
13922
|
+
}
|
|
13923
|
+
this.stopRecordingInternal();
|
|
13924
|
+
this.stopNetworkCapture();
|
|
13925
|
+
this.stopStorageCapture();
|
|
13926
|
+
this.stopConsoleCapture();
|
|
13927
|
+
if (this.events.length > 0) {
|
|
13928
|
+
this.sendBatch();
|
|
13929
|
+
}
|
|
13930
|
+
if (this.config.debug) {
|
|
13931
|
+
console.log("[BentoLabsSDK] Stopped successfully");
|
|
13932
|
+
}
|
|
13933
|
+
}
|
|
13934
|
+
/**
|
|
13935
|
+
* Get current session ID
|
|
13936
|
+
*/
|
|
13937
|
+
getSessionId() {
|
|
13938
|
+
return this.sessionId;
|
|
13939
|
+
}
|
|
13940
|
+
/**
|
|
13941
|
+
* Check if recording is active
|
|
13942
|
+
*/
|
|
13943
|
+
isRecordingActive() {
|
|
13944
|
+
return this.isRecording;
|
|
13945
|
+
}
|
|
13946
|
+
/**
|
|
13947
|
+
* Get current configuration (without exposing sensitive data)
|
|
13948
|
+
*/
|
|
13949
|
+
getConfig() {
|
|
13950
|
+
return {
|
|
13951
|
+
apiKey: this.config.apiKey ? this.config.apiKey.substring(0, 8) + "..." : "",
|
|
13952
|
+
endpoint: this.config.endpoint,
|
|
13953
|
+
debug: this.config.debug,
|
|
13954
|
+
batchSize: this.config.batchSize,
|
|
13955
|
+
batchInterval: this.config.batchInterval,
|
|
13956
|
+
enableRecording: this.config.enableRecording,
|
|
13957
|
+
maxRetries: this.config.maxRetries,
|
|
13958
|
+
baseRetryDelay: this.config.baseRetryDelay,
|
|
13959
|
+
enableCompression: this.config.enableCompression,
|
|
13960
|
+
persistence: this.config.persistence,
|
|
13961
|
+
respect_dnt: this.config.respect_dnt,
|
|
13962
|
+
autocapture: this.config.autocapture,
|
|
13963
|
+
capture_pageview: this.config.capture_pageview,
|
|
13964
|
+
capture_pageleave: this.config.capture_pageleave,
|
|
13965
|
+
capture_network: this.config.capture_network,
|
|
13966
|
+
networkOptions: this.config.networkOptions,
|
|
13967
|
+
capture_storage: this.config.capture_storage,
|
|
13968
|
+
storageOptions: this.config.storageOptions,
|
|
13969
|
+
capture_console: this.config.capture_console,
|
|
13970
|
+
consoleOptions: this.config.consoleOptions
|
|
13971
|
+
};
|
|
13972
|
+
}
|
|
13973
|
+
/**
|
|
13974
|
+
* Get current event queue length
|
|
13975
|
+
*/
|
|
13976
|
+
getEventQueueLength() {
|
|
13977
|
+
return this.events.length;
|
|
13978
|
+
}
|
|
13979
|
+
/**
|
|
13980
|
+
* Manually trigger a batch send
|
|
13981
|
+
*/
|
|
13982
|
+
async flushEvents() {
|
|
13983
|
+
await this.sendBatch();
|
|
13984
|
+
}
|
|
13985
|
+
// ============================================================================
|
|
13986
|
+
// Private Methods
|
|
13987
|
+
// ============================================================================
|
|
13988
|
+
generateSessionId() {
|
|
13989
|
+
const uuid = this.generateUUID();
|
|
13990
|
+
return `sess_${uuid}`;
|
|
13991
|
+
}
|
|
13992
|
+
generateAnonymousId() {
|
|
13993
|
+
const uuid = this.generateUUID();
|
|
13994
|
+
return `anon_${uuid}`;
|
|
13995
|
+
}
|
|
13996
|
+
generateUUID() {
|
|
13997
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
13998
|
+
const r = Math.random() * 16 | 0;
|
|
13999
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
14000
|
+
return v.toString(16);
|
|
14001
|
+
});
|
|
14002
|
+
}
|
|
14003
|
+
isDNTEnabled() {
|
|
14004
|
+
if (typeof navigator === "undefined") return false;
|
|
14005
|
+
return navigator.doNotTrack === "1" || navigator.doNotTrack === "yes";
|
|
14006
|
+
}
|
|
14007
|
+
loadPersistedState() {
|
|
14008
|
+
if (!this.persistence) return;
|
|
14009
|
+
const storedDistinctId = this.persistence.get(PERSISTENCE_KEYS.DISTINCT_ID);
|
|
14010
|
+
if (storedDistinctId) {
|
|
14011
|
+
this.distinctId = storedDistinctId;
|
|
14012
|
+
this.identity.distinctId = storedDistinctId;
|
|
14013
|
+
}
|
|
14014
|
+
const isIdentified = this.persistence.get(PERSISTENCE_KEYS.IS_IDENTIFIED);
|
|
14015
|
+
if (isIdentified === "true") {
|
|
14016
|
+
this.identity.isIdentified = true;
|
|
14017
|
+
}
|
|
14018
|
+
const storedSuperProps = this.persistence.get(PERSISTENCE_KEYS.SUPER_PROPERTIES);
|
|
14019
|
+
if (storedSuperProps) {
|
|
14020
|
+
try {
|
|
14021
|
+
this.superProperties = JSON.parse(storedSuperProps);
|
|
14022
|
+
} catch (e) {
|
|
14023
|
+
this.superProperties = {};
|
|
14024
|
+
}
|
|
14025
|
+
}
|
|
14026
|
+
const storedUserProps = this.persistence.get(PERSISTENCE_KEYS.USER_PROPERTIES);
|
|
14027
|
+
if (storedUserProps) {
|
|
14028
|
+
try {
|
|
14029
|
+
this.identity.properties = JSON.parse(storedUserProps);
|
|
14030
|
+
} catch (e) {
|
|
14031
|
+
this.identity.properties = {};
|
|
14032
|
+
}
|
|
14033
|
+
}
|
|
14034
|
+
const optedOut = this.persistence.get(PERSISTENCE_KEYS.OPTED_OUT);
|
|
14035
|
+
if (optedOut === "true") {
|
|
14036
|
+
this.optedOut = true;
|
|
14037
|
+
}
|
|
14038
|
+
}
|
|
14039
|
+
startRecording() {
|
|
14040
|
+
if (!this.config.enableRecording) {
|
|
14041
|
+
if (this.config.debug) {
|
|
14042
|
+
console.log("[BentoLabsSDK] Recording disabled in configuration");
|
|
14043
|
+
}
|
|
14044
|
+
return;
|
|
14045
|
+
}
|
|
14046
|
+
if (this.optedOut) {
|
|
14047
|
+
if (this.config.debug) {
|
|
14048
|
+
console.log("[BentoLabsSDK] Recording disabled (opted out)");
|
|
14049
|
+
}
|
|
14050
|
+
return;
|
|
14051
|
+
}
|
|
14052
|
+
if (this.config.debug) {
|
|
14053
|
+
console.log("[BentoLabsSDK] Starting recording for session:", this.sessionId);
|
|
14054
|
+
}
|
|
14055
|
+
try {
|
|
12728
14056
|
if (this.config.debug) {
|
|
12729
14057
|
console.log("[BentoLabsSDK] Calling rrweb record()...");
|
|
12730
14058
|
}
|
|
@@ -12743,7 +14071,6 @@ var BentoLabsSDK = class {
|
|
|
12743
14071
|
recordCanvas: true,
|
|
12744
14072
|
collectFonts: true,
|
|
12745
14073
|
plugins: [],
|
|
12746
|
-
// Enable element context capture for rich metadata on interactions
|
|
12747
14074
|
captureElementContext: true,
|
|
12748
14075
|
elementContextOptions: {
|
|
12749
14076
|
maxTextLength: 500,
|
|
@@ -12751,33 +14078,28 @@ var BentoLabsSDK = class {
|
|
|
12751
14078
|
outerHTMLDepth: 2,
|
|
12752
14079
|
includeInputValues: true
|
|
12753
14080
|
},
|
|
12754
|
-
// Mask password inputs for security
|
|
12755
14081
|
maskInputOptions: {
|
|
12756
14082
|
password: true
|
|
12757
14083
|
},
|
|
12758
|
-
// Sampling and throttling to reduce event volume
|
|
12759
14084
|
sampling: {
|
|
12760
14085
|
mousemove: true,
|
|
12761
|
-
// Sample mouse movements
|
|
12762
14086
|
mouseInteraction: { click: false, dblclick: false },
|
|
12763
|
-
// Capture all clicks
|
|
12764
14087
|
scroll: 150,
|
|
12765
|
-
// Throttle scroll events to 150ms
|
|
12766
14088
|
input: "last"
|
|
12767
|
-
// Only record final input value
|
|
12768
14089
|
},
|
|
12769
14090
|
checkoutEveryNms: 5 * 60 * 1e3
|
|
12770
|
-
// Take full snapshot every 5 minutes
|
|
12771
14091
|
});
|
|
12772
14092
|
if (typeof stopFn === "function") {
|
|
12773
|
-
this.
|
|
14093
|
+
this.stopRecordingFn = stopFn;
|
|
12774
14094
|
this.isRecording = true;
|
|
12775
14095
|
if (this.config.debug) {
|
|
12776
14096
|
console.log("[BentoLabsSDK] Recording started successfully");
|
|
12777
14097
|
}
|
|
12778
14098
|
} else {
|
|
12779
|
-
console.error(
|
|
12780
|
-
|
|
14099
|
+
console.error(
|
|
14100
|
+
"[BentoLabsSDK] rrweb record() did not return a stop function. Recording may not have started properly."
|
|
14101
|
+
);
|
|
14102
|
+
this.stopRecordingFn = null;
|
|
12781
14103
|
this.isRecording = false;
|
|
12782
14104
|
}
|
|
12783
14105
|
} catch (error) {
|
|
@@ -12785,9 +14107,6 @@ var BentoLabsSDK = class {
|
|
|
12785
14107
|
this.isRecording = false;
|
|
12786
14108
|
}
|
|
12787
14109
|
}
|
|
12788
|
-
/**
|
|
12789
|
-
* Start batching events for transmission
|
|
12790
|
-
*/
|
|
12791
14110
|
startBatching() {
|
|
12792
14111
|
if (this.config.debug) {
|
|
12793
14112
|
console.log("[BentoLabsSDK] Starting event batching");
|
|
@@ -12799,32 +14118,29 @@ var BentoLabsSDK = class {
|
|
|
12799
14118
|
this.sendBatch();
|
|
12800
14119
|
}, this.config.batchInterval);
|
|
12801
14120
|
if (this.config.debug) {
|
|
12802
|
-
console.log(
|
|
12803
|
-
`[BentoLabsSDK] Batch timer set for ${this.config.batchInterval}ms intervals`
|
|
12804
|
-
);
|
|
14121
|
+
console.log(`[BentoLabsSDK] Batch timer set for ${this.config.batchInterval}ms intervals`);
|
|
12805
14122
|
}
|
|
12806
14123
|
}
|
|
12807
|
-
/**
|
|
12808
|
-
* Add an event to the events array
|
|
12809
|
-
*/
|
|
12810
14124
|
addEvent(event) {
|
|
14125
|
+
if (this.optedOut) return;
|
|
12811
14126
|
this.events.push(event);
|
|
12812
14127
|
if (this.config.debug) {
|
|
12813
14128
|
console.log("[BentoLabsSDK] Event added:", event.type);
|
|
12814
14129
|
}
|
|
12815
|
-
if (this.events.length >= this.config.batchSize) {
|
|
14130
|
+
if (this.events.length >= this.config.batchSize && !this.isSending) {
|
|
12816
14131
|
if (this.config.debug) {
|
|
12817
|
-
console.log(
|
|
12818
|
-
`[BentoLabsSDK] Batch size limit reached (${this.config.batchSize}), sending batch`
|
|
12819
|
-
);
|
|
14132
|
+
console.log(`[BentoLabsSDK] Batch size limit reached (${this.config.batchSize}), sending batch`);
|
|
12820
14133
|
}
|
|
12821
14134
|
this.sendBatch();
|
|
12822
14135
|
}
|
|
12823
14136
|
}
|
|
12824
|
-
/**
|
|
12825
|
-
* Send batched events to the API with exponential backoff retry
|
|
12826
|
-
*/
|
|
12827
14137
|
async sendBatch() {
|
|
14138
|
+
if (this.isSending) {
|
|
14139
|
+
if (this.config.debug) {
|
|
14140
|
+
console.log("[BentoLabsSDK] Already sending, skipping batch");
|
|
14141
|
+
}
|
|
14142
|
+
return;
|
|
14143
|
+
}
|
|
12828
14144
|
const now = Date.now();
|
|
12829
14145
|
const readyEvents = this.events.filter((event) => {
|
|
12830
14146
|
if ("nextRetryTime" in event) {
|
|
@@ -12835,28 +14151,67 @@ var BentoLabsSDK = class {
|
|
|
12835
14151
|
if (readyEvents.length === 0) {
|
|
12836
14152
|
return;
|
|
12837
14153
|
}
|
|
14154
|
+
this.isSending = true;
|
|
12838
14155
|
this.events = this.events.filter((event) => !readyEvents.includes(event));
|
|
12839
14156
|
const payload = {
|
|
12840
14157
|
sessionId: this.sessionId,
|
|
12841
14158
|
events: readyEvents,
|
|
12842
14159
|
timestamp: now,
|
|
12843
14160
|
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "Unknown",
|
|
12844
|
-
url: typeof window !== "undefined" ? window.location.href : "Unknown"
|
|
14161
|
+
url: typeof window !== "undefined" ? window.location.href : "Unknown",
|
|
14162
|
+
distinctId: this.distinctId
|
|
12845
14163
|
};
|
|
12846
14164
|
if (this.config.debug) {
|
|
12847
|
-
console.log(
|
|
12848
|
-
`[BentoLabsSDK] Sending batch with ${readyEvents.length} events`
|
|
12849
|
-
);
|
|
14165
|
+
console.log(`[BentoLabsSDK] Sending batch with ${readyEvents.length} events`);
|
|
12850
14166
|
}
|
|
12851
14167
|
try {
|
|
14168
|
+
const jsonPayload = JSON.stringify(payload);
|
|
14169
|
+
let body = jsonPayload;
|
|
14170
|
+
const headers = {
|
|
14171
|
+
"Content-Type": "application/json",
|
|
14172
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
14173
|
+
"X-Session-ID": this.sessionId
|
|
14174
|
+
};
|
|
14175
|
+
if (this.config.enableCompression && typeof CompressionStream !== "undefined") {
|
|
14176
|
+
try {
|
|
14177
|
+
const encoder = new TextEncoder();
|
|
14178
|
+
const data = encoder.encode(jsonPayload);
|
|
14179
|
+
const cs = new CompressionStream("gzip");
|
|
14180
|
+
const writer = cs.writable.getWriter();
|
|
14181
|
+
writer.write(data);
|
|
14182
|
+
writer.close();
|
|
14183
|
+
const compressedChunks = [];
|
|
14184
|
+
const reader = cs.readable.getReader();
|
|
14185
|
+
let result2 = await reader.read();
|
|
14186
|
+
while (!result2.done) {
|
|
14187
|
+
compressedChunks.push(result2.value);
|
|
14188
|
+
result2 = await reader.read();
|
|
14189
|
+
}
|
|
14190
|
+
const totalLength = compressedChunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
14191
|
+
const compressedData = new Uint8Array(totalLength);
|
|
14192
|
+
let offset = 0;
|
|
14193
|
+
for (const chunk of compressedChunks) {
|
|
14194
|
+
compressedData.set(chunk, offset);
|
|
14195
|
+
offset += chunk.length;
|
|
14196
|
+
}
|
|
14197
|
+
body = new Blob([compressedData], { type: "application/json" });
|
|
14198
|
+
headers["Content-Encoding"] = "gzip";
|
|
14199
|
+
if (this.config.debug) {
|
|
14200
|
+
const ratio = ((1 - compressedData.length / data.length) * 100).toFixed(1);
|
|
14201
|
+
console.log(
|
|
14202
|
+
`[BentoLabsSDK] Compressed payload: ${data.length} -> ${compressedData.length} bytes (${ratio}% reduction)`
|
|
14203
|
+
);
|
|
14204
|
+
}
|
|
14205
|
+
} catch (compressionError) {
|
|
14206
|
+
if (this.config.debug) {
|
|
14207
|
+
console.warn("[BentoLabsSDK] Compression failed, sending uncompressed:", compressionError);
|
|
14208
|
+
}
|
|
14209
|
+
}
|
|
14210
|
+
}
|
|
12852
14211
|
const response = await fetch(`${this.config.endpoint}/events/`, {
|
|
12853
14212
|
method: "POST",
|
|
12854
|
-
headers
|
|
12855
|
-
|
|
12856
|
-
Authorization: `Bearer ${this.config.apiKey}`,
|
|
12857
|
-
"X-Session-ID": this.sessionId
|
|
12858
|
-
},
|
|
12859
|
-
body: JSON.stringify(payload)
|
|
14213
|
+
headers,
|
|
14214
|
+
body
|
|
12860
14215
|
});
|
|
12861
14216
|
if (!response.ok) {
|
|
12862
14217
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -12865,7 +14220,9 @@ var BentoLabsSDK = class {
|
|
|
12865
14220
|
console.log("[BentoLabsSDK] Batch sent successfully");
|
|
12866
14221
|
}
|
|
12867
14222
|
} catch (error) {
|
|
12868
|
-
|
|
14223
|
+
if (this.config.debug) {
|
|
14224
|
+
console.error("[BentoLabsSDK] Failed to send batch:", error);
|
|
14225
|
+
}
|
|
12869
14226
|
const retryableEvents = readyEvents.map((event) => {
|
|
12870
14227
|
const retryCount = "retryCount" in event ? event.retryCount + 1 : 1;
|
|
12871
14228
|
const delay = this.config.baseRetryDelay * Math.pow(2, retryCount - 1);
|
|
@@ -12874,43 +14231,33 @@ var BentoLabsSDK = class {
|
|
|
12874
14231
|
nextRetryTime: now + delay
|
|
12875
14232
|
});
|
|
12876
14233
|
});
|
|
12877
|
-
const eventsToRetry = retryableEvents.filter(
|
|
12878
|
-
(event) => event.retryCount <= this.config.maxRetries
|
|
12879
|
-
);
|
|
14234
|
+
const eventsToRetry = retryableEvents.filter((event) => event.retryCount <= this.config.maxRetries);
|
|
12880
14235
|
const droppedEvents = retryableEvents.length - eventsToRetry.length;
|
|
12881
14236
|
if (droppedEvents > 0 && this.config.debug) {
|
|
12882
|
-
console.log(
|
|
12883
|
-
`[BentoLabsSDK] Dropped ${droppedEvents} events after max retries`
|
|
12884
|
-
);
|
|
14237
|
+
console.log(`[BentoLabsSDK] Dropped ${droppedEvents} events after max retries`);
|
|
12885
14238
|
}
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
`[BentoLabsSDK] ${eventsToRetry.length} events re-queued for retry in ${nextRetryIn}ms`
|
|
12891
|
-
|
|
14239
|
+
if (eventsToRetry.length > 0) {
|
|
14240
|
+
this.events.unshift(...eventsToRetry);
|
|
14241
|
+
if (this.config.debug) {
|
|
14242
|
+
const nextRetryIn = Math.min(...eventsToRetry.map((e) => e.nextRetryTime)) - now;
|
|
14243
|
+
console.log(`[BentoLabsSDK] ${eventsToRetry.length} events re-queued for retry in ${nextRetryIn}ms`);
|
|
14244
|
+
}
|
|
12892
14245
|
}
|
|
12893
|
-
|
|
14246
|
+
} finally {
|
|
14247
|
+
this.isSending = false;
|
|
12894
14248
|
}
|
|
12895
14249
|
}
|
|
12896
|
-
/**
|
|
12897
|
-
* Schedule retry attempts for failed events
|
|
12898
|
-
*/
|
|
12899
14250
|
scheduleRetry() {
|
|
12900
14251
|
if (this.retryTimer) {
|
|
12901
14252
|
clearTimeout(this.retryTimer);
|
|
12902
14253
|
this.retryTimer = null;
|
|
12903
14254
|
}
|
|
12904
14255
|
const now = Date.now();
|
|
12905
|
-
const retryableEvents = this.events.filter(
|
|
12906
|
-
(event) => "nextRetryTime" in event
|
|
12907
|
-
);
|
|
14256
|
+
const retryableEvents = this.events.filter((event) => "nextRetryTime" in event);
|
|
12908
14257
|
if (retryableEvents.length === 0) {
|
|
12909
14258
|
return;
|
|
12910
14259
|
}
|
|
12911
|
-
const nextRetryTime = Math.min(
|
|
12912
|
-
...retryableEvents.map((e) => e.nextRetryTime)
|
|
12913
|
-
);
|
|
14260
|
+
const nextRetryTime = Math.min(...retryableEvents.map((e) => e.nextRetryTime));
|
|
12914
14261
|
const delay = Math.max(0, nextRetryTime - now);
|
|
12915
14262
|
if (this.config.debug) {
|
|
12916
14263
|
console.log(`[BentoLabsSDK] Scheduling retry in ${delay}ms`);
|
|
@@ -12919,13 +14266,10 @@ var BentoLabsSDK = class {
|
|
|
12919
14266
|
this.sendBatch();
|
|
12920
14267
|
}, delay);
|
|
12921
14268
|
}
|
|
12922
|
-
/**
|
|
12923
|
-
* Internal method to stop recording without logging (used during re-init)
|
|
12924
|
-
*/
|
|
12925
14269
|
stopRecordingInternal() {
|
|
12926
|
-
if (this.
|
|
12927
|
-
this.
|
|
12928
|
-
this.
|
|
14270
|
+
if (this.stopRecordingFn) {
|
|
14271
|
+
this.stopRecordingFn();
|
|
14272
|
+
this.stopRecordingFn = null;
|
|
12929
14273
|
}
|
|
12930
14274
|
if (this.batchTimer) {
|
|
12931
14275
|
clearInterval(this.batchTimer);
|
|
@@ -12935,86 +14279,115 @@ var BentoLabsSDK = class {
|
|
|
12935
14279
|
clearTimeout(this.retryTimer);
|
|
12936
14280
|
this.retryTimer = null;
|
|
12937
14281
|
}
|
|
14282
|
+
if (this.autocaptureCleanup) {
|
|
14283
|
+
this.autocaptureCleanup();
|
|
14284
|
+
this.autocaptureCleanup = null;
|
|
14285
|
+
}
|
|
12938
14286
|
this.isRecording = false;
|
|
12939
14287
|
}
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
14288
|
+
setupAutocapture() {
|
|
14289
|
+
if (typeof document === "undefined") return;
|
|
14290
|
+
const handleClick = (e) => {
|
|
14291
|
+
var _a2;
|
|
14292
|
+
const target = e.target;
|
|
14293
|
+
if (!target) return;
|
|
14294
|
+
const tagName = target.tagName.toLowerCase();
|
|
14295
|
+
const allowedTags = ["a", "button", "input", "select", "textarea"];
|
|
14296
|
+
const isAllowed = allowedTags.includes(tagName) || target.getAttribute("role") === "button" || target.hasAttribute("data-bento-capture");
|
|
14297
|
+
if (!isAllowed) return;
|
|
14298
|
+
let selector = "";
|
|
14299
|
+
try {
|
|
14300
|
+
selector = generateSelectorFromElement(target);
|
|
14301
|
+
} catch (e2) {
|
|
14302
|
+
selector = "";
|
|
14303
|
+
}
|
|
14304
|
+
this.capture("$autocapture", {
|
|
14305
|
+
$event_type: "click",
|
|
14306
|
+
$element_tag: tagName,
|
|
14307
|
+
$element_text: ((_a2 = target.textContent) == null ? void 0 : _a2.slice(0, 100)) || "",
|
|
14308
|
+
$element_classes: target.className || "",
|
|
14309
|
+
$element_id: target.id || "",
|
|
14310
|
+
$bento_selector: selector
|
|
14311
|
+
});
|
|
14312
|
+
};
|
|
14313
|
+
const handleChange = (e) => {
|
|
14314
|
+
const target = e.target;
|
|
14315
|
+
if (!target) return;
|
|
14316
|
+
const tagName = target.tagName.toLowerCase();
|
|
14317
|
+
if (!["input", "select", "textarea"].includes(tagName)) return;
|
|
14318
|
+
if (target.type === "password") return;
|
|
14319
|
+
let selector = "";
|
|
14320
|
+
try {
|
|
14321
|
+
selector = generateSelectorFromElement(target);
|
|
14322
|
+
} catch (e2) {
|
|
14323
|
+
selector = "";
|
|
14324
|
+
}
|
|
14325
|
+
this.capture("$autocapture", {
|
|
14326
|
+
$event_type: "change",
|
|
14327
|
+
$element_tag: tagName,
|
|
14328
|
+
$element_type: target.type || "",
|
|
14329
|
+
$element_name: target.name || "",
|
|
14330
|
+
$element_id: target.id || "",
|
|
14331
|
+
$bento_selector: selector
|
|
14332
|
+
// Don't capture actual value for privacy
|
|
14333
|
+
});
|
|
14334
|
+
};
|
|
14335
|
+
const handleSubmit = (e) => {
|
|
14336
|
+
const target = e.target;
|
|
14337
|
+
if (!target || target.tagName.toLowerCase() !== "form") return;
|
|
14338
|
+
let selector = "";
|
|
14339
|
+
try {
|
|
14340
|
+
selector = generateSelectorFromElement(target);
|
|
14341
|
+
} catch (e2) {
|
|
14342
|
+
selector = "";
|
|
14343
|
+
}
|
|
14344
|
+
this.capture("$autocapture", {
|
|
14345
|
+
$event_type: "submit",
|
|
14346
|
+
$element_tag: "form",
|
|
14347
|
+
$form_name: target.name || "",
|
|
14348
|
+
$form_id: target.id || "",
|
|
14349
|
+
$form_action: target.action || "",
|
|
14350
|
+
$bento_selector: selector
|
|
14351
|
+
});
|
|
14352
|
+
};
|
|
14353
|
+
document.addEventListener("click", handleClick, true);
|
|
14354
|
+
document.addEventListener("change", handleChange, true);
|
|
14355
|
+
document.addEventListener("submit", handleSubmit, true);
|
|
14356
|
+
this.autocaptureCleanup = () => {
|
|
14357
|
+
document.removeEventListener("click", handleClick, true);
|
|
14358
|
+
document.removeEventListener("change", handleChange, true);
|
|
14359
|
+
document.removeEventListener("submit", handleSubmit, true);
|
|
14360
|
+
};
|
|
12951
14361
|
if (this.config.debug) {
|
|
12952
|
-
console.log("[BentoLabsSDK]
|
|
14362
|
+
console.log("[BentoLabsSDK] Autocapture setup complete");
|
|
12953
14363
|
}
|
|
12954
14364
|
}
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
return this.sessionId;
|
|
12960
|
-
}
|
|
12961
|
-
/**
|
|
12962
|
-
* Check if recording is active
|
|
12963
|
-
*/
|
|
12964
|
-
isRecordingActive() {
|
|
12965
|
-
return this.isRecording;
|
|
12966
|
-
}
|
|
12967
|
-
/**
|
|
12968
|
-
* Get current configuration (without exposing sensitive data)
|
|
12969
|
-
*/
|
|
12970
|
-
getConfig() {
|
|
12971
|
-
return {
|
|
12972
|
-
apiKey: this.config.apiKey ? this.config.apiKey.substring(0, 8) + "..." : "",
|
|
12973
|
-
endpoint: this.config.endpoint,
|
|
12974
|
-
debug: this.config.debug,
|
|
12975
|
-
batchSize: this.config.batchSize,
|
|
12976
|
-
batchInterval: this.config.batchInterval,
|
|
12977
|
-
enableRecording: this.config.enableRecording,
|
|
12978
|
-
maxRetries: this.config.maxRetries,
|
|
12979
|
-
baseRetryDelay: this.config.baseRetryDelay
|
|
12980
|
-
};
|
|
12981
|
-
}
|
|
12982
|
-
/**
|
|
12983
|
-
* Get current event queue length
|
|
12984
|
-
*/
|
|
12985
|
-
getEventQueueLength() {
|
|
12986
|
-
return this.events.length;
|
|
12987
|
-
}
|
|
12988
|
-
/**
|
|
12989
|
-
* Manually trigger a batch send
|
|
12990
|
-
*/
|
|
12991
|
-
async flushEvents() {
|
|
12992
|
-
await this.sendBatch();
|
|
12993
|
-
}
|
|
12994
|
-
/**
|
|
12995
|
-
* Track a custom event with custom data
|
|
12996
|
-
* @param eventName - Name of the custom event
|
|
12997
|
-
* @param data - Custom event data (will be JSON stringified)
|
|
12998
|
-
*/
|
|
12999
|
-
trackCustomEvent(eventName, data) {
|
|
13000
|
-
if (!this.sessionId) {
|
|
13001
|
-
console.warn("[BentoLabsSDK] Cannot track event: SDK not initialized");
|
|
13002
|
-
return;
|
|
13003
|
-
}
|
|
13004
|
-
this.addEvent({
|
|
13005
|
-
timestamp: Date.now(),
|
|
13006
|
-
type: `custom:${eventName}`,
|
|
13007
|
-
data: data || {},
|
|
13008
|
-
sessionId: this.sessionId
|
|
14365
|
+
handlePageLeave() {
|
|
14366
|
+
this.capture("$pageleave", {
|
|
14367
|
+
$current_url: typeof window !== "undefined" ? window.location.href : "",
|
|
14368
|
+
$pathname: typeof window !== "undefined" ? window.location.pathname : ""
|
|
13009
14369
|
});
|
|
13010
|
-
if (this.
|
|
13011
|
-
|
|
14370
|
+
if (this.events.length > 0 && typeof navigator !== "undefined" && navigator.sendBeacon) {
|
|
14371
|
+
const payload = {
|
|
14372
|
+
sessionId: this.sessionId,
|
|
14373
|
+
events: this.events,
|
|
14374
|
+
timestamp: Date.now(),
|
|
14375
|
+
userAgent: navigator.userAgent,
|
|
14376
|
+
url: window.location.href,
|
|
14377
|
+
distinctId: this.distinctId
|
|
14378
|
+
};
|
|
14379
|
+
navigator.sendBeacon(
|
|
14380
|
+
`${this.config.endpoint}/events/`,
|
|
14381
|
+
new Blob([JSON.stringify(payload)], { type: "application/json" })
|
|
14382
|
+
);
|
|
14383
|
+
this.events = [];
|
|
13012
14384
|
}
|
|
13013
14385
|
}
|
|
13014
14386
|
};
|
|
13015
14387
|
var sdk = new BentoLabsSDK();
|
|
13016
14388
|
if (typeof window !== "undefined") {
|
|
13017
|
-
window.BentoLabsSDK =
|
|
14389
|
+
window.BentoLabsSDK = {
|
|
14390
|
+
// Core methods
|
|
13018
14391
|
init: sdk.init.bind(sdk),
|
|
13019
14392
|
stop: sdk.stop.bind(sdk),
|
|
13020
14393
|
getSessionId: sdk.getSessionId.bind(sdk),
|
|
@@ -13023,8 +14396,34 @@ if (typeof window !== "undefined") {
|
|
|
13023
14396
|
getEventQueueLength: sdk.getEventQueueLength.bind(sdk),
|
|
13024
14397
|
flushEvents: sdk.flushEvents.bind(sdk),
|
|
13025
14398
|
trackCustomEvent: sdk.trackCustomEvent.bind(sdk),
|
|
13026
|
-
|
|
13027
|
-
|
|
14399
|
+
// Selector generation
|
|
14400
|
+
generateSelector,
|
|
14401
|
+
generateSelectorFromElement,
|
|
14402
|
+
generateSelectorFromHTML,
|
|
14403
|
+
// PostHog-like methods
|
|
14404
|
+
capture: sdk.capture.bind(sdk),
|
|
14405
|
+
identify: sdk.identify.bind(sdk),
|
|
14406
|
+
reset: sdk.reset.bind(sdk),
|
|
14407
|
+
getDistinctId: sdk.getDistinctId.bind(sdk),
|
|
14408
|
+
register: sdk.register.bind(sdk),
|
|
14409
|
+
registerOnce: sdk.registerOnce.bind(sdk),
|
|
14410
|
+
unregister: sdk.unregister.bind(sdk),
|
|
14411
|
+
opt_out_capturing: sdk.opt_out_capturing.bind(sdk),
|
|
14412
|
+
opt_in_capturing: sdk.opt_in_capturing.bind(sdk),
|
|
14413
|
+
has_opted_out_capturing: sdk.has_opted_out_capturing.bind(sdk),
|
|
14414
|
+
has_opted_in_capturing: sdk.has_opted_in_capturing.bind(sdk),
|
|
14415
|
+
people: sdk.people,
|
|
14416
|
+
// Monitoring control
|
|
14417
|
+
startNetworkCapture: sdk.startNetworkCapture.bind(sdk),
|
|
14418
|
+
stopNetworkCapture: sdk.stopNetworkCapture.bind(sdk),
|
|
14419
|
+
startStorageCapture: sdk.startStorageCapture.bind(sdk),
|
|
14420
|
+
stopStorageCapture: sdk.stopStorageCapture.bind(sdk),
|
|
14421
|
+
startConsoleCapture: sdk.startConsoleCapture.bind(sdk),
|
|
14422
|
+
stopConsoleCapture: sdk.stopConsoleCapture.bind(sdk),
|
|
14423
|
+
getNetworkLogs: sdk.getNetworkLogs.bind(sdk),
|
|
14424
|
+
getStorageLogs: sdk.getStorageLogs.bind(sdk),
|
|
14425
|
+
getConsoleLogs: sdk.getConsoleLogs.bind(sdk)
|
|
14426
|
+
};
|
|
13028
14427
|
}
|
|
13029
14428
|
var index_default = sdk;
|
|
13030
14429
|
/*! Bundled license information:
|