@v-tilt/browser 1.0.11 → 1.1.1
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/array.js +1 -1
- package/dist/array.js.map +1 -1
- package/dist/array.no-external.js +1 -1
- package/dist/array.no-external.js.map +1 -1
- package/dist/constants.d.ts +172 -10
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +230 -46
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +230 -46
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/rate-limiter.d.ts +52 -0
- package/dist/request-queue.d.ts +78 -0
- package/dist/request.d.ts +54 -0
- package/dist/retry-queue.d.ts +64 -0
- package/dist/session.d.ts +2 -2
- package/dist/types.d.ts +154 -37
- package/dist/user-manager.d.ts +2 -2
- package/dist/vtilt.d.ts +51 -12
- package/lib/config.js +6 -13
- package/lib/constants.d.ts +172 -10
- package/lib/constants.js +644 -439
- package/lib/rate-limiter.d.ts +52 -0
- package/lib/rate-limiter.js +80 -0
- package/lib/request-queue.d.ts +78 -0
- package/lib/request-queue.js +156 -0
- package/lib/request.d.ts +54 -0
- package/lib/request.js +265 -0
- package/lib/retry-queue.d.ts +64 -0
- package/lib/retry-queue.js +182 -0
- package/lib/session.d.ts +2 -2
- package/lib/session.js +3 -3
- package/lib/types.d.ts +154 -37
- package/lib/types.js +6 -0
- package/lib/user-manager.d.ts +2 -2
- package/lib/user-manager.js +38 -11
- package/lib/utils/event-utils.js +88 -82
- package/lib/utils/index.js +2 -2
- package/lib/utils/request-utils.js +21 -19
- package/lib/vtilt.d.ts +51 -12
- package/lib/vtilt.js +199 -40
- package/lib/web-vitals.js +1 -1
- package/package.json +2 -1
package/lib/vtilt.js
CHANGED
|
@@ -8,18 +8,35 @@ const session_1 = require("./session");
|
|
|
8
8
|
const user_manager_1 = require("./user-manager");
|
|
9
9
|
const web_vitals_1 = require("./web-vitals");
|
|
10
10
|
const history_autocapture_1 = require("./extensions/history-autocapture");
|
|
11
|
+
const request_1 = require("./request");
|
|
12
|
+
const request_queue_1 = require("./request-queue");
|
|
13
|
+
const retry_queue_1 = require("./retry-queue");
|
|
14
|
+
const rate_limiter_1 = require("./rate-limiter");
|
|
11
15
|
const utils_1 = require("./utils");
|
|
12
16
|
const event_utils_1 = require("./utils/event-utils");
|
|
13
17
|
const globals_1 = require("./utils/globals");
|
|
18
|
+
/*
|
|
19
|
+
STYLE GUIDE:
|
|
20
|
+
|
|
21
|
+
Naming conventions:
|
|
22
|
+
- snake_case: Config options and public API methods (e.g., capture_pageview, get_distinct_id)
|
|
23
|
+
- camelCase: Internal variables and class properties (e.g., sessionManager, requestQueue)
|
|
24
|
+
- _snake_case: Private methods that can be mangled/minified (e.g., _init, _dom_loaded)
|
|
25
|
+
- __snake_case: Internal but not minified, signals internal use (e.g., __loaded, __request_queue)
|
|
26
|
+
- UPPER_CASE: Constants and globals (e.g., PRIMARY_INSTANCE_NAME, ENQUEUE_REQUESTS)
|
|
27
|
+
|
|
28
|
+
Use TypeScript accessibility modifiers (private/protected) for non-public members.
|
|
29
|
+
*/
|
|
14
30
|
// Helper to check if value is an array
|
|
15
31
|
const isArray = Array.isArray;
|
|
16
32
|
class VTilt {
|
|
17
33
|
constructor(config = {}) {
|
|
18
34
|
this.__loaded = false; // Matches snippet's window.vt.__loaded check
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.__request_queue = []; //
|
|
22
|
-
this.
|
|
35
|
+
this._initial_pageview_captured = false;
|
|
36
|
+
this._visibility_state_listener = null;
|
|
37
|
+
this.__request_queue = []; // Legacy queue for DOM loaded handler (pre-init)
|
|
38
|
+
this._has_warned_about_config = false; // Track if we've already warned about missing config
|
|
39
|
+
this._set_once_properties_sent = false; // Track if $set_once with initial props has been sent (only send once per page load)
|
|
23
40
|
this.configManager = new config_1.ConfigManager(config);
|
|
24
41
|
const fullConfig = this.configManager.getConfig();
|
|
25
42
|
// Auto-detect domain from location if not provided
|
|
@@ -30,6 +47,27 @@ class VTilt {
|
|
|
30
47
|
this.sessionManager = new session_1.SessionManager(fullConfig.storage || "cookie", domain);
|
|
31
48
|
this.userManager = new user_manager_1.UserManager(fullConfig.persistence || "localStorage", domain);
|
|
32
49
|
this.webVitalsManager = new web_vitals_1.WebVitalsManager(fullConfig, this);
|
|
50
|
+
// Initialize rate limiter to prevent flooding
|
|
51
|
+
// Default: 10 events/second with burst of 100
|
|
52
|
+
this.rateLimiter = new rate_limiter_1.RateLimiter({
|
|
53
|
+
eventsPerSecond: 10,
|
|
54
|
+
eventsBurstLimit: 100,
|
|
55
|
+
captureWarning: (message) => {
|
|
56
|
+
// Send rate limit warning event (bypasses rate limiting)
|
|
57
|
+
this._capture_internal(rate_limiter_1.RATE_LIMIT_WARNING_EVENT, {
|
|
58
|
+
$$client_ingestion_warning_message: message,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
// Initialize retry queue for failed requests
|
|
63
|
+
// Retries with exponential backoff: 3s, 6s, 12s... up to 30 minutes
|
|
64
|
+
this.retryQueue = new retry_queue_1.RetryQueue({
|
|
65
|
+
sendRequest: (req) => this._send_http_request(req),
|
|
66
|
+
sendBeacon: (req) => this._send_beacon_request(req),
|
|
67
|
+
});
|
|
68
|
+
// Initialize request queue for event batching
|
|
69
|
+
// Events are batched and sent every 3 seconds (configurable)
|
|
70
|
+
this.requestQueue = new request_queue_1.RequestQueue((req) => this._send_batched_request(req), { flush_interval_ms: 3000 });
|
|
33
71
|
}
|
|
34
72
|
/**
|
|
35
73
|
* Initializes a new instance of the VTilt tracking object.
|
|
@@ -99,10 +137,46 @@ class VTilt {
|
|
|
99
137
|
// Initialize history autocapture
|
|
100
138
|
this.historyAutocapture = new history_autocapture_1.HistoryAutocapture(this);
|
|
101
139
|
this.historyAutocapture.startIfEnabled();
|
|
140
|
+
// Set up page unload handler to flush queued events
|
|
141
|
+
this._setup_unload_handler();
|
|
142
|
+
// Enable the request queue for batched sending (PostHog pattern)
|
|
143
|
+
this._start_queue_if_opted_in();
|
|
102
144
|
// Capture initial pageview (with visibility check)
|
|
103
|
-
|
|
145
|
+
// Only if capture_pageview is enabled (default: true)
|
|
146
|
+
if (fullConfig.capture_pageview !== false) {
|
|
147
|
+
this._capture_initial_pageview();
|
|
148
|
+
}
|
|
104
149
|
return this;
|
|
105
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Start the request queue if user hasn't opted out
|
|
153
|
+
* Following PostHog's pattern - called from both _init() and _dom_loaded()
|
|
154
|
+
* Safe to call multiple times as enable() is idempotent
|
|
155
|
+
*/
|
|
156
|
+
_start_queue_if_opted_in() {
|
|
157
|
+
// TODO: Add opt-out check when consent management is implemented
|
|
158
|
+
// if (!this.is_capturing()) return;
|
|
159
|
+
// Enable batched sending
|
|
160
|
+
this.requestQueue.enable();
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Set up handler to flush event queue on page unload
|
|
164
|
+
* Uses both beforeunload and pagehide for maximum compatibility
|
|
165
|
+
*/
|
|
166
|
+
_setup_unload_handler() {
|
|
167
|
+
if (!globals_1.window) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const unloadHandler = () => {
|
|
171
|
+
// Flush all queued events using sendBeacon for reliable delivery
|
|
172
|
+
this.requestQueue.unload();
|
|
173
|
+
// Also flush any pending retries
|
|
174
|
+
this.retryQueue.unload();
|
|
175
|
+
};
|
|
176
|
+
// beforeunload is more reliable but pagehide works better on mobile
|
|
177
|
+
(0, utils_1.addEventListener)(globals_1.window, "beforeunload", unloadHandler);
|
|
178
|
+
(0, utils_1.addEventListener)(globals_1.window, "pagehide", unloadHandler);
|
|
179
|
+
}
|
|
106
180
|
/**
|
|
107
181
|
* Returns a string representation of the instance name
|
|
108
182
|
* Used for debugging and logging
|
|
@@ -137,16 +211,16 @@ class VTilt {
|
|
|
137
211
|
* Returns true if projectId and token are present, false otherwise
|
|
138
212
|
* Logs a warning only once per instance if not configured
|
|
139
213
|
*/
|
|
140
|
-
|
|
214
|
+
_is_configured() {
|
|
141
215
|
const config = this.configManager.getConfig();
|
|
142
216
|
if (config.projectId && config.token) {
|
|
143
217
|
return true;
|
|
144
218
|
}
|
|
145
219
|
// Only warn once to avoid console spam
|
|
146
|
-
if (!this.
|
|
220
|
+
if (!this._has_warned_about_config) {
|
|
147
221
|
console.warn("VTilt: projectId and token are required for tracking. " +
|
|
148
222
|
"Events will be skipped until init() or updateConfig() is called with these fields.");
|
|
149
|
-
this.
|
|
223
|
+
this._has_warned_about_config = true;
|
|
150
224
|
}
|
|
151
225
|
return false;
|
|
152
226
|
}
|
|
@@ -155,8 +229,8 @@ class VTilt {
|
|
|
155
229
|
*/
|
|
156
230
|
buildUrl() {
|
|
157
231
|
const config = this.configManager.getConfig();
|
|
158
|
-
const { proxyUrl, proxy,
|
|
159
|
-
// Use proxy endpoint to handle
|
|
232
|
+
const { proxyUrl, proxy, api_host, token } = config;
|
|
233
|
+
// Use proxy endpoint to handle backend authentication
|
|
160
234
|
if (proxyUrl) {
|
|
161
235
|
// Use the full proxy URL as provided
|
|
162
236
|
return proxyUrl;
|
|
@@ -165,8 +239,8 @@ class VTilt {
|
|
|
165
239
|
// Construct the proxy URL from the proxy domain with token
|
|
166
240
|
return `${proxy}/api/tracking?token=${token}`;
|
|
167
241
|
}
|
|
168
|
-
else if (
|
|
169
|
-
const cleanHost =
|
|
242
|
+
else if (api_host) {
|
|
243
|
+
const cleanHost = api_host.replace(/\/+$/gm, "");
|
|
170
244
|
return `${cleanHost}/api/tracking?token=${token}`;
|
|
171
245
|
}
|
|
172
246
|
else {
|
|
@@ -177,11 +251,12 @@ class VTilt {
|
|
|
177
251
|
/**
|
|
178
252
|
* Send HTTP request
|
|
179
253
|
* This is the central entry point for all tracking requests
|
|
254
|
+
* Events are batched and sent every 3 seconds for better performance
|
|
180
255
|
*/
|
|
181
256
|
sendRequest(url, event, shouldEnqueue) {
|
|
182
257
|
// Validate configuration (only warns once per instance)
|
|
183
258
|
// This is the single place where validation happens for all sending methods
|
|
184
|
-
if (!this.
|
|
259
|
+
if (!this._is_configured()) {
|
|
185
260
|
return;
|
|
186
261
|
}
|
|
187
262
|
// Check if we should queue requests (for older browsers before DOM is loaded)
|
|
@@ -193,10 +268,69 @@ class VTilt {
|
|
|
193
268
|
return;
|
|
194
269
|
}
|
|
195
270
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
271
|
+
// Enqueue event for batched sending
|
|
272
|
+
this.requestQueue.enqueue({ url, event });
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Send a batched request with multiple events
|
|
276
|
+
* Called by RequestQueue when flushing
|
|
277
|
+
* Uses RetryQueue for automatic retry on failure
|
|
278
|
+
*/
|
|
279
|
+
_send_batched_request(req) {
|
|
280
|
+
const { transport } = req;
|
|
281
|
+
// Use sendBeacon for reliable delivery on page unload
|
|
282
|
+
if (transport === "sendBeacon") {
|
|
283
|
+
this._send_beacon_request(req);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
// Use retry queue for normal requests (handles failures automatically)
|
|
287
|
+
this.retryQueue.retriableRequest(req);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Send HTTP request and return status code
|
|
291
|
+
* Uses GZip compression for payloads > 1KB
|
|
292
|
+
* Used by RetryQueue for retryable requests
|
|
293
|
+
*/
|
|
294
|
+
_send_http_request(req) {
|
|
295
|
+
return new Promise((resolve) => {
|
|
296
|
+
const { url, events } = req;
|
|
297
|
+
// Prepare payload (single event or batch)
|
|
298
|
+
const payload = events.length === 1 ? events[0] : { events };
|
|
299
|
+
// Determine compression
|
|
300
|
+
const compression = this.configManager.getConfig().disable_compression
|
|
301
|
+
? request_1.Compression.None
|
|
302
|
+
: request_1.Compression.GZipJS;
|
|
303
|
+
(0, request_1.request)({
|
|
304
|
+
url,
|
|
305
|
+
data: payload,
|
|
306
|
+
method: "POST",
|
|
307
|
+
transport: "XHR",
|
|
308
|
+
compression,
|
|
309
|
+
callback: (response) => {
|
|
310
|
+
resolve({ statusCode: response.statusCode });
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Send request using sendBeacon for reliable delivery on page unload
|
|
317
|
+
* Uses GZip compression for payloads > 1KB
|
|
318
|
+
*/
|
|
319
|
+
_send_beacon_request(req) {
|
|
320
|
+
const { url, events } = req;
|
|
321
|
+
// Prepare payload (single event or batch)
|
|
322
|
+
const payload = events.length === 1 ? events[0] : { events };
|
|
323
|
+
// Determine compression
|
|
324
|
+
const compression = this.configManager.getConfig().disable_compression
|
|
325
|
+
? request_1.Compression.None
|
|
326
|
+
: request_1.Compression.GZipJS;
|
|
327
|
+
(0, request_1.request)({
|
|
328
|
+
url,
|
|
329
|
+
data: payload,
|
|
330
|
+
method: "POST",
|
|
331
|
+
transport: "sendBeacon",
|
|
332
|
+
compression,
|
|
333
|
+
});
|
|
200
334
|
}
|
|
201
335
|
/**
|
|
202
336
|
* Send a queued request (called after DOM is loaded)
|
|
@@ -213,8 +347,9 @@ class VTilt {
|
|
|
213
347
|
*
|
|
214
348
|
* @param name - Event name
|
|
215
349
|
* @param payload - Event payload
|
|
350
|
+
* @param options - Optional capture options
|
|
216
351
|
*/
|
|
217
|
-
capture(name, payload) {
|
|
352
|
+
capture(name, payload, options) {
|
|
218
353
|
this.sessionManager.setSessionId();
|
|
219
354
|
// Only send events in browser environment (not SSR)
|
|
220
355
|
if (!globals_1.navigator || !globals_1.navigator.userAgent) {
|
|
@@ -223,6 +358,12 @@ class VTilt {
|
|
|
223
358
|
if (!(0, utils_1.isValidUserAgent)(globals_1.navigator.userAgent)) {
|
|
224
359
|
return;
|
|
225
360
|
}
|
|
361
|
+
// Check rate limiting (unless explicitly skipped)
|
|
362
|
+
if (!(options === null || options === void 0 ? void 0 : options.skip_client_rate_limiting) &&
|
|
363
|
+
!this.rateLimiter.shouldAllowEvent()) {
|
|
364
|
+
// Event is rate limited - drop it silently
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
226
367
|
const url = this.buildUrl();
|
|
227
368
|
// Add properties to all events
|
|
228
369
|
// This includes: $current_url, $host, $pathname, $referrer, $referring_domain, $browser, $os, $device, $timezone, etc.
|
|
@@ -241,14 +382,16 @@ class VTilt {
|
|
|
241
382
|
// This allows linking events with different distinct_ids that share the same anonymous_id
|
|
242
383
|
// This is especially important for handling race conditions when $identify events arrive
|
|
243
384
|
const anonymousId = this.userManager.getAnonymousId();
|
|
244
|
-
// Build $
|
|
245
|
-
//
|
|
246
|
-
//
|
|
385
|
+
// Build $set_once from initial props (only on first event per page load)
|
|
386
|
+
// This optimization follows PostHog's pattern: initial props only need to be sent once
|
|
387
|
+
// since $set_once preserves first values. Sending them on every event would be redundant
|
|
388
|
+
// and cause unnecessary server processing for identity creation/updates.
|
|
247
389
|
const setOnce = {};
|
|
248
|
-
if
|
|
390
|
+
// Only include initial props if we haven't sent them yet this page load
|
|
391
|
+
if (!this._set_once_properties_sent && Object.keys(initialProps).length > 0) {
|
|
249
392
|
Object.assign(setOnce, initialProps);
|
|
250
393
|
}
|
|
251
|
-
//
|
|
394
|
+
// Always merge with user-provided $set_once if present
|
|
252
395
|
if (payload.$set_once) {
|
|
253
396
|
Object.assign(setOnce, payload.$set_once);
|
|
254
397
|
}
|
|
@@ -260,12 +403,16 @@ class VTilt {
|
|
|
260
403
|
// Always include $anon_distinct_id for identity linking (even for identified users)
|
|
261
404
|
// This allows the server to merge identities proactively when events arrive out of order
|
|
262
405
|
...(anonymousId ? { $anon_distinct_id: anonymousId } : {}),
|
|
263
|
-
// Add $set_once with initial props
|
|
406
|
+
// Add $set_once with initial props (only if there are properties to set)
|
|
264
407
|
...(Object.keys(setOnce).length > 0 ? { $set_once: setOnce } : {}),
|
|
265
408
|
// Merge user-provided $set if present
|
|
266
409
|
...(payload.$set ? { $set: payload.$set } : {}),
|
|
267
410
|
...payload, // User-provided payload (can override base and person properties)
|
|
268
411
|
};
|
|
412
|
+
// Mark that $set_once with initial props has been sent (only do this once per page load)
|
|
413
|
+
if (!this._set_once_properties_sent && Object.keys(initialProps).length > 0) {
|
|
414
|
+
this._set_once_properties_sent = true;
|
|
415
|
+
}
|
|
269
416
|
// Add title only to $pageview events
|
|
270
417
|
if (name === "$pageview" && globals_1.document) {
|
|
271
418
|
enrichedPayload.title = globals_1.document.title;
|
|
@@ -313,6 +460,13 @@ class VTilt {
|
|
|
313
460
|
};
|
|
314
461
|
this.sendRequest(url, trackingEvent, true);
|
|
315
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* Internal capture method that bypasses rate limiting
|
|
465
|
+
* Used for system events like rate limit warnings
|
|
466
|
+
*/
|
|
467
|
+
_capture_internal(name, payload) {
|
|
468
|
+
this.capture(name, payload, { skip_client_rate_limiting: true });
|
|
469
|
+
}
|
|
316
470
|
/**
|
|
317
471
|
* Track a custom event (alias for capture)
|
|
318
472
|
*/
|
|
@@ -380,7 +534,7 @@ class VTilt {
|
|
|
380
534
|
// New distinct_id and properties go in payload
|
|
381
535
|
this.capture("$identify", {
|
|
382
536
|
distinct_id: newDistinctId, // New distinct_id in payload
|
|
383
|
-
$anon_distinct_id:
|
|
537
|
+
$anon_distinct_id: anonymousId, // Previous ID in payload
|
|
384
538
|
$set: userPropertiesToSet || {},
|
|
385
539
|
$set_once: userPropertiesToSetOnce || {},
|
|
386
540
|
});
|
|
@@ -515,8 +669,9 @@ class VTilt {
|
|
|
515
669
|
}
|
|
516
670
|
/**
|
|
517
671
|
* Capture initial pageview with visibility check
|
|
672
|
+
* Note: The capture_pageview config check happens at the call site (in _init)
|
|
518
673
|
*/
|
|
519
|
-
|
|
674
|
+
_capture_initial_pageview() {
|
|
520
675
|
if (!globals_1.document) {
|
|
521
676
|
return;
|
|
522
677
|
}
|
|
@@ -525,17 +680,17 @@ class VTilt {
|
|
|
525
680
|
// This is useful to avoid `prerender` calls from Chrome/Wordpress/SPAs
|
|
526
681
|
// that are not visible to the user
|
|
527
682
|
if (globals_1.document.visibilityState !== "visible") {
|
|
528
|
-
if (!this.
|
|
529
|
-
this.
|
|
530
|
-
this.
|
|
683
|
+
if (!this._visibility_state_listener) {
|
|
684
|
+
this._visibility_state_listener = () => {
|
|
685
|
+
this._capture_initial_pageview();
|
|
531
686
|
};
|
|
532
|
-
(0, utils_1.addEventListener)(globals_1.document, "visibilitychange", this.
|
|
687
|
+
(0, utils_1.addEventListener)(globals_1.document, "visibilitychange", this._visibility_state_listener);
|
|
533
688
|
}
|
|
534
689
|
return;
|
|
535
690
|
}
|
|
536
691
|
// Extra check here to guarantee we only ever trigger a single initial pageview event
|
|
537
|
-
if (!this.
|
|
538
|
-
this.
|
|
692
|
+
if (!this._initial_pageview_captured) {
|
|
693
|
+
this._initial_pageview_captured = true;
|
|
539
694
|
// Wait a bit for SPA routers
|
|
540
695
|
setTimeout(() => {
|
|
541
696
|
// Double-check we're still in browser environment (defensive check)
|
|
@@ -550,9 +705,9 @@ class VTilt {
|
|
|
550
705
|
this.capture("$pageview", payload);
|
|
551
706
|
}, 300);
|
|
552
707
|
// After we've captured the initial pageview, we can remove the listener
|
|
553
|
-
if (this.
|
|
554
|
-
globals_1.document.removeEventListener("visibilitychange", this.
|
|
555
|
-
this.
|
|
708
|
+
if (this._visibility_state_listener) {
|
|
709
|
+
globals_1.document.removeEventListener("visibilitychange", this._visibility_state_listener);
|
|
710
|
+
this._visibility_state_listener = null;
|
|
556
711
|
}
|
|
557
712
|
}
|
|
558
713
|
}
|
|
@@ -614,15 +769,18 @@ class VTilt {
|
|
|
614
769
|
});
|
|
615
770
|
}
|
|
616
771
|
/**
|
|
617
|
-
* Called when DOM is loaded - processes queued requests
|
|
772
|
+
* Called when DOM is loaded - processes queued requests and enables batching
|
|
773
|
+
* Following PostHog's pattern in _dom_loaded()
|
|
618
774
|
*/
|
|
619
775
|
_dom_loaded() {
|
|
620
|
-
// Process all queued requests
|
|
776
|
+
// Process all pre-init queued requests
|
|
621
777
|
this.__request_queue.forEach((item) => {
|
|
622
778
|
this._send_retriable_request(item);
|
|
623
779
|
});
|
|
624
|
-
// Clear the queue
|
|
780
|
+
// Clear the legacy queue
|
|
625
781
|
this.__request_queue = [];
|
|
782
|
+
// Enable the request queue for batched sending (PostHog pattern)
|
|
783
|
+
this._start_queue_if_opted_in();
|
|
626
784
|
}
|
|
627
785
|
}
|
|
628
786
|
exports.VTilt = VTilt;
|
|
@@ -637,7 +795,8 @@ let ENQUEUE_REQUESTS = !SUPPORTS_REQUEST &&
|
|
|
637
795
|
(globals_1.userAgent === null || globals_1.userAgent === void 0 ? void 0 : globals_1.userAgent.indexOf("MSIE")) === -1 &&
|
|
638
796
|
(globals_1.userAgent === null || globals_1.userAgent === void 0 ? void 0 : globals_1.userAgent.indexOf("Mozilla")) === -1;
|
|
639
797
|
// Expose ENQUEUE_REQUESTS to window for request queuing
|
|
640
|
-
|
|
798
|
+
// Use typeof check to safely access global window object
|
|
799
|
+
if (typeof globals_1.window !== "undefined" && globals_1.window !== null) {
|
|
641
800
|
globals_1.window.__VTILT_ENQUEUE_REQUESTS = ENQUEUE_REQUESTS;
|
|
642
801
|
}
|
|
643
802
|
/**
|
|
@@ -653,7 +812,7 @@ const add_dom_loaded_handler = function () {
|
|
|
653
812
|
dom_loaded_handler.done = true;
|
|
654
813
|
// Disable request queuing now that DOM is loaded
|
|
655
814
|
ENQUEUE_REQUESTS = false;
|
|
656
|
-
if (typeof globals_1.window !== "undefined") {
|
|
815
|
+
if (typeof globals_1.window !== "undefined" && globals_1.window !== null) {
|
|
657
816
|
globals_1.window.__VTILT_ENQUEUE_REQUESTS = false;
|
|
658
817
|
}
|
|
659
818
|
// Process queued requests for all instances
|
package/lib/web-vitals.js
CHANGED
|
@@ -7,7 +7,7 @@ class WebVitalsManager {
|
|
|
7
7
|
constructor(config, instance) {
|
|
8
8
|
this.instance = instance;
|
|
9
9
|
// Load web-vitals if enabled and in browser environment (not SSR)
|
|
10
|
-
if (config.
|
|
10
|
+
if (config.capture_performance && globals_1.window) {
|
|
11
11
|
try {
|
|
12
12
|
// Dynamically require web-vitals, ignore type error since it's a runtime import
|
|
13
13
|
// eslint-disable-next-line
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@v-tilt/browser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "vTilt browser tracking library",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"module": "dist/module.js",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"typescript": "^5.3.3"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"fflate": "^0.8.2",
|
|
51
52
|
"web-vitals": "^3.5.0"
|
|
52
53
|
},
|
|
53
54
|
"peerDependencies": {
|