@bentolabs/sdk 1.2.2 → 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/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 buildBentoSelector(element) {
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 (parts.length === 1) {
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 generateSelector(outerHTML) {
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
- context.bentoSelector = buildBentoSelector(element);
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,114 @@ 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.stopRecording = null;
13460
+ this.stopRecordingFn = null;
13461
+ this.distinctId = "";
13462
+ this.identity = {
13463
+ distinctId: null,
13464
+ properties: {},
13465
+ isIdentified: false
13466
+ };
13467
+ this.superProperties = {};
13468
+ this.persistence = null;
13469
+ this.initialized = false;
13470
+ this.optedOut = false;
13471
+ this.networkMonitor = null;
13472
+ this.storageMonitor = null;
13473
+ this.consoleMonitor = null;
13474
+ this.people = new PeopleAPI(this);
13475
+ this.autocaptureCleanup = null;
12642
13476
  }
12643
13477
  /**
12644
13478
  * Initialize the BentoLabs SDK
12645
- * @param apiKey - Your API key for authentication
12646
- * @param options - Optional configuration options
12647
13479
  */
12648
13480
  init(apiKey, options) {
12649
- var _a2;
12650
- if (this.isRecording || this.stopRecording) {
13481
+ var _a2, _b, _c;
13482
+ if (this.isRecording || this.stopRecordingFn) {
12651
13483
  this.stopRecordingInternal();
12652
13484
  }
12653
13485
  const defaultOptions = {
12654
13486
  endpoint: "https://api.bentolabs.ai",
12655
13487
  debug: false,
12656
13488
  batchSize: 100,
12657
- // Increased from 50 to reduce API calls
12658
- batchInterval: 3e4,
12659
- // 30 seconds (increased from 10s)
13489
+ batchInterval: 5e3,
13490
+ // 5 seconds (PostHog-style)
12660
13491
  enableRecording: true,
12661
13492
  maxRetries: 3,
12662
- baseRetryDelay: 1e3
12663
- // 1 second
13493
+ baseRetryDelay: 1e3,
13494
+ enableCompression: true,
13495
+ // PostHog-like defaults
13496
+ persistence: "localStorage+cookie",
13497
+ respect_dnt: false,
13498
+ autocapture: true,
13499
+ capture_pageview: true,
13500
+ capture_pageleave: true,
13501
+ // Monitoring defaults
13502
+ capture_network: true,
13503
+ networkOptions: {
13504
+ skipUrls: ["api.bentolabs.ai"],
13505
+ captureHeaders: false,
13506
+ captureBody: false,
13507
+ maxBodySize: 5e3,
13508
+ sampleRate: 1
13509
+ },
13510
+ capture_storage: true,
13511
+ storageOptions: {
13512
+ localStorage: true,
13513
+ sessionStorage: true,
13514
+ cookies: true,
13515
+ sensitiveKeys: ["password", "token", "secret", "api_key", "apikey", "auth", "credential"]
13516
+ },
13517
+ capture_console: true,
13518
+ consoleOptions: {
13519
+ levels: ["log", "warn", "error", "info"],
13520
+ maxLogs: 500,
13521
+ maxPayloadSize: 5e3,
13522
+ captureStackTrace: true
13523
+ }
12664
13524
  };
12665
13525
  const mergedOptions = __spreadProps(__spreadValues(__spreadValues({}, defaultOptions), options), {
12666
- // Ensure debug is always a boolean
12667
- debug: (_a2 = options == null ? void 0 : options.debug) != null ? _a2 : defaultOptions.debug
13526
+ networkOptions: __spreadValues(__spreadValues({}, defaultOptions.networkOptions), options == null ? void 0 : options.networkOptions),
13527
+ storageOptions: __spreadValues(__spreadValues({}, defaultOptions.storageOptions), options == null ? void 0 : options.storageOptions),
13528
+ consoleOptions: __spreadValues(__spreadValues({}, defaultOptions.consoleOptions), options == null ? void 0 : options.consoleOptions)
12668
13529
  });
12669
13530
  this.config = {
12670
13531
  apiKey,
@@ -12674,57 +13535,523 @@ var BentoLabsSDK = class {
12674
13535
  batchInterval: mergedOptions.batchInterval,
12675
13536
  enableRecording: mergedOptions.enableRecording,
12676
13537
  maxRetries: mergedOptions.maxRetries,
12677
- baseRetryDelay: mergedOptions.baseRetryDelay
13538
+ baseRetryDelay: mergedOptions.baseRetryDelay,
13539
+ enableCompression: (_a2 = mergedOptions.enableCompression) != null ? _a2 : true,
13540
+ persistence: mergedOptions.persistence,
13541
+ respect_dnt: mergedOptions.respect_dnt,
13542
+ autocapture: mergedOptions.autocapture,
13543
+ capture_pageview: mergedOptions.capture_pageview,
13544
+ capture_pageleave: mergedOptions.capture_pageleave,
13545
+ capture_network: mergedOptions.capture_network,
13546
+ networkOptions: mergedOptions.networkOptions,
13547
+ capture_storage: mergedOptions.capture_storage,
13548
+ storageOptions: mergedOptions.storageOptions,
13549
+ capture_console: mergedOptions.capture_console,
13550
+ consoleOptions: mergedOptions.consoleOptions,
13551
+ bootstrap: mergedOptions.bootstrap
12678
13552
  };
13553
+ if (this.config.respect_dnt && this.isDNTEnabled()) {
13554
+ if (this.config.debug) {
13555
+ console.log("[BentoLabsSDK] Do Not Track is enabled, opting out");
13556
+ }
13557
+ this.optedOut = true;
13558
+ }
13559
+ this.persistence = new Persistence(this.config.persistence);
13560
+ this.loadPersistedState();
13561
+ if ((_b = this.config.bootstrap) == null ? void 0 : _b.distinctID) {
13562
+ this.distinctId = this.config.bootstrap.distinctID;
13563
+ this.identity.distinctId = this.config.bootstrap.distinctID;
13564
+ this.identity.isIdentified = (_c = this.config.bootstrap.isIdentifiedID) != null ? _c : false;
13565
+ this.persistence.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
13566
+ if (this.identity.isIdentified) {
13567
+ this.persistence.set(PERSISTENCE_KEYS.IS_IDENTIFIED, "true");
13568
+ }
13569
+ }
13570
+ if (!this.distinctId) {
13571
+ this.distinctId = this.generateAnonymousId();
13572
+ this.persistence.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
13573
+ }
12679
13574
  this.sessionId = this.generateSessionId();
13575
+ this.initialized = true;
12680
13576
  if (this.config.debug) {
12681
13577
  console.log("[BentoLabsSDK] Initialized with config:", {
12682
13578
  apiKey: apiKey.substring(0, 8) + "...",
12683
13579
  endpoint: this.config.endpoint,
12684
13580
  debug: this.config.debug,
12685
- sessionId: this.sessionId
13581
+ sessionId: this.sessionId,
13582
+ distinctId: this.distinctId
12686
13583
  });
12687
13584
  }
12688
13585
  this.startRecording();
12689
13586
  this.startBatching();
13587
+ if (this.config.autocapture) {
13588
+ this.setupAutocapture();
13589
+ }
13590
+ if (this.config.capture_pageview) {
13591
+ this.capture("$pageview", {
13592
+ $current_url: typeof window !== "undefined" ? window.location.href : "",
13593
+ $pathname: typeof window !== "undefined" ? window.location.pathname : "",
13594
+ $referrer: typeof document !== "undefined" ? document.referrer : ""
13595
+ });
13596
+ }
13597
+ if (this.config.capture_pageleave && typeof window !== "undefined") {
13598
+ window.addEventListener("beforeunload", this.handlePageLeave.bind(this));
13599
+ }
13600
+ if (this.config.capture_network) {
13601
+ this.startNetworkCapture();
13602
+ }
13603
+ if (this.config.capture_storage) {
13604
+ this.startStorageCapture();
13605
+ }
13606
+ if (this.config.capture_console) {
13607
+ this.startConsoleCapture();
13608
+ }
12690
13609
  }
13610
+ // ============================================================================
13611
+ // Identity Methods
13612
+ // ============================================================================
12691
13613
  /**
12692
- * Generate a unique session ID with 'sess_' prefix
13614
+ * Identify a user with a distinct ID and optional properties
12693
13615
  */
12694
- generateSessionId() {
12695
- const uuid = this.generateUUID();
12696
- return `sess_${uuid}`;
13616
+ identify(distinctId, properties) {
13617
+ var _a2, _b, _c;
13618
+ if (!this.initialized) {
13619
+ console.warn("[BentoLabsSDK] Cannot identify: SDK not initialized");
13620
+ return;
13621
+ }
13622
+ const previousId = this.distinctId;
13623
+ this.distinctId = distinctId;
13624
+ this.identity.distinctId = distinctId;
13625
+ this.identity.isIdentified = true;
13626
+ if (properties) {
13627
+ this.identity.properties = __spreadValues(__spreadValues({}, this.identity.properties), properties);
13628
+ }
13629
+ (_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.DISTINCT_ID, distinctId);
13630
+ (_b = this.persistence) == null ? void 0 : _b.set(PERSISTENCE_KEYS.IS_IDENTIFIED, "true");
13631
+ if (properties) {
13632
+ (_c = this.persistence) == null ? void 0 : _c.set(PERSISTENCE_KEYS.USER_PROPERTIES, JSON.stringify(this.identity.properties));
13633
+ }
13634
+ this.capture("$identify", __spreadValues({
13635
+ $anon_distinct_id: previousId
13636
+ }, properties));
13637
+ if (this.config.debug) {
13638
+ console.log("[BentoLabsSDK] Identified user:", distinctId);
13639
+ }
12697
13640
  }
12698
13641
  /**
12699
- * Generate a simple UUID v4
13642
+ * Reset the user to an anonymous state
12700
13643
  */
12701
- generateUUID() {
12702
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
12703
- /[xy]/g,
12704
- function(c) {
12705
- const r = Math.random() * 16 | 0;
12706
- const v = c === "x" ? r : r & 3 | 8;
12707
- return v.toString(16);
13644
+ reset() {
13645
+ var _a2, _b;
13646
+ if (!this.initialized) return;
13647
+ this.distinctId = this.generateAnonymousId();
13648
+ this.identity = {
13649
+ distinctId: null,
13650
+ properties: {},
13651
+ isIdentified: false
13652
+ };
13653
+ this.superProperties = {};
13654
+ (_a2 = this.persistence) == null ? void 0 : _a2.clear();
13655
+ (_b = this.persistence) == null ? void 0 : _b.set(PERSISTENCE_KEYS.DISTINCT_ID, this.distinctId);
13656
+ if (this.config.debug) {
13657
+ console.log("[BentoLabsSDK] Reset to anonymous:", this.distinctId);
13658
+ }
13659
+ }
13660
+ /**
13661
+ * Get the current distinct ID
13662
+ */
13663
+ getDistinctId() {
13664
+ return this.distinctId;
13665
+ }
13666
+ // ============================================================================
13667
+ // Super Properties Methods
13668
+ // ============================================================================
13669
+ /**
13670
+ * Register super properties to be sent with every event
13671
+ */
13672
+ register(properties) {
13673
+ var _a2;
13674
+ this.superProperties = __spreadValues(__spreadValues({}, this.superProperties), properties);
13675
+ (_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.SUPER_PROPERTIES, JSON.stringify(this.superProperties));
13676
+ if (this.config.debug) {
13677
+ console.log("[BentoLabsSDK] Registered super properties:", properties);
13678
+ }
13679
+ }
13680
+ /**
13681
+ * Register super properties only if they haven't been set
13682
+ */
13683
+ registerOnce(properties) {
13684
+ const newProps = {};
13685
+ for (const [key, value] of Object.entries(properties)) {
13686
+ if (!(key in this.superProperties)) {
13687
+ newProps[key] = value;
12708
13688
  }
12709
- );
13689
+ }
13690
+ if (Object.keys(newProps).length > 0) {
13691
+ this.register(newProps);
13692
+ }
12710
13693
  }
12711
13694
  /**
12712
- * Start recording user interactions
13695
+ * Remove a super property
12713
13696
  */
12714
- startRecording() {
12715
- if (!this.config.enableRecording) {
13697
+ unregister(propertyName) {
13698
+ var _a2;
13699
+ delete this.superProperties[propertyName];
13700
+ (_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.SUPER_PROPERTIES, JSON.stringify(this.superProperties));
13701
+ if (this.config.debug) {
13702
+ console.log("[BentoLabsSDK] Unregistered super property:", propertyName);
13703
+ }
13704
+ }
13705
+ // ============================================================================
13706
+ // Event Capture
13707
+ // ============================================================================
13708
+ /**
13709
+ * Capture an event
13710
+ */
13711
+ capture(eventName, properties) {
13712
+ if (!this.initialized) {
13713
+ console.warn("[BentoLabsSDK] Cannot capture event: SDK not initialized");
13714
+ return;
13715
+ }
13716
+ if (this.optedOut) {
12716
13717
  if (this.config.debug) {
12717
- console.log("[BentoLabsSDK] Recording disabled in configuration");
13718
+ console.log("[BentoLabsSDK] Event not captured (opted out):", eventName);
12718
13719
  }
12719
13720
  return;
12720
13721
  }
13722
+ const event = {
13723
+ event: eventName,
13724
+ properties: __spreadProps(__spreadValues(__spreadValues({}, this.superProperties), properties), {
13725
+ $current_url: typeof window !== "undefined" ? window.location.href : "",
13726
+ $pathname: typeof window !== "undefined" ? window.location.pathname : ""
13727
+ }),
13728
+ $timestamp: Date.now(),
13729
+ $session_id: this.sessionId,
13730
+ $distinct_id: this.distinctId
13731
+ };
13732
+ this.addEvent({
13733
+ timestamp: event.$timestamp,
13734
+ type: `capture:${eventName}`,
13735
+ data: event,
13736
+ sessionId: this.sessionId
13737
+ });
12721
13738
  if (this.config.debug) {
12722
- console.log(
12723
- "[BentoLabsSDK] Starting recording for session:",
12724
- this.sessionId
12725
- );
13739
+ console.log("[BentoLabsSDK] Captured event:", eventName, properties);
12726
13740
  }
12727
- try {
13741
+ }
13742
+ /**
13743
+ * Track a custom event (legacy method, delegates to capture)
13744
+ */
13745
+ trackCustomEvent(eventName, data) {
13746
+ if (!this.sessionId) {
13747
+ console.warn("[BentoLabsSDK] Cannot track event: SDK not initialized");
13748
+ return;
13749
+ }
13750
+ this.capture(`custom:${eventName}`, data);
13751
+ }
13752
+ // ============================================================================
13753
+ // Opt-in/Opt-out
13754
+ // ============================================================================
13755
+ /**
13756
+ * Opt out of tracking
13757
+ */
13758
+ opt_out_capturing() {
13759
+ var _a2;
13760
+ this.optedOut = true;
13761
+ (_a2 = this.persistence) == null ? void 0 : _a2.set(PERSISTENCE_KEYS.OPTED_OUT, "true");
13762
+ if (this.isRecording) {
13763
+ this.stopRecordingInternal();
13764
+ }
13765
+ this.stopNetworkCapture();
13766
+ this.stopStorageCapture();
13767
+ this.stopConsoleCapture();
13768
+ if (this.config.debug) {
13769
+ console.log("[BentoLabsSDK] Opted out of capturing");
13770
+ }
13771
+ }
13772
+ /**
13773
+ * Opt back into tracking
13774
+ */
13775
+ opt_in_capturing() {
13776
+ var _a2;
13777
+ this.optedOut = false;
13778
+ (_a2 = this.persistence) == null ? void 0 : _a2.remove(PERSISTENCE_KEYS.OPTED_OUT);
13779
+ if (this.initialized && this.config.enableRecording) {
13780
+ this.startRecording();
13781
+ }
13782
+ if (this.config.capture_network) {
13783
+ this.startNetworkCapture();
13784
+ }
13785
+ if (this.config.capture_storage) {
13786
+ this.startStorageCapture();
13787
+ }
13788
+ if (this.config.capture_console) {
13789
+ this.startConsoleCapture();
13790
+ }
13791
+ if (this.config.debug) {
13792
+ console.log("[BentoLabsSDK] Opted back into capturing");
13793
+ }
13794
+ }
13795
+ /**
13796
+ * Check if user has opted out
13797
+ */
13798
+ has_opted_out_capturing() {
13799
+ return this.optedOut;
13800
+ }
13801
+ /**
13802
+ * Check if user has opted in (not opted out)
13803
+ */
13804
+ has_opted_in_capturing() {
13805
+ return !this.optedOut;
13806
+ }
13807
+ // ============================================================================
13808
+ // Monitoring Control
13809
+ // ============================================================================
13810
+ /**
13811
+ * Start network request monitoring
13812
+ */
13813
+ startNetworkCapture() {
13814
+ if (this.networkMonitor) return;
13815
+ this.networkMonitor = new NetworkMonitor(this.config.networkOptions, (log) => {
13816
+ if (!this.optedOut) {
13817
+ this.capture("$network_request", log);
13818
+ }
13819
+ });
13820
+ this.networkMonitor.start();
13821
+ if (this.config.debug) {
13822
+ console.log("[BentoLabsSDK] Network capture started");
13823
+ }
13824
+ }
13825
+ /**
13826
+ * Stop network request monitoring
13827
+ */
13828
+ stopNetworkCapture() {
13829
+ if (this.networkMonitor) {
13830
+ this.networkMonitor.stop();
13831
+ this.networkMonitor = null;
13832
+ if (this.config.debug) {
13833
+ console.log("[BentoLabsSDK] Network capture stopped");
13834
+ }
13835
+ }
13836
+ }
13837
+ /**
13838
+ * Start storage monitoring
13839
+ */
13840
+ startStorageCapture() {
13841
+ if (this.storageMonitor) return;
13842
+ this.storageMonitor = new StorageMonitor(this.config.storageOptions, (log) => {
13843
+ if (!this.optedOut) {
13844
+ this.capture("$storage_event", log);
13845
+ }
13846
+ });
13847
+ this.storageMonitor.start();
13848
+ if (this.config.debug) {
13849
+ console.log("[BentoLabsSDK] Storage capture started");
13850
+ }
13851
+ }
13852
+ /**
13853
+ * Stop storage monitoring
13854
+ */
13855
+ stopStorageCapture() {
13856
+ if (this.storageMonitor) {
13857
+ this.storageMonitor.stop();
13858
+ this.storageMonitor = null;
13859
+ if (this.config.debug) {
13860
+ console.log("[BentoLabsSDK] Storage capture stopped");
13861
+ }
13862
+ }
13863
+ }
13864
+ /**
13865
+ * Start console monitoring
13866
+ */
13867
+ startConsoleCapture() {
13868
+ if (this.consoleMonitor) return;
13869
+ this.consoleMonitor = new ConsoleMonitor(this.config.consoleOptions, (log) => {
13870
+ if (!this.optedOut) {
13871
+ this.capture("$console_log", log);
13872
+ }
13873
+ });
13874
+ this.consoleMonitor.start();
13875
+ if (this.config.debug) {
13876
+ console.log("[BentoLabsSDK] Console capture started");
13877
+ }
13878
+ }
13879
+ /**
13880
+ * Stop console monitoring
13881
+ */
13882
+ stopConsoleCapture() {
13883
+ if (this.consoleMonitor) {
13884
+ this.consoleMonitor.stop();
13885
+ this.consoleMonitor = null;
13886
+ if (this.config.debug) {
13887
+ console.log("[BentoLabsSDK] Console capture stopped");
13888
+ }
13889
+ }
13890
+ }
13891
+ /**
13892
+ * Get network logs
13893
+ */
13894
+ getNetworkLogs() {
13895
+ var _a2;
13896
+ return ((_a2 = this.networkMonitor) == null ? void 0 : _a2.getLogs()) || [];
13897
+ }
13898
+ /**
13899
+ * Get storage logs
13900
+ */
13901
+ getStorageLogs() {
13902
+ var _a2;
13903
+ return ((_a2 = this.storageMonitor) == null ? void 0 : _a2.getLogs()) || [];
13904
+ }
13905
+ /**
13906
+ * Get console logs
13907
+ */
13908
+ getConsoleLogs() {
13909
+ var _a2;
13910
+ return ((_a2 = this.consoleMonitor) == null ? void 0 : _a2.getLogs()) || [];
13911
+ }
13912
+ // ============================================================================
13913
+ // Existing Public Methods (Preserved)
13914
+ // ============================================================================
13915
+ /**
13916
+ * Stop recording and clean up resources
13917
+ */
13918
+ stop() {
13919
+ if (this.config.debug) {
13920
+ console.log("[BentoLabsSDK] Stopping recording and batching");
13921
+ }
13922
+ this.stopRecordingInternal();
13923
+ this.stopNetworkCapture();
13924
+ this.stopStorageCapture();
13925
+ this.stopConsoleCapture();
13926
+ if (this.events.length > 0) {
13927
+ this.sendBatch();
13928
+ }
13929
+ if (this.config.debug) {
13930
+ console.log("[BentoLabsSDK] Stopped successfully");
13931
+ }
13932
+ }
13933
+ /**
13934
+ * Get current session ID
13935
+ */
13936
+ getSessionId() {
13937
+ return this.sessionId;
13938
+ }
13939
+ /**
13940
+ * Check if recording is active
13941
+ */
13942
+ isRecordingActive() {
13943
+ return this.isRecording;
13944
+ }
13945
+ /**
13946
+ * Get current configuration (without exposing sensitive data)
13947
+ */
13948
+ getConfig() {
13949
+ return {
13950
+ apiKey: this.config.apiKey ? this.config.apiKey.substring(0, 8) + "..." : "",
13951
+ endpoint: this.config.endpoint,
13952
+ debug: this.config.debug,
13953
+ batchSize: this.config.batchSize,
13954
+ batchInterval: this.config.batchInterval,
13955
+ enableRecording: this.config.enableRecording,
13956
+ maxRetries: this.config.maxRetries,
13957
+ baseRetryDelay: this.config.baseRetryDelay,
13958
+ enableCompression: this.config.enableCompression,
13959
+ persistence: this.config.persistence,
13960
+ respect_dnt: this.config.respect_dnt,
13961
+ autocapture: this.config.autocapture,
13962
+ capture_pageview: this.config.capture_pageview,
13963
+ capture_pageleave: this.config.capture_pageleave,
13964
+ capture_network: this.config.capture_network,
13965
+ networkOptions: this.config.networkOptions,
13966
+ capture_storage: this.config.capture_storage,
13967
+ storageOptions: this.config.storageOptions,
13968
+ capture_console: this.config.capture_console,
13969
+ consoleOptions: this.config.consoleOptions
13970
+ };
13971
+ }
13972
+ /**
13973
+ * Get current event queue length
13974
+ */
13975
+ getEventQueueLength() {
13976
+ return this.events.length;
13977
+ }
13978
+ /**
13979
+ * Manually trigger a batch send
13980
+ */
13981
+ async flushEvents() {
13982
+ await this.sendBatch();
13983
+ }
13984
+ // ============================================================================
13985
+ // Private Methods
13986
+ // ============================================================================
13987
+ generateSessionId() {
13988
+ const uuid = this.generateUUID();
13989
+ return `sess_${uuid}`;
13990
+ }
13991
+ generateAnonymousId() {
13992
+ const uuid = this.generateUUID();
13993
+ return `anon_${uuid}`;
13994
+ }
13995
+ generateUUID() {
13996
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
13997
+ const r = Math.random() * 16 | 0;
13998
+ const v = c === "x" ? r : r & 3 | 8;
13999
+ return v.toString(16);
14000
+ });
14001
+ }
14002
+ isDNTEnabled() {
14003
+ if (typeof navigator === "undefined") return false;
14004
+ return navigator.doNotTrack === "1" || navigator.doNotTrack === "yes";
14005
+ }
14006
+ loadPersistedState() {
14007
+ if (!this.persistence) return;
14008
+ const storedDistinctId = this.persistence.get(PERSISTENCE_KEYS.DISTINCT_ID);
14009
+ if (storedDistinctId) {
14010
+ this.distinctId = storedDistinctId;
14011
+ this.identity.distinctId = storedDistinctId;
14012
+ }
14013
+ const isIdentified = this.persistence.get(PERSISTENCE_KEYS.IS_IDENTIFIED);
14014
+ if (isIdentified === "true") {
14015
+ this.identity.isIdentified = true;
14016
+ }
14017
+ const storedSuperProps = this.persistence.get(PERSISTENCE_KEYS.SUPER_PROPERTIES);
14018
+ if (storedSuperProps) {
14019
+ try {
14020
+ this.superProperties = JSON.parse(storedSuperProps);
14021
+ } catch (e) {
14022
+ this.superProperties = {};
14023
+ }
14024
+ }
14025
+ const storedUserProps = this.persistence.get(PERSISTENCE_KEYS.USER_PROPERTIES);
14026
+ if (storedUserProps) {
14027
+ try {
14028
+ this.identity.properties = JSON.parse(storedUserProps);
14029
+ } catch (e) {
14030
+ this.identity.properties = {};
14031
+ }
14032
+ }
14033
+ const optedOut = this.persistence.get(PERSISTENCE_KEYS.OPTED_OUT);
14034
+ if (optedOut === "true") {
14035
+ this.optedOut = true;
14036
+ }
14037
+ }
14038
+ startRecording() {
14039
+ if (!this.config.enableRecording) {
14040
+ if (this.config.debug) {
14041
+ console.log("[BentoLabsSDK] Recording disabled in configuration");
14042
+ }
14043
+ return;
14044
+ }
14045
+ if (this.optedOut) {
14046
+ if (this.config.debug) {
14047
+ console.log("[BentoLabsSDK] Recording disabled (opted out)");
14048
+ }
14049
+ return;
14050
+ }
14051
+ if (this.config.debug) {
14052
+ console.log("[BentoLabsSDK] Starting recording for session:", this.sessionId);
14053
+ }
14054
+ try {
12728
14055
  if (this.config.debug) {
12729
14056
  console.log("[BentoLabsSDK] Calling rrweb record()...");
12730
14057
  }
@@ -12743,7 +14070,6 @@ var BentoLabsSDK = class {
12743
14070
  recordCanvas: true,
12744
14071
  collectFonts: true,
12745
14072
  plugins: [],
12746
- // Enable element context capture for rich metadata on interactions
12747
14073
  captureElementContext: true,
12748
14074
  elementContextOptions: {
12749
14075
  maxTextLength: 500,
@@ -12751,33 +14077,28 @@ var BentoLabsSDK = class {
12751
14077
  outerHTMLDepth: 2,
12752
14078
  includeInputValues: true
12753
14079
  },
12754
- // Mask password inputs for security
12755
14080
  maskInputOptions: {
12756
14081
  password: true
12757
14082
  },
12758
- // Sampling and throttling to reduce event volume
12759
14083
  sampling: {
12760
14084
  mousemove: true,
12761
- // Sample mouse movements
12762
14085
  mouseInteraction: { click: false, dblclick: false },
12763
- // Capture all clicks
12764
14086
  scroll: 150,
12765
- // Throttle scroll events to 150ms
12766
14087
  input: "last"
12767
- // Only record final input value
12768
14088
  },
12769
14089
  checkoutEveryNms: 5 * 60 * 1e3
12770
- // Take full snapshot every 5 minutes
12771
14090
  });
12772
14091
  if (typeof stopFn === "function") {
12773
- this.stopRecording = stopFn;
14092
+ this.stopRecordingFn = stopFn;
12774
14093
  this.isRecording = true;
12775
14094
  if (this.config.debug) {
12776
14095
  console.log("[BentoLabsSDK] Recording started successfully");
12777
14096
  }
12778
14097
  } else {
12779
- console.error("[BentoLabsSDK] rrweb record() did not return a stop function. Recording may not have started properly.");
12780
- this.stopRecording = null;
14098
+ console.error(
14099
+ "[BentoLabsSDK] rrweb record() did not return a stop function. Recording may not have started properly."
14100
+ );
14101
+ this.stopRecordingFn = null;
12781
14102
  this.isRecording = false;
12782
14103
  }
12783
14104
  } catch (error) {
@@ -12785,9 +14106,6 @@ var BentoLabsSDK = class {
12785
14106
  this.isRecording = false;
12786
14107
  }
12787
14108
  }
12788
- /**
12789
- * Start batching events for transmission
12790
- */
12791
14109
  startBatching() {
12792
14110
  if (this.config.debug) {
12793
14111
  console.log("[BentoLabsSDK] Starting event batching");
@@ -12799,31 +14117,22 @@ var BentoLabsSDK = class {
12799
14117
  this.sendBatch();
12800
14118
  }, this.config.batchInterval);
12801
14119
  if (this.config.debug) {
12802
- console.log(
12803
- `[BentoLabsSDK] Batch timer set for ${this.config.batchInterval}ms intervals`
12804
- );
14120
+ console.log(`[BentoLabsSDK] Batch timer set for ${this.config.batchInterval}ms intervals`);
12805
14121
  }
12806
14122
  }
12807
- /**
12808
- * Add an event to the events array
12809
- */
12810
14123
  addEvent(event) {
14124
+ if (this.optedOut) return;
12811
14125
  this.events.push(event);
12812
14126
  if (this.config.debug) {
12813
14127
  console.log("[BentoLabsSDK] Event added:", event.type);
12814
14128
  }
12815
14129
  if (this.events.length >= this.config.batchSize) {
12816
14130
  if (this.config.debug) {
12817
- console.log(
12818
- `[BentoLabsSDK] Batch size limit reached (${this.config.batchSize}), sending batch`
12819
- );
14131
+ console.log(`[BentoLabsSDK] Batch size limit reached (${this.config.batchSize}), sending batch`);
12820
14132
  }
12821
14133
  this.sendBatch();
12822
14134
  }
12823
14135
  }
12824
- /**
12825
- * Send batched events to the API with exponential backoff retry
12826
- */
12827
14136
  async sendBatch() {
12828
14137
  const now = Date.now();
12829
14138
  const readyEvents = this.events.filter((event) => {
@@ -12841,12 +14150,11 @@ var BentoLabsSDK = class {
12841
14150
  events: readyEvents,
12842
14151
  timestamp: now,
12843
14152
  userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "Unknown",
12844
- url: typeof window !== "undefined" ? window.location.href : "Unknown"
14153
+ url: typeof window !== "undefined" ? window.location.href : "Unknown",
14154
+ distinctId: this.distinctId
12845
14155
  };
12846
14156
  if (this.config.debug) {
12847
- console.log(
12848
- `[BentoLabsSDK] Sending batch with ${readyEvents.length} events`
12849
- );
14157
+ console.log(`[BentoLabsSDK] Sending batch with ${readyEvents.length} events`);
12850
14158
  }
12851
14159
  try {
12852
14160
  const response = await fetch(`${this.config.endpoint}/events/`, {
@@ -12874,43 +14182,30 @@ var BentoLabsSDK = class {
12874
14182
  nextRetryTime: now + delay
12875
14183
  });
12876
14184
  });
12877
- const eventsToRetry = retryableEvents.filter(
12878
- (event) => event.retryCount <= this.config.maxRetries
12879
- );
14185
+ const eventsToRetry = retryableEvents.filter((event) => event.retryCount <= this.config.maxRetries);
12880
14186
  const droppedEvents = retryableEvents.length - eventsToRetry.length;
12881
14187
  if (droppedEvents > 0 && this.config.debug) {
12882
- console.log(
12883
- `[BentoLabsSDK] Dropped ${droppedEvents} events after max retries`
12884
- );
14188
+ console.log(`[BentoLabsSDK] Dropped ${droppedEvents} events after max retries`);
12885
14189
  }
12886
14190
  this.events.unshift(...eventsToRetry);
12887
14191
  if (this.config.debug && eventsToRetry.length > 0) {
12888
14192
  const nextRetryIn = Math.min(...eventsToRetry.map((e) => e.nextRetryTime)) - now;
12889
- console.log(
12890
- `[BentoLabsSDK] ${eventsToRetry.length} events re-queued for retry in ${nextRetryIn}ms`
12891
- );
14193
+ console.log(`[BentoLabsSDK] ${eventsToRetry.length} events re-queued for retry in ${nextRetryIn}ms`);
12892
14194
  }
12893
14195
  this.scheduleRetry();
12894
14196
  }
12895
14197
  }
12896
- /**
12897
- * Schedule retry attempts for failed events
12898
- */
12899
14198
  scheduleRetry() {
12900
14199
  if (this.retryTimer) {
12901
14200
  clearTimeout(this.retryTimer);
12902
14201
  this.retryTimer = null;
12903
14202
  }
12904
14203
  const now = Date.now();
12905
- const retryableEvents = this.events.filter(
12906
- (event) => "nextRetryTime" in event
12907
- );
14204
+ const retryableEvents = this.events.filter((event) => "nextRetryTime" in event);
12908
14205
  if (retryableEvents.length === 0) {
12909
14206
  return;
12910
14207
  }
12911
- const nextRetryTime = Math.min(
12912
- ...retryableEvents.map((e) => e.nextRetryTime)
12913
- );
14208
+ const nextRetryTime = Math.min(...retryableEvents.map((e) => e.nextRetryTime));
12914
14209
  const delay = Math.max(0, nextRetryTime - now);
12915
14210
  if (this.config.debug) {
12916
14211
  console.log(`[BentoLabsSDK] Scheduling retry in ${delay}ms`);
@@ -12919,13 +14214,10 @@ var BentoLabsSDK = class {
12919
14214
  this.sendBatch();
12920
14215
  }, delay);
12921
14216
  }
12922
- /**
12923
- * Internal method to stop recording without logging (used during re-init)
12924
- */
12925
14217
  stopRecordingInternal() {
12926
- if (this.stopRecording) {
12927
- this.stopRecording();
12928
- this.stopRecording = null;
14218
+ if (this.stopRecordingFn) {
14219
+ this.stopRecordingFn();
14220
+ this.stopRecordingFn = null;
12929
14221
  }
12930
14222
  if (this.batchTimer) {
12931
14223
  clearInterval(this.batchTimer);
@@ -12935,86 +14227,115 @@ var BentoLabsSDK = class {
12935
14227
  clearTimeout(this.retryTimer);
12936
14228
  this.retryTimer = null;
12937
14229
  }
14230
+ if (this.autocaptureCleanup) {
14231
+ this.autocaptureCleanup();
14232
+ this.autocaptureCleanup = null;
14233
+ }
12938
14234
  this.isRecording = false;
12939
14235
  }
12940
- /**
12941
- * Stop recording and clean up resources
12942
- */
12943
- stop() {
12944
- if (this.config.debug) {
12945
- console.log("[BentoLabsSDK] Stopping recording and batching");
12946
- }
12947
- this.stopRecordingInternal();
12948
- if (this.events.length > 0) {
12949
- this.sendBatch();
12950
- }
14236
+ setupAutocapture() {
14237
+ if (typeof document === "undefined") return;
14238
+ const handleClick = (e) => {
14239
+ var _a2;
14240
+ const target = e.target;
14241
+ if (!target) return;
14242
+ const tagName = target.tagName.toLowerCase();
14243
+ const allowedTags = ["a", "button", "input", "select", "textarea"];
14244
+ const isAllowed = allowedTags.includes(tagName) || target.getAttribute("role") === "button" || target.hasAttribute("data-bento-capture");
14245
+ if (!isAllowed) return;
14246
+ let selector = "";
14247
+ try {
14248
+ selector = generateSelectorFromElement(target);
14249
+ } catch (e2) {
14250
+ selector = "";
14251
+ }
14252
+ this.capture("$autocapture", {
14253
+ $event_type: "click",
14254
+ $element_tag: tagName,
14255
+ $element_text: ((_a2 = target.textContent) == null ? void 0 : _a2.slice(0, 100)) || "",
14256
+ $element_classes: target.className || "",
14257
+ $element_id: target.id || "",
14258
+ $bento_selector: selector
14259
+ });
14260
+ };
14261
+ const handleChange = (e) => {
14262
+ const target = e.target;
14263
+ if (!target) return;
14264
+ const tagName = target.tagName.toLowerCase();
14265
+ if (!["input", "select", "textarea"].includes(tagName)) return;
14266
+ if (target.type === "password") return;
14267
+ let selector = "";
14268
+ try {
14269
+ selector = generateSelectorFromElement(target);
14270
+ } catch (e2) {
14271
+ selector = "";
14272
+ }
14273
+ this.capture("$autocapture", {
14274
+ $event_type: "change",
14275
+ $element_tag: tagName,
14276
+ $element_type: target.type || "",
14277
+ $element_name: target.name || "",
14278
+ $element_id: target.id || "",
14279
+ $bento_selector: selector
14280
+ // Don't capture actual value for privacy
14281
+ });
14282
+ };
14283
+ const handleSubmit = (e) => {
14284
+ const target = e.target;
14285
+ if (!target || target.tagName.toLowerCase() !== "form") return;
14286
+ let selector = "";
14287
+ try {
14288
+ selector = generateSelectorFromElement(target);
14289
+ } catch (e2) {
14290
+ selector = "";
14291
+ }
14292
+ this.capture("$autocapture", {
14293
+ $event_type: "submit",
14294
+ $element_tag: "form",
14295
+ $form_name: target.name || "",
14296
+ $form_id: target.id || "",
14297
+ $form_action: target.action || "",
14298
+ $bento_selector: selector
14299
+ });
14300
+ };
14301
+ document.addEventListener("click", handleClick, true);
14302
+ document.addEventListener("change", handleChange, true);
14303
+ document.addEventListener("submit", handleSubmit, true);
14304
+ this.autocaptureCleanup = () => {
14305
+ document.removeEventListener("click", handleClick, true);
14306
+ document.removeEventListener("change", handleChange, true);
14307
+ document.removeEventListener("submit", handleSubmit, true);
14308
+ };
12951
14309
  if (this.config.debug) {
12952
- console.log("[BentoLabsSDK] Stopped successfully");
14310
+ console.log("[BentoLabsSDK] Autocapture setup complete");
12953
14311
  }
12954
14312
  }
12955
- /**
12956
- * Get current session ID
12957
- */
12958
- getSessionId() {
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
14313
+ handlePageLeave() {
14314
+ this.capture("$pageleave", {
14315
+ $current_url: typeof window !== "undefined" ? window.location.href : "",
14316
+ $pathname: typeof window !== "undefined" ? window.location.pathname : ""
13009
14317
  });
13010
- if (this.config.debug) {
13011
- console.log(`[BentoLabsSDK] Custom event tracked: ${eventName}`, data);
14318
+ if (this.events.length > 0 && typeof navigator !== "undefined" && navigator.sendBeacon) {
14319
+ const payload = {
14320
+ sessionId: this.sessionId,
14321
+ events: this.events,
14322
+ timestamp: Date.now(),
14323
+ userAgent: navigator.userAgent,
14324
+ url: window.location.href,
14325
+ distinctId: this.distinctId
14326
+ };
14327
+ navigator.sendBeacon(
14328
+ `${this.config.endpoint}/events/`,
14329
+ new Blob([JSON.stringify(payload)], { type: "application/json" })
14330
+ );
14331
+ this.events = [];
13012
14332
  }
13013
14333
  }
13014
14334
  };
13015
14335
  var sdk = new BentoLabsSDK();
13016
14336
  if (typeof window !== "undefined") {
13017
- window.BentoLabsSDK = __spreadProps(__spreadValues({}, sdk), {
14337
+ window.BentoLabsSDK = {
14338
+ // Core methods
13018
14339
  init: sdk.init.bind(sdk),
13019
14340
  stop: sdk.stop.bind(sdk),
13020
14341
  getSessionId: sdk.getSessionId.bind(sdk),
@@ -13023,8 +14344,34 @@ if (typeof window !== "undefined") {
13023
14344
  getEventQueueLength: sdk.getEventQueueLength.bind(sdk),
13024
14345
  flushEvents: sdk.flushEvents.bind(sdk),
13025
14346
  trackCustomEvent: sdk.trackCustomEvent.bind(sdk),
13026
- generateSelector
13027
- });
14347
+ // Selector generation
14348
+ generateSelector,
14349
+ generateSelectorFromElement,
14350
+ generateSelectorFromHTML,
14351
+ // PostHog-like methods
14352
+ capture: sdk.capture.bind(sdk),
14353
+ identify: sdk.identify.bind(sdk),
14354
+ reset: sdk.reset.bind(sdk),
14355
+ getDistinctId: sdk.getDistinctId.bind(sdk),
14356
+ register: sdk.register.bind(sdk),
14357
+ registerOnce: sdk.registerOnce.bind(sdk),
14358
+ unregister: sdk.unregister.bind(sdk),
14359
+ opt_out_capturing: sdk.opt_out_capturing.bind(sdk),
14360
+ opt_in_capturing: sdk.opt_in_capturing.bind(sdk),
14361
+ has_opted_out_capturing: sdk.has_opted_out_capturing.bind(sdk),
14362
+ has_opted_in_capturing: sdk.has_opted_in_capturing.bind(sdk),
14363
+ people: sdk.people,
14364
+ // Monitoring control
14365
+ startNetworkCapture: sdk.startNetworkCapture.bind(sdk),
14366
+ stopNetworkCapture: sdk.stopNetworkCapture.bind(sdk),
14367
+ startStorageCapture: sdk.startStorageCapture.bind(sdk),
14368
+ stopStorageCapture: sdk.stopStorageCapture.bind(sdk),
14369
+ startConsoleCapture: sdk.startConsoleCapture.bind(sdk),
14370
+ stopConsoleCapture: sdk.stopConsoleCapture.bind(sdk),
14371
+ getNetworkLogs: sdk.getNetworkLogs.bind(sdk),
14372
+ getStorageLogs: sdk.getStorageLogs.bind(sdk),
14373
+ getConsoleLogs: sdk.getConsoleLogs.bind(sdk)
14374
+ };
13028
14375
  }
13029
14376
  var index_default = sdk;
13030
14377
  /*! Bundled license information: