veo-sdk 0.6.2 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder-5F2EFT4X.mjs +5 -0
- package/dist/{builder-ENKEIVF2.mjs.map → builder-5F2EFT4X.mjs.map} +1 -1
- package/dist/builder.cjs +396 -103
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +76 -6
- package/dist/builder.d.ts +76 -6
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-L3QQJLDJ.mjs → chunk-72YH2GXC.mjs} +3 -3
- package/dist/{chunk-L3QQJLDJ.mjs.map → chunk-72YH2GXC.mjs.map} +1 -1
- package/dist/{chunk-ZILDR4YE.mjs → chunk-CMPOHTUM.mjs} +112 -80
- package/dist/chunk-CMPOHTUM.mjs.map +1 -0
- package/dist/{chunk-4QZDNJJE.mjs → chunk-YEKCBVNJ.mjs} +284 -15
- package/dist/chunk-YEKCBVNJ.mjs.map +1 -0
- package/dist/index.cjs +653 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.mjs +308 -7
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +54 -19
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +6 -6
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo.js +2 -2
- package/dist/veo.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-ENKEIVF2.mjs +0 -5
- package/dist/chunk-4QZDNJJE.mjs.map +0 -1
- package/dist/chunk-ZILDR4YE.mjs.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -26,6 +26,11 @@ interface VeoAutocaptureConfig {
|
|
|
26
26
|
captureElementMetadata?: boolean;
|
|
27
27
|
/** Niveles de ancestros a capturar. Default: 5. Max: 10. */
|
|
28
28
|
captureAncestors?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Calidad de los clics: emite `$dead_click` (clic sin reacción), `$rage_click`
|
|
31
|
+
* (ráfaga de clics) y `$click_error` (clic seguido de un error). Default: true.
|
|
32
|
+
*/
|
|
33
|
+
captureClickQuality?: boolean;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* Configuración del cliente Veo.
|
|
@@ -127,6 +132,8 @@ type VeoClientEventListener<E extends VeoClientEventName> = (payload: VeoClientE
|
|
|
127
132
|
* Forward declaration del client (implementación viene en core/client.ts).
|
|
128
133
|
*/
|
|
129
134
|
interface VeoClient {
|
|
135
|
+
/** Versión del SDK cargado (la de package.json en el build). `window.veo.version` en consola. */
|
|
136
|
+
readonly version: string;
|
|
130
137
|
identify(payload: {
|
|
131
138
|
visitor: VeoVisitor;
|
|
132
139
|
organization?: VeoOrganization;
|
|
@@ -180,7 +187,8 @@ declare function guidesPlugin(config: GuidesPluginConfig): VeoPlugin;
|
|
|
180
187
|
* - veo-backend/src/modules/ingest/dto/*.dto.ts
|
|
181
188
|
* - CONTRACT.md
|
|
182
189
|
*/
|
|
183
|
-
|
|
190
|
+
/** Versión del SDK, inyectada en build desde package.json (tsup `define`); fallback en tests/dev. */
|
|
191
|
+
declare const SDK_VERSION: string;
|
|
184
192
|
|
|
185
193
|
/**
|
|
186
194
|
* Cliente principal del SDK. Expone la API pública (`identify`, `track`,
|
|
@@ -189,6 +197,7 @@ declare const SDK_VERSION = "0.0.1";
|
|
|
189
197
|
*/
|
|
190
198
|
declare class Client implements VeoClient {
|
|
191
199
|
private readonly config;
|
|
200
|
+
readonly version: string;
|
|
192
201
|
private readonly transport;
|
|
193
202
|
private readonly storage;
|
|
194
203
|
private readonly session;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ interface VeoAutocaptureConfig {
|
|
|
26
26
|
captureElementMetadata?: boolean;
|
|
27
27
|
/** Niveles de ancestros a capturar. Default: 5. Max: 10. */
|
|
28
28
|
captureAncestors?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Calidad de los clics: emite `$dead_click` (clic sin reacción), `$rage_click`
|
|
31
|
+
* (ráfaga de clics) y `$click_error` (clic seguido de un error). Default: true.
|
|
32
|
+
*/
|
|
33
|
+
captureClickQuality?: boolean;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* Configuración del cliente Veo.
|
|
@@ -127,6 +132,8 @@ type VeoClientEventListener<E extends VeoClientEventName> = (payload: VeoClientE
|
|
|
127
132
|
* Forward declaration del client (implementación viene en core/client.ts).
|
|
128
133
|
*/
|
|
129
134
|
interface VeoClient {
|
|
135
|
+
/** Versión del SDK cargado (la de package.json en el build). `window.veo.version` en consola. */
|
|
136
|
+
readonly version: string;
|
|
130
137
|
identify(payload: {
|
|
131
138
|
visitor: VeoVisitor;
|
|
132
139
|
organization?: VeoOrganization;
|
|
@@ -180,7 +187,8 @@ declare function guidesPlugin(config: GuidesPluginConfig): VeoPlugin;
|
|
|
180
187
|
* - veo-backend/src/modules/ingest/dto/*.dto.ts
|
|
181
188
|
* - CONTRACT.md
|
|
182
189
|
*/
|
|
183
|
-
|
|
190
|
+
/** Versión del SDK, inyectada en build desde package.json (tsup `define`); fallback en tests/dev. */
|
|
191
|
+
declare const SDK_VERSION: string;
|
|
184
192
|
|
|
185
193
|
/**
|
|
186
194
|
* Cliente principal del SDK. Expone la API pública (`identify`, `track`,
|
|
@@ -189,6 +197,7 @@ declare const SDK_VERSION = "0.0.1";
|
|
|
189
197
|
*/
|
|
190
198
|
declare class Client implements VeoClient {
|
|
191
199
|
private readonly config;
|
|
200
|
+
readonly version: string;
|
|
192
201
|
private readonly transport;
|
|
193
202
|
private readonly storage;
|
|
194
203
|
private readonly session;
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { definePlugin, guidesPlugin, runPreviewMode } from './chunk-
|
|
2
|
-
export { definePlugin, guidesPlugin } from './chunk-
|
|
3
|
-
import { hasWindow, hasDocument, uuidv7, DEFAULT_AUTOCAPTURE_CONFIG, MAX_ANCESTORS, hasLocalStorage, hasNavigatorSendBeacon, isBuilderMode,
|
|
4
|
-
export { closeGuidePreview, previewGuide } from './chunk-
|
|
1
|
+
import { definePlugin, guidesPlugin, runPreviewMode } from './chunk-72YH2GXC.mjs';
|
|
2
|
+
export { definePlugin, guidesPlugin } from './chunk-72YH2GXC.mjs';
|
|
3
|
+
import { hasWindow, hasDocument, uuidv7, DEFAULT_AUTOCAPTURE_CONFIG, MAX_ANCESTORS, hasLocalStorage, hasNavigatorSendBeacon, isBuilderMode, buildSelectorPath, ALWAYS_BLOCK_SELECTORS, resolveBuilderToken, filterHumanClasses, PRIORITY_ATTRIBUTES, SENSITIVE_ATTRIBUTES, getPreviewToken, previewGuide, closeGuidePreview } from './chunk-CMPOHTUM.mjs';
|
|
4
|
+
export { closeGuidePreview, previewGuide } from './chunk-CMPOHTUM.mjs';
|
|
5
5
|
import './chunk-PTG3BTJE.mjs';
|
|
6
6
|
|
|
7
7
|
// src/contract/v1.ts
|
|
8
|
-
var SDK_VERSION = "0.
|
|
8
|
+
var SDK_VERSION = "0.6.4";
|
|
9
9
|
var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
|
|
10
10
|
var DEFAULT_FLUSH_BATCH_SIZE = 20;
|
|
11
11
|
var DEFAULT_API_URL = "https://veo-backend-production-8c78.up.railway.app";
|
|
@@ -62,6 +62,294 @@ function deriveBuilderUrl() {
|
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// src/plugins/autocapture/click-quality.ts
|
|
66
|
+
var NO_DEAD_TAGS = /* @__PURE__ */ new Set(["input", "textarea", "select", "option", "label"]);
|
|
67
|
+
var MAX_ERROR_MESSAGE = 200;
|
|
68
|
+
var HISTORY_MS = 3e3;
|
|
69
|
+
var DEFAULTS = {
|
|
70
|
+
reactionWindowMs: 1e3,
|
|
71
|
+
errorWindowMs: 2e3,
|
|
72
|
+
rageClicks: 3,
|
|
73
|
+
rageWindowMs: 1e3,
|
|
74
|
+
rageClicksSlow: 5,
|
|
75
|
+
rageWindowSlowMs: 3e3,
|
|
76
|
+
maxPerElement: 50
|
|
77
|
+
};
|
|
78
|
+
function describeError(value) {
|
|
79
|
+
let text;
|
|
80
|
+
if (value instanceof Error) text = `${value.name}: ${value.message}`;
|
|
81
|
+
else if (typeof value === "string") text = value;
|
|
82
|
+
else {
|
|
83
|
+
try {
|
|
84
|
+
text = JSON.stringify(value) ?? String(value);
|
|
85
|
+
} catch {
|
|
86
|
+
text = String(value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return text.slice(0, MAX_ERROR_MESSAGE);
|
|
90
|
+
}
|
|
91
|
+
var ClickQualityTracker = class {
|
|
92
|
+
constructor(emit, options = {}) {
|
|
93
|
+
this.emit = emit;
|
|
94
|
+
this.installed = false;
|
|
95
|
+
this.cleanups = [];
|
|
96
|
+
// Sensores globales (contadores: cada observación compara su foto inicial).
|
|
97
|
+
this.netCount = 0;
|
|
98
|
+
this.navCount = 0;
|
|
99
|
+
this.focusCount = 0;
|
|
100
|
+
this.unloading = false;
|
|
101
|
+
this.errors = [];
|
|
102
|
+
this.history = /* @__PURE__ */ new Map();
|
|
103
|
+
this.bursts = /* @__PURE__ */ new Map();
|
|
104
|
+
this.emitted = /* @__PURE__ */ new Map();
|
|
105
|
+
this.opts = { ...DEFAULTS, ...options };
|
|
106
|
+
}
|
|
107
|
+
install() {
|
|
108
|
+
if (this.installed || !hasWindow()) return;
|
|
109
|
+
this.installed = true;
|
|
110
|
+
const on = (type, handler, capture = true) => {
|
|
111
|
+
window.addEventListener(type, handler, capture);
|
|
112
|
+
this.cleanups.push(() => window.removeEventListener(type, handler, capture));
|
|
113
|
+
};
|
|
114
|
+
on("error", (e) => {
|
|
115
|
+
const err = e;
|
|
116
|
+
this.pushError("error", err.error ?? err.message ?? "error");
|
|
117
|
+
});
|
|
118
|
+
on("unhandledrejection", (e) => {
|
|
119
|
+
this.pushError("unhandledrejection", e.reason ?? "rejection");
|
|
120
|
+
});
|
|
121
|
+
on("popstate", () => this.navCount++);
|
|
122
|
+
on("hashchange", () => this.navCount++);
|
|
123
|
+
on("focusin", () => this.focusCount++);
|
|
124
|
+
on("pagehide", () => {
|
|
125
|
+
this.unloading = true;
|
|
126
|
+
});
|
|
127
|
+
on("beforeunload", () => {
|
|
128
|
+
this.unloading = true;
|
|
129
|
+
});
|
|
130
|
+
this.patchHistory();
|
|
131
|
+
this.patchConsole();
|
|
132
|
+
this.patchFetch();
|
|
133
|
+
this.patchXhr();
|
|
134
|
+
}
|
|
135
|
+
destroy() {
|
|
136
|
+
for (const c of this.cleanups) c();
|
|
137
|
+
this.cleanups = [];
|
|
138
|
+
for (const b of this.bursts.values()) window.clearTimeout(b.timer);
|
|
139
|
+
this.bursts.clear();
|
|
140
|
+
this.history.clear();
|
|
141
|
+
this.installed = false;
|
|
142
|
+
}
|
|
143
|
+
/** Llamar por cada clic real sobre un elemento interactivo, ANTES del throttle. */
|
|
144
|
+
onClick(element, event) {
|
|
145
|
+
if (!this.installed) return;
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
const obs = { at: now, reacted: false };
|
|
148
|
+
this.remember(element, obs);
|
|
149
|
+
this.observeReaction(element, obs, event);
|
|
150
|
+
this.observeErrors(element, now);
|
|
151
|
+
this.trackRage(element, now);
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------- dead ----
|
|
154
|
+
observeReaction(element, obs, event) {
|
|
155
|
+
const netAtClick = this.netCount;
|
|
156
|
+
const navAtClick = this.navCount;
|
|
157
|
+
const focusAtClick = this.focusCount;
|
|
158
|
+
const hrefAtClick = window.location.href;
|
|
159
|
+
const skipDead = this.isDeadExempt(element, event);
|
|
160
|
+
let observer = null;
|
|
161
|
+
if (typeof MutationObserver !== "undefined") {
|
|
162
|
+
observer = new MutationObserver(() => {
|
|
163
|
+
obs.reacted = true;
|
|
164
|
+
observer?.disconnect();
|
|
165
|
+
observer = null;
|
|
166
|
+
});
|
|
167
|
+
observer.observe(document.documentElement, {
|
|
168
|
+
childList: true,
|
|
169
|
+
attributes: true,
|
|
170
|
+
characterData: true,
|
|
171
|
+
subtree: true
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
window.setTimeout(() => {
|
|
175
|
+
observer?.disconnect();
|
|
176
|
+
const reacted = obs.reacted || this.unloading || this.netCount !== netAtClick || this.navCount !== navAtClick || this.focusCount !== focusAtClick || window.location.href !== hrefAtClick;
|
|
177
|
+
obs.reacted = reacted;
|
|
178
|
+
if (!reacted && !skipDead) this.report("$dead_click", element, {});
|
|
179
|
+
}, this.opts.reactionWindowMs);
|
|
180
|
+
}
|
|
181
|
+
isDeadExempt(element, event) {
|
|
182
|
+
if (event && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey)) return true;
|
|
183
|
+
if (event && typeof event.button === "number" && event.button !== 0) return true;
|
|
184
|
+
const tag = element.tagName.toLowerCase();
|
|
185
|
+
if (NO_DEAD_TAGS.has(tag)) return true;
|
|
186
|
+
if (element.closest('[contenteditable=""],[contenteditable="true"]')) return true;
|
|
187
|
+
const anchor = element.closest("a");
|
|
188
|
+
if (anchor) {
|
|
189
|
+
if (anchor.target === "_blank" || anchor.hasAttribute("download")) return true;
|
|
190
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
191
|
+
if (/^(mailto|tel|sms):/i.test(href)) return true;
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
// --------------------------------------------------------------- error ----
|
|
196
|
+
observeErrors(element, clickedAt) {
|
|
197
|
+
const deadline = clickedAt + this.opts.errorWindowMs;
|
|
198
|
+
const check = () => {
|
|
199
|
+
const err = this.errors.find((e) => e.at >= clickedAt && e.at <= deadline);
|
|
200
|
+
if (err) {
|
|
201
|
+
this.report("$click_error", element, {
|
|
202
|
+
$error_source: err.source,
|
|
203
|
+
$error_message: err.message
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (Date.now() < deadline) window.setTimeout(check, 250);
|
|
208
|
+
};
|
|
209
|
+
window.setTimeout(check, 250);
|
|
210
|
+
}
|
|
211
|
+
pushError(source, value) {
|
|
212
|
+
const message = describeError(value);
|
|
213
|
+
if (message.startsWith("[veo]")) return;
|
|
214
|
+
this.errors.push({ at: Date.now(), source, message });
|
|
215
|
+
const cutoff = Date.now() - this.opts.errorWindowMs - 1e3;
|
|
216
|
+
while (this.errors.length > 0 && this.errors[0].at < cutoff) {
|
|
217
|
+
this.errors.shift();
|
|
218
|
+
}
|
|
219
|
+
if (this.errors.length > 50) this.errors.splice(0, this.errors.length - 50);
|
|
220
|
+
}
|
|
221
|
+
// ---------------------------------------------------------------- rage ----
|
|
222
|
+
remember(element, obs) {
|
|
223
|
+
const list = this.history.get(element) ?? [];
|
|
224
|
+
const cutoff = obs.at - HISTORY_MS;
|
|
225
|
+
const kept = list.filter((o) => o.at >= cutoff);
|
|
226
|
+
kept.push(obs);
|
|
227
|
+
this.history.set(element, kept);
|
|
228
|
+
if (this.history.size > 100) {
|
|
229
|
+
for (const [el, obsList] of this.history) {
|
|
230
|
+
if (obsList.every((o) => o.at < cutoff)) this.history.delete(el);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
trackRage(element, now) {
|
|
235
|
+
const list = this.history.get(element) ?? [];
|
|
236
|
+
const fast = list.filter((o) => o.at >= now - this.opts.rageWindowMs).length;
|
|
237
|
+
const slow = list.filter((o) => o.at >= now - this.opts.rageWindowSlowMs).length;
|
|
238
|
+
const existing = this.bursts.get(element);
|
|
239
|
+
const isRage = fast >= this.opts.rageClicks || slow >= this.opts.rageClicksSlow;
|
|
240
|
+
if (!existing && !isRage) return;
|
|
241
|
+
const burst = existing ?? {
|
|
242
|
+
startedAt: (list.find((o) => o.at >= now - this.opts.rageWindowSlowMs) ?? { at: now }).at,
|
|
243
|
+
count: 0,
|
|
244
|
+
timer: 0
|
|
245
|
+
};
|
|
246
|
+
burst.count = list.filter((o) => o.at >= burst.startedAt).length;
|
|
247
|
+
window.clearTimeout(burst.timer);
|
|
248
|
+
burst.timer = window.setTimeout(() => {
|
|
249
|
+
this.bursts.delete(element);
|
|
250
|
+
const clicks = (this.history.get(element) ?? []).filter((o) => o.at >= burst.startedAt);
|
|
251
|
+
this.report("$rage_click", element, {
|
|
252
|
+
$click_count: Math.max(clicks.length, burst.count),
|
|
253
|
+
$reacted_clicks: clicks.filter((o) => o.reacted).length
|
|
254
|
+
});
|
|
255
|
+
}, this.opts.reactionWindowMs + 50);
|
|
256
|
+
this.bursts.set(element, burst);
|
|
257
|
+
}
|
|
258
|
+
// ---------------------------------------------------------------- emit ----
|
|
259
|
+
report(kind, element, extra) {
|
|
260
|
+
const key = buildSelectorPath(element, 5);
|
|
261
|
+
const counts = this.emitted.get(key) ?? { $dead_click: 0, $rage_click: 0, $click_error: 0 };
|
|
262
|
+
if (counts[kind] >= this.opts.maxPerElement) return;
|
|
263
|
+
counts[kind]++;
|
|
264
|
+
this.emitted.set(key, counts);
|
|
265
|
+
try {
|
|
266
|
+
this.emit(kind, element, extra);
|
|
267
|
+
} catch {
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// ------------------------------------------------------------- patches ----
|
|
271
|
+
patchHistory() {
|
|
272
|
+
const h = window.history;
|
|
273
|
+
if (!h) return;
|
|
274
|
+
const origPush = h.pushState;
|
|
275
|
+
const origReplace = h.replaceState;
|
|
276
|
+
const self = this;
|
|
277
|
+
h.pushState = function(...args) {
|
|
278
|
+
self.navCount++;
|
|
279
|
+
return origPush.apply(this, args);
|
|
280
|
+
};
|
|
281
|
+
h.replaceState = function(...args) {
|
|
282
|
+
self.navCount++;
|
|
283
|
+
return origReplace.apply(this, args);
|
|
284
|
+
};
|
|
285
|
+
this.cleanups.push(() => {
|
|
286
|
+
h.pushState = origPush;
|
|
287
|
+
h.replaceState = origReplace;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
patchConsole() {
|
|
291
|
+
const orig = console.error;
|
|
292
|
+
const self = this;
|
|
293
|
+
console.error = function(...args) {
|
|
294
|
+
try {
|
|
295
|
+
self.pushError("console", args.map(describeError).join(" "));
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
return orig.apply(this, args);
|
|
299
|
+
};
|
|
300
|
+
this.cleanups.push(() => {
|
|
301
|
+
console.error = orig;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* `fetch`: cuenta la petición (reacción) y registra respuestas ≥500 o
|
|
306
|
+
* rechazos como error. Se observa con `.then(ok, err)` sobre la promesa
|
|
307
|
+
* original: eso marca el rechazo como manejado para el navegador, así que
|
|
308
|
+
* un fetch fallido sin catch de la app no dispara `unhandledrejection` —
|
|
309
|
+
* lo registramos nosotros como error de red.
|
|
310
|
+
*/
|
|
311
|
+
patchFetch() {
|
|
312
|
+
if (typeof window.fetch !== "function") return;
|
|
313
|
+
const orig = window.fetch;
|
|
314
|
+
const self = this;
|
|
315
|
+
window.fetch = function(...args) {
|
|
316
|
+
self.netCount++;
|
|
317
|
+
const p = orig.apply(this, args);
|
|
318
|
+
if (p && typeof p.then === "function") {
|
|
319
|
+
p.then(
|
|
320
|
+
(res) => {
|
|
321
|
+
if (res && res.status >= 500) self.pushError("fetch", `${res.status} ${res.url}`);
|
|
322
|
+
},
|
|
323
|
+
(err) => self.pushError("fetch", err)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return p;
|
|
327
|
+
};
|
|
328
|
+
this.cleanups.push(() => {
|
|
329
|
+
window.fetch = orig;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
patchXhr() {
|
|
333
|
+
const proto = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest.prototype : null;
|
|
334
|
+
if (!proto) return;
|
|
335
|
+
const origSend = proto.send;
|
|
336
|
+
const self = this;
|
|
337
|
+
proto.send = function(...args) {
|
|
338
|
+
self.netCount++;
|
|
339
|
+
try {
|
|
340
|
+
this.addEventListener("loadend", () => {
|
|
341
|
+
if (this.status >= 500) self.pushError("xhr", `${this.status} ${this.responseURL}`);
|
|
342
|
+
});
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
return origSend.apply(this, args);
|
|
346
|
+
};
|
|
347
|
+
this.cleanups.push(() => {
|
|
348
|
+
proto.send = origSend;
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
65
353
|
// src/plugins/autocapture/pii-guard.ts
|
|
66
354
|
function isElementBlocked(element, extraBlockSelectors = []) {
|
|
67
355
|
const allBlockers = [...ALWAYS_BLOCK_SELECTORS, ...extraBlockSelectors];
|
|
@@ -165,6 +453,7 @@ var DomListener = class {
|
|
|
165
453
|
constructor(client, config) {
|
|
166
454
|
this.client = client;
|
|
167
455
|
this.cleanups = [];
|
|
456
|
+
this.quality = null;
|
|
168
457
|
const d = DEFAULT_AUTOCAPTURE_CONFIG;
|
|
169
458
|
this.config = {
|
|
170
459
|
enabled: config.enabled ?? d.enabled,
|
|
@@ -177,12 +466,20 @@ var DomListener = class {
|
|
|
177
466
|
throttleMs: config.throttleMs ?? d.throttleMs,
|
|
178
467
|
maxTextLength: config.maxTextLength ?? d.maxTextLength,
|
|
179
468
|
captureElementMetadata: config.captureElementMetadata ?? d.captureElementMetadata,
|
|
180
|
-
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS)
|
|
469
|
+
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS),
|
|
470
|
+
captureClickQuality: config.captureClickQuality ?? d.captureClickQuality
|
|
181
471
|
};
|
|
182
472
|
this.throttle = new ElementThrottle(this.config.throttleMs);
|
|
183
473
|
}
|
|
184
474
|
install() {
|
|
185
475
|
if (!hasDocument() || !this.config.enabled) return;
|
|
476
|
+
if (this.config.captureClicks && this.config.captureClickQuality) {
|
|
477
|
+
this.quality = new ClickQualityTracker((kind, element, extra) => {
|
|
478
|
+
const payload = buildAutocapturePayload("click", element, this.config);
|
|
479
|
+
this.client.track(kind, { ...payload, ...extra });
|
|
480
|
+
});
|
|
481
|
+
this.quality.install();
|
|
482
|
+
}
|
|
186
483
|
if (this.config.captureClicks) this.attach("click", (e) => this.handleClick(e));
|
|
187
484
|
if (this.config.captureSubmits) this.attach("submit", (e) => this.handleSubmit(e), true);
|
|
188
485
|
if (this.config.captureChanges) this.attach("change", (e) => this.handleChange(e), true);
|
|
@@ -190,6 +487,8 @@ var DomListener = class {
|
|
|
190
487
|
destroy() {
|
|
191
488
|
for (const cleanup of this.cleanups) cleanup();
|
|
192
489
|
this.cleanups = [];
|
|
490
|
+
this.quality?.destroy();
|
|
491
|
+
this.quality = null;
|
|
193
492
|
}
|
|
194
493
|
attach(eventName, handler, useCapture = false) {
|
|
195
494
|
const listener = (e) => {
|
|
@@ -208,6 +507,7 @@ var DomListener = class {
|
|
|
208
507
|
const meaningful = this.findMeaningfulAncestor(target);
|
|
209
508
|
if (!meaningful) return;
|
|
210
509
|
if (isElementBlocked(meaningful, this.config.blockSelectors)) return;
|
|
510
|
+
this.quality?.onClick(meaningful, event);
|
|
211
511
|
if (!this.throttle.shouldFire(meaningful)) return;
|
|
212
512
|
this.emit("click", meaningful);
|
|
213
513
|
}
|
|
@@ -704,6 +1004,7 @@ function stableStringify(value) {
|
|
|
704
1004
|
var Client = class {
|
|
705
1005
|
constructor(config) {
|
|
706
1006
|
this.config = config;
|
|
1007
|
+
this.version = SDK_VERSION;
|
|
707
1008
|
this.plugins = [];
|
|
708
1009
|
this.currentOrganizationId = null;
|
|
709
1010
|
this.listeners = {
|
|
@@ -966,7 +1267,7 @@ function init(config) {
|
|
|
966
1267
|
// Fallback de carga del builder para bundlers: `import()` dinámico → el bundler
|
|
967
1268
|
// lo code-splitea (solo se descarga en modo builder). Así el consumidor por
|
|
968
1269
|
// bundler NO necesita copiar `veo-builder.js` ni setear `builderUrl`.
|
|
969
|
-
() => import('./builder-
|
|
1270
|
+
() => import('./builder-5F2EFT4X.mjs').then((m) => m.initBuilderMode()),
|
|
970
1271
|
// Detector de tema: también por `import()` dinámico → chunk aparte que el
|
|
971
1272
|
// bundler solo descarga cuando toca detectar (como mucho una vez al día).
|
|
972
1273
|
() => import('./detect-theme-X4K3TLUL.mjs')
|