@v-tilt/browser 1.0.8 → 1.0.10
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 +0 -1
- package/dist/extensions/history-autocapture.d.ts +0 -2
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +53 -133
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +53 -133
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/session.d.ts +5 -5
- package/dist/types.d.ts +2 -1
- package/dist/user-manager.d.ts +30 -20
- package/dist/utils/event-utils.d.ts +7 -8
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/patch.d.ts +0 -1
- package/dist/vtilt.d.ts +54 -14
- package/dist/web-vitals.d.ts +3 -3
- package/lib/constants.d.ts +0 -1
- package/lib/constants.js +1 -23
- package/lib/extensions/history-autocapture.d.ts +0 -2
- package/lib/extensions/history-autocapture.js +3 -5
- package/lib/session.d.ts +5 -5
- package/lib/session.js +8 -8
- package/lib/types.d.ts +2 -1
- package/lib/user-manager.d.ts +30 -20
- package/lib/user-manager.js +103 -94
- package/lib/utils/event-utils.d.ts +7 -8
- package/lib/utils/event-utils.js +8 -9
- package/lib/utils/index.d.ts +0 -5
- package/lib/utils/index.js +0 -13
- package/lib/utils/patch.d.ts +0 -1
- package/lib/utils/patch.js +0 -1
- package/lib/vtilt.d.ts +54 -14
- package/lib/vtilt.js +323 -41
- package/lib/web-vitals.d.ts +3 -3
- package/lib/web-vitals.js +3 -3
- package/package.json +1 -1
- package/dist/tracking.d.ts +0 -120
- package/dist/utils/is-function.d.ts +0 -4
- package/lib/tracking.d.ts +0 -120
- package/lib/tracking.js +0 -338
- package/lib/utils/is-function.d.ts +0 -4
- package/lib/utils/is-function.js +0 -9
package/lib/vtilt.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { VTiltConfig, EventPayload } from "./types";
|
|
2
|
-
import { TrackingManager } from "./tracking";
|
|
3
2
|
import { HistoryAutocapture } from "./extensions/history-autocapture";
|
|
3
|
+
interface QueuedRequest {
|
|
4
|
+
url: string;
|
|
5
|
+
event: any;
|
|
6
|
+
}
|
|
4
7
|
export declare class VTilt {
|
|
5
8
|
private configManager;
|
|
6
|
-
|
|
9
|
+
private sessionManager;
|
|
10
|
+
private userManager;
|
|
7
11
|
private webVitalsManager;
|
|
8
12
|
historyAutocapture?: HistoryAutocapture;
|
|
9
13
|
__loaded: boolean;
|
|
10
14
|
private _initialPageviewCaptured;
|
|
11
15
|
private _visibilityStateListener;
|
|
16
|
+
__request_queue: QueuedRequest[];
|
|
17
|
+
private _hasWarnedAboutConfig;
|
|
12
18
|
constructor(config?: Partial<VTiltConfig>);
|
|
13
19
|
/**
|
|
14
20
|
* Initializes a new instance of the VTilt tracking object.
|
|
@@ -44,23 +50,57 @@ export declare class VTilt {
|
|
|
44
50
|
/**
|
|
45
51
|
* Handles the actual initialization logic for a VTilt instance.
|
|
46
52
|
* This internal method should only be called by `init()`.
|
|
47
|
-
* Follows the PostHog convention of using a private `_init()` method for instance setup.
|
|
48
53
|
*/
|
|
49
54
|
private _init;
|
|
50
55
|
/**
|
|
51
|
-
* Returns a string representation of the instance name
|
|
56
|
+
* Returns a string representation of the instance name
|
|
52
57
|
* Used for debugging and logging
|
|
53
58
|
*
|
|
54
59
|
* @internal
|
|
55
60
|
*/
|
|
56
61
|
toString(): string;
|
|
57
62
|
/**
|
|
58
|
-
*
|
|
63
|
+
* Get current domain from location
|
|
64
|
+
* Returns full URL format for consistency with dashboard
|
|
65
|
+
*/
|
|
66
|
+
private getCurrentDomain;
|
|
67
|
+
/**
|
|
68
|
+
* Check if tracking is properly configured
|
|
69
|
+
* Returns true if projectId and token are present, false otherwise
|
|
70
|
+
* Logs a warning only once per instance if not configured
|
|
71
|
+
*/
|
|
72
|
+
private _isConfigured;
|
|
73
|
+
/**
|
|
74
|
+
* Build the tracking URL with token in query parameters
|
|
75
|
+
*/
|
|
76
|
+
private buildUrl;
|
|
77
|
+
/**
|
|
78
|
+
* Send HTTP request
|
|
79
|
+
* This is the central entry point for all tracking requests
|
|
80
|
+
*/
|
|
81
|
+
private sendRequest;
|
|
82
|
+
/**
|
|
83
|
+
* Send a queued request (called after DOM is loaded)
|
|
84
|
+
*/
|
|
85
|
+
_send_retriable_request(item: QueuedRequest): void;
|
|
86
|
+
/**
|
|
87
|
+
* Capture an event
|
|
88
|
+
* Automatically adds common properties to all events
|
|
89
|
+
* ($current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.)
|
|
90
|
+
* Also adds title property for $pageview events only
|
|
91
|
+
*
|
|
92
|
+
* @param name - Event name
|
|
93
|
+
* @param payload - Event payload
|
|
94
|
+
*/
|
|
95
|
+
capture(name: string, payload: EventPayload): void;
|
|
96
|
+
/**
|
|
97
|
+
* Track a custom event (alias for capture)
|
|
59
98
|
*/
|
|
60
99
|
trackEvent(name: string, payload?: EventPayload): void;
|
|
61
100
|
/**
|
|
62
|
-
* Identify a user with
|
|
63
|
-
*
|
|
101
|
+
* Identify a user with property operations
|
|
102
|
+
* Sends $identify event when transitioning from anonymous to identified
|
|
103
|
+
* Event's distinct_id is the previous/anonymous ID, new distinct_id is in payload
|
|
64
104
|
*
|
|
65
105
|
* @example
|
|
66
106
|
* ```js
|
|
@@ -77,9 +117,9 @@ export declare class VTilt {
|
|
|
77
117
|
* vTilt.identify('user_123', { name: 'John Doe', email: 'john@example.com' })
|
|
78
118
|
* ```
|
|
79
119
|
*/
|
|
80
|
-
identify(
|
|
120
|
+
identify(newDistinctId?: string, userPropertiesToSet?: Record<string, any>, userPropertiesToSetOnce?: Record<string, any>): void;
|
|
81
121
|
/**
|
|
82
|
-
* Set user properties
|
|
122
|
+
* Set user properties
|
|
83
123
|
* Sets properties on the person profile associated with the current distinct_id
|
|
84
124
|
*
|
|
85
125
|
* @example
|
|
@@ -103,7 +143,7 @@ export declare class VTilt {
|
|
|
103
143
|
setUserProperties(userPropertiesToSet?: Record<string, any>, userPropertiesToSetOnce?: Record<string, any>): void;
|
|
104
144
|
/**
|
|
105
145
|
* Reset user identity (logout)
|
|
106
|
-
*
|
|
146
|
+
* Clears all user data, generates new anonymous ID, resets session
|
|
107
147
|
*
|
|
108
148
|
* @param reset_device_id - If true, also resets device_id. Default: false
|
|
109
149
|
*
|
|
@@ -130,7 +170,7 @@ export declare class VTilt {
|
|
|
130
170
|
getUserState(): "anonymous" | "identified";
|
|
131
171
|
/**
|
|
132
172
|
* Create an alias to link two distinct IDs
|
|
133
|
-
*
|
|
173
|
+
* Links anonymous session to account on signup
|
|
134
174
|
*
|
|
135
175
|
* @param alias - A unique identifier that you want to use for this user in the future
|
|
136
176
|
* @param original - The current identifier being used for this user (optional, defaults to current distinct_id)
|
|
@@ -146,7 +186,6 @@ export declare class VTilt {
|
|
|
146
186
|
createAlias(alias: string, original?: string): void;
|
|
147
187
|
/**
|
|
148
188
|
* Capture initial pageview with visibility check
|
|
149
|
-
* Based on PostHog's _captureInitialPageview implementation
|
|
150
189
|
*/
|
|
151
190
|
private _captureInitialPageview;
|
|
152
191
|
/**
|
|
@@ -179,12 +218,12 @@ export declare class VTilt {
|
|
|
179
218
|
_dom_loaded(): void;
|
|
180
219
|
}
|
|
181
220
|
/**
|
|
182
|
-
* Initialize vTilt as a module
|
|
221
|
+
* Initialize vTilt as a module
|
|
183
222
|
* Returns an uninitialized vTilt instance that the user must call init() on
|
|
184
223
|
*/
|
|
185
224
|
export declare function init_as_module(): VTilt;
|
|
186
225
|
/**
|
|
187
|
-
* Initialize vTilt from snippet
|
|
226
|
+
* Initialize vTilt from snippet
|
|
188
227
|
* Processes queued calls from the snippet stub and replaces it with real instance
|
|
189
228
|
*
|
|
190
229
|
* The snippet uses some clever tricks to allow deferred loading of array.js (this code)
|
|
@@ -214,3 +253,4 @@ export declare function init_as_module(): VTilt;
|
|
|
214
253
|
* ]
|
|
215
254
|
*/
|
|
216
255
|
export declare function init_from_snippet(): void;
|
|
256
|
+
export {};
|
package/lib/vtilt.js
CHANGED
|
@@ -4,10 +4,12 @@ exports.VTilt = void 0;
|
|
|
4
4
|
exports.init_as_module = init_as_module;
|
|
5
5
|
exports.init_from_snippet = init_from_snippet;
|
|
6
6
|
const config_1 = require("./config");
|
|
7
|
-
const
|
|
7
|
+
const session_1 = require("./session");
|
|
8
|
+
const user_manager_1 = require("./user-manager");
|
|
8
9
|
const web_vitals_1 = require("./web-vitals");
|
|
9
10
|
const history_autocapture_1 = require("./extensions/history-autocapture");
|
|
10
11
|
const utils_1 = require("./utils");
|
|
12
|
+
const event_utils_1 = require("./utils/event-utils");
|
|
11
13
|
const globals_1 = require("./utils/globals");
|
|
12
14
|
// Helper to check if value is an array
|
|
13
15
|
const isArray = Array.isArray;
|
|
@@ -16,9 +18,18 @@ class VTilt {
|
|
|
16
18
|
this.__loaded = false; // Matches snippet's window.vt.__loaded check
|
|
17
19
|
this._initialPageviewCaptured = false;
|
|
18
20
|
this._visibilityStateListener = null;
|
|
21
|
+
this.__request_queue = []; // Public for DOM loaded handler
|
|
22
|
+
this._hasWarnedAboutConfig = false; // Track if we've already warned about missing config
|
|
19
23
|
this.configManager = new config_1.ConfigManager(config);
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
const fullConfig = this.configManager.getConfig();
|
|
25
|
+
// Auto-detect domain from location if not provided
|
|
26
|
+
let domain = fullConfig.domain;
|
|
27
|
+
if (!domain && globals_1.location) {
|
|
28
|
+
domain = this.getCurrentDomain();
|
|
29
|
+
}
|
|
30
|
+
this.sessionManager = new session_1.SessionManager(fullConfig.storage || "cookie", domain);
|
|
31
|
+
this.userManager = new user_manager_1.UserManager(fullConfig.persistence || "localStorage", domain);
|
|
32
|
+
this.webVitalsManager = new web_vitals_1.WebVitalsManager(fullConfig, this);
|
|
22
33
|
}
|
|
23
34
|
/**
|
|
24
35
|
* Initializes a new instance of the VTilt tracking object.
|
|
@@ -68,7 +79,6 @@ class VTilt {
|
|
|
68
79
|
/**
|
|
69
80
|
* Handles the actual initialization logic for a VTilt instance.
|
|
70
81
|
* This internal method should only be called by `init()`.
|
|
71
|
-
* Follows the PostHog convention of using a private `_init()` method for instance setup.
|
|
72
82
|
*/
|
|
73
83
|
_init(projectId, config = {}, name) {
|
|
74
84
|
// Guard: prevent re-initialization (matches snippet's __loaded check)
|
|
@@ -76,14 +86,14 @@ class VTilt {
|
|
|
76
86
|
console.warn("vTilt: You have already initialized vTilt! Re-initializing is a no-op");
|
|
77
87
|
return this;
|
|
78
88
|
}
|
|
79
|
-
// Update config with projectId, token, and name
|
|
89
|
+
// Update config with projectId, token, and name
|
|
80
90
|
this.updateConfig({
|
|
81
91
|
...config,
|
|
82
92
|
projectId: projectId || config.projectId,
|
|
83
93
|
name: name,
|
|
84
94
|
});
|
|
85
95
|
this.__loaded = true;
|
|
86
|
-
// Initialize history autocapture
|
|
96
|
+
// Initialize history autocapture
|
|
87
97
|
this.historyAutocapture = new history_autocapture_1.HistoryAutocapture(this);
|
|
88
98
|
this.historyAutocapture.startIfEnabled();
|
|
89
99
|
// Capture initial pageview (with visibility check)
|
|
@@ -91,7 +101,7 @@ class VTilt {
|
|
|
91
101
|
return this;
|
|
92
102
|
}
|
|
93
103
|
/**
|
|
94
|
-
* Returns a string representation of the instance name
|
|
104
|
+
* Returns a string representation of the instance name
|
|
95
105
|
* Used for debugging and logging
|
|
96
106
|
*
|
|
97
107
|
* @internal
|
|
@@ -106,14 +116,191 @@ class VTilt {
|
|
|
106
116
|
return name;
|
|
107
117
|
}
|
|
108
118
|
/**
|
|
109
|
-
*
|
|
119
|
+
* Get current domain from location
|
|
120
|
+
* Returns full URL format for consistency with dashboard
|
|
121
|
+
*/
|
|
122
|
+
getCurrentDomain() {
|
|
123
|
+
if (!globals_1.location) {
|
|
124
|
+
return "";
|
|
125
|
+
}
|
|
126
|
+
const protocol = globals_1.location.protocol;
|
|
127
|
+
const hostname = globals_1.location.hostname;
|
|
128
|
+
const port = globals_1.location.port;
|
|
129
|
+
const portSuffix = port ? `:${port}` : "";
|
|
130
|
+
return `${protocol}//${hostname}${portSuffix}`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if tracking is properly configured
|
|
134
|
+
* Returns true if projectId and token are present, false otherwise
|
|
135
|
+
* Logs a warning only once per instance if not configured
|
|
136
|
+
*/
|
|
137
|
+
_isConfigured() {
|
|
138
|
+
const config = this.configManager.getConfig();
|
|
139
|
+
if (config.projectId && config.token) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
// Only warn once to avoid console spam
|
|
143
|
+
if (!this._hasWarnedAboutConfig) {
|
|
144
|
+
console.warn("VTilt: projectId and token are required for tracking. " +
|
|
145
|
+
"Events will be skipped until init() or updateConfig() is called with these fields.");
|
|
146
|
+
this._hasWarnedAboutConfig = true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Build the tracking URL with token in query parameters
|
|
152
|
+
*/
|
|
153
|
+
buildUrl() {
|
|
154
|
+
const config = this.configManager.getConfig();
|
|
155
|
+
const { proxyUrl, proxy, host, token } = config;
|
|
156
|
+
// Use proxy endpoint to handle Tinybird authentication
|
|
157
|
+
if (proxyUrl) {
|
|
158
|
+
// Use the full proxy URL as provided
|
|
159
|
+
return proxyUrl;
|
|
160
|
+
}
|
|
161
|
+
else if (proxy) {
|
|
162
|
+
// Construct the proxy URL from the proxy domain with token
|
|
163
|
+
return `${proxy}/api/tracking?token=${token}`;
|
|
164
|
+
}
|
|
165
|
+
else if (host) {
|
|
166
|
+
const cleanHost = host.replace(/\/+$/gm, "");
|
|
167
|
+
return `${cleanHost}/api/tracking?token=${token}`;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Use relative path to our tracking proxy endpoint
|
|
171
|
+
return `/api/tracking?token=${token}`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Send HTTP request
|
|
176
|
+
* This is the central entry point for all tracking requests
|
|
177
|
+
*/
|
|
178
|
+
sendRequest(url, event, shouldEnqueue) {
|
|
179
|
+
// Validate configuration (only warns once per instance)
|
|
180
|
+
// This is the single place where validation happens for all sending methods
|
|
181
|
+
if (!this._isConfigured()) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// Check if we should queue requests (for older browsers before DOM is loaded)
|
|
185
|
+
if (shouldEnqueue && typeof globals_1.window !== "undefined") {
|
|
186
|
+
// ENQUEUE_REQUESTS is defined in vtilt.ts as a module-level variable
|
|
187
|
+
const ENQUEUE_REQUESTS = globals_1.window.__VTILT_ENQUEUE_REQUESTS;
|
|
188
|
+
if (ENQUEUE_REQUESTS) {
|
|
189
|
+
this.__request_queue.push({ url, event });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const request = new XMLHttpRequest();
|
|
194
|
+
request.open("POST", url, true);
|
|
195
|
+
request.setRequestHeader("Content-Type", "application/json");
|
|
196
|
+
request.send(JSON.stringify(event));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Send a queued request (called after DOM is loaded)
|
|
200
|
+
*/
|
|
201
|
+
_send_retriable_request(item) {
|
|
202
|
+
this.sendRequest(item.url, item.event, false);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Capture an event
|
|
206
|
+
* Automatically adds common properties to all events
|
|
207
|
+
* ($current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.)
|
|
208
|
+
* Also adds title property for $pageview events only
|
|
209
|
+
*
|
|
210
|
+
* @param name - Event name
|
|
211
|
+
* @param payload - Event payload
|
|
212
|
+
*/
|
|
213
|
+
capture(name, payload) {
|
|
214
|
+
this.sessionManager.setSessionId();
|
|
215
|
+
// Only send events in browser environment (not SSR)
|
|
216
|
+
if (!globals_1.navigator || !globals_1.navigator.userAgent) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (!(0, utils_1.isValidUserAgent)(globals_1.navigator.userAgent)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const url = this.buildUrl();
|
|
223
|
+
// Add properties to all events
|
|
224
|
+
// This includes: $current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.
|
|
225
|
+
const eventProperties = (0, event_utils_1.getEventProperties)();
|
|
226
|
+
// Get person properties (includes $device_id and other user properties)
|
|
227
|
+
// These are automatically included in all events
|
|
228
|
+
const personProperties = this.userManager.getUserProperties();
|
|
229
|
+
// Get session and window IDs
|
|
230
|
+
// Both methods ensure IDs always exist (generate if needed)
|
|
231
|
+
const session_id = this.sessionManager.getSessionId();
|
|
232
|
+
const window_id = this.sessionManager.getWindowId();
|
|
233
|
+
// Always include anonymous_id in properties for identity linking
|
|
234
|
+
// This allows linking events with different distinct_ids that share the same anonymous_id
|
|
235
|
+
// This is especially important for handling race conditions when $identify events arrive
|
|
236
|
+
const anonymousId = this.userManager.getAnonymousId();
|
|
237
|
+
const enrichedPayload = {
|
|
238
|
+
...eventProperties, // Base properties for all events
|
|
239
|
+
...personProperties, // Person properties (includes $device_id)
|
|
240
|
+
$session_id: session_id, // Session ID in properties
|
|
241
|
+
$window_id: window_id, // Window ID in properties
|
|
242
|
+
// Always include $anon_distinct_id for identity linking (even for identified users)
|
|
243
|
+
// This allows the server to merge identities proactively when events arrive out of order
|
|
244
|
+
...(anonymousId ? { $anon_distinct_id: anonymousId } : {}),
|
|
245
|
+
...payload, // User-provided payload (can override base and person properties)
|
|
246
|
+
};
|
|
247
|
+
// Add title only to $pageview events
|
|
248
|
+
if (name === "$pageview" && globals_1.document) {
|
|
249
|
+
enrichedPayload.title = globals_1.document.title;
|
|
250
|
+
}
|
|
251
|
+
const config = this.configManager.getConfig();
|
|
252
|
+
let processedPayload;
|
|
253
|
+
if (config.stringifyPayload !== false) {
|
|
254
|
+
processedPayload = Object.assign({}, enrichedPayload, config.globalAttributes);
|
|
255
|
+
processedPayload = JSON.stringify(processedPayload);
|
|
256
|
+
if (!(0, utils_1.isValidPayload)(processedPayload)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
processedPayload = Object.assign({}, enrichedPayload, config.globalAttributes);
|
|
262
|
+
const payloadStr = JSON.stringify(processedPayload);
|
|
263
|
+
if (!(0, utils_1.isValidPayload)(payloadStr)) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Use current distinct_id (or anonymous_id) for event's distinct_id
|
|
268
|
+
// For $identify events, use anonymous_id as the event's distinct_id (identifying FROM anonymous TO new ID)
|
|
269
|
+
// New distinct_id should be in payload properties (e.g., for $identify events)
|
|
270
|
+
let distinct_id;
|
|
271
|
+
if (name === "$identify") {
|
|
272
|
+
// For $identify events, always use anonymous_id as the event's distinct_id
|
|
273
|
+
// The new distinct_id is in the payload
|
|
274
|
+
distinct_id = this.userManager.getAnonymousId();
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
// For other events, use current distinct_id or fall back to anonymous_id
|
|
278
|
+
distinct_id =
|
|
279
|
+
this.userManager.getDistinctId() || this.userManager.getAnonymousId();
|
|
280
|
+
}
|
|
281
|
+
const trackingEvent = {
|
|
282
|
+
timestamp: new Date().toISOString(),
|
|
283
|
+
event: name,
|
|
284
|
+
project_id: config.projectId || "",
|
|
285
|
+
domain: config.domain || this.getCurrentDomain(), // Use config domain or current domain
|
|
286
|
+
payload: processedPayload,
|
|
287
|
+
distinct_id: distinct_id,
|
|
288
|
+
// Always include anonymous_id in the event for identity linking
|
|
289
|
+
// This allows the server to merge identities proactively
|
|
290
|
+
anonymous_id: anonymousId,
|
|
291
|
+
};
|
|
292
|
+
this.sendRequest(url, trackingEvent, true);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Track a custom event (alias for capture)
|
|
110
296
|
*/
|
|
111
297
|
trackEvent(name, payload = {}) {
|
|
112
|
-
this.
|
|
298
|
+
this.capture(name, payload);
|
|
113
299
|
}
|
|
114
300
|
/**
|
|
115
|
-
* Identify a user with
|
|
116
|
-
*
|
|
301
|
+
* Identify a user with property operations
|
|
302
|
+
* Sends $identify event when transitioning from anonymous to identified
|
|
303
|
+
* Event's distinct_id is the previous/anonymous ID, new distinct_id is in payload
|
|
117
304
|
*
|
|
118
305
|
* @example
|
|
119
306
|
* ```js
|
|
@@ -130,11 +317,76 @@ class VTilt {
|
|
|
130
317
|
* vTilt.identify('user_123', { name: 'John Doe', email: 'john@example.com' })
|
|
131
318
|
* ```
|
|
132
319
|
*/
|
|
133
|
-
identify(
|
|
134
|
-
|
|
320
|
+
identify(newDistinctId, userPropertiesToSet, userPropertiesToSetOnce) {
|
|
321
|
+
// Validation: Convert number to string
|
|
322
|
+
if (typeof newDistinctId === "number") {
|
|
323
|
+
newDistinctId = String(newDistinctId);
|
|
324
|
+
console.warn("The first argument to vTilt.identify was a number, but it should be a string. It has been converted to a string.");
|
|
325
|
+
}
|
|
326
|
+
// Validation: Check if distinct_id is provided
|
|
327
|
+
if (!newDistinctId) {
|
|
328
|
+
console.error("Unique user id has not been set in vTilt.identify");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
// Validation: Check for hardcoded strings
|
|
332
|
+
if (this.userManager.isDistinctIdStringLikePublic(newDistinctId)) {
|
|
333
|
+
console.error(`The string "${newDistinctId}" was set in vTilt.identify which indicates an error. This ID should be unique to the user and not a hardcoded string.`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
// Validation: Check for cookieless sentinel value
|
|
337
|
+
if (newDistinctId === "COOKIELESS_SENTINEL_VALUE") {
|
|
338
|
+
console.error(`The string "${newDistinctId}" was set in vTilt.identify which indicates an error. This ID is only used as a sentinel value.`);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const previousDistinctId = this.userManager.getDistinctId();
|
|
342
|
+
const anonymousId = this.userManager.getAnonymousId();
|
|
343
|
+
const deviceId = this.userManager.getDeviceId();
|
|
344
|
+
const isKnownAnonymous = this.userManager.getUserState() === "anonymous";
|
|
345
|
+
// Handle device ID if not already set
|
|
346
|
+
if (!deviceId) {
|
|
347
|
+
const device_id = previousDistinctId || anonymousId;
|
|
348
|
+
this.userManager.ensureDeviceId(device_id);
|
|
349
|
+
}
|
|
350
|
+
// Send $identify event when distinct_id is changing AND user was anonymous
|
|
351
|
+
// Event's distinct_id is the previous/anonymous ID, new distinct_id goes in payload
|
|
352
|
+
if (newDistinctId !== previousDistinctId && isKnownAnonymous) {
|
|
353
|
+
// Update user state and properties BEFORE sending event
|
|
354
|
+
// (But don't update distinct_id yet - sendEvent needs to use previous ID)
|
|
355
|
+
this.userManager.setUserState("identified");
|
|
356
|
+
this.userManager.updateUserProperties(userPropertiesToSet, userPropertiesToSetOnce);
|
|
357
|
+
// Send $identify event with previous/anonymous ID as event's distinct_id
|
|
358
|
+
// New distinct_id and properties go in payload
|
|
359
|
+
this.capture("$identify", {
|
|
360
|
+
distinct_id: newDistinctId, // New distinct_id in payload
|
|
361
|
+
$anon_distinct_id: previousDistinctId || anonymousId, // Previous ID in payload
|
|
362
|
+
$set: userPropertiesToSet || {},
|
|
363
|
+
$set_once: userPropertiesToSetOnce || {},
|
|
364
|
+
});
|
|
365
|
+
// Now update distinct_id after sending the event
|
|
366
|
+
this.userManager.setDistinctId(newDistinctId);
|
|
367
|
+
}
|
|
368
|
+
else if (userPropertiesToSet || userPropertiesToSetOnce) {
|
|
369
|
+
// If distinct_id is not changing but we have properties to set
|
|
370
|
+
// Update user state if not already identified
|
|
371
|
+
if (isKnownAnonymous) {
|
|
372
|
+
this.userManager.setUserState("identified");
|
|
373
|
+
}
|
|
374
|
+
// Update properties
|
|
375
|
+
this.userManager.updateUserProperties(userPropertiesToSet, userPropertiesToSetOnce);
|
|
376
|
+
// Send $set event to notify server of property updates
|
|
377
|
+
this.capture("$set", {
|
|
378
|
+
$set: userPropertiesToSet || {},
|
|
379
|
+
$set_once: userPropertiesToSetOnce || {},
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
else if (newDistinctId !== previousDistinctId) {
|
|
383
|
+
// If distinct_id is changing but user was already identified, just update the distinct_id
|
|
384
|
+
this.userManager.setUserState("identified");
|
|
385
|
+
this.userManager.setDistinctId(newDistinctId);
|
|
386
|
+
}
|
|
135
387
|
}
|
|
136
388
|
/**
|
|
137
|
-
* Set user properties
|
|
389
|
+
* Set user properties
|
|
138
390
|
* Sets properties on the person profile associated with the current distinct_id
|
|
139
391
|
*
|
|
140
392
|
* @example
|
|
@@ -156,11 +408,20 @@ class VTilt {
|
|
|
156
408
|
* @param userPropertiesToSetOnce Optional: Properties to set once (preserves first value)
|
|
157
409
|
*/
|
|
158
410
|
setUserProperties(userPropertiesToSet, userPropertiesToSetOnce) {
|
|
159
|
-
|
|
411
|
+
// Update user properties in UserManager
|
|
412
|
+
// Returns true if properties were updated (not a duplicate), false otherwise
|
|
413
|
+
const shouldSendEvent = this.userManager.setUserProperties(userPropertiesToSet, userPropertiesToSetOnce);
|
|
414
|
+
// Only send $set event if properties were actually updated (not a duplicate)
|
|
415
|
+
if (shouldSendEvent) {
|
|
416
|
+
this.capture("$set", {
|
|
417
|
+
$set: userPropertiesToSet || {},
|
|
418
|
+
$set_once: userPropertiesToSetOnce || {},
|
|
419
|
+
});
|
|
420
|
+
}
|
|
160
421
|
}
|
|
161
422
|
/**
|
|
162
423
|
* Reset user identity (logout)
|
|
163
|
-
*
|
|
424
|
+
* Clears all user data, generates new anonymous ID, resets session
|
|
164
425
|
*
|
|
165
426
|
* @param reset_device_id - If true, also resets device_id. Default: false
|
|
166
427
|
*
|
|
@@ -173,29 +434,31 @@ class VTilt {
|
|
|
173
434
|
* vtilt.resetUser(true)
|
|
174
435
|
*/
|
|
175
436
|
resetUser(reset_device_id) {
|
|
176
|
-
|
|
437
|
+
// Reset session ID
|
|
438
|
+
this.sessionManager.resetSessionId();
|
|
439
|
+
this.userManager.reset(reset_device_id);
|
|
177
440
|
}
|
|
178
441
|
/**
|
|
179
442
|
* Get current user identity
|
|
180
443
|
*/
|
|
181
444
|
getUserIdentity() {
|
|
182
|
-
return this.
|
|
445
|
+
return this.userManager.getUserIdentity();
|
|
183
446
|
}
|
|
184
447
|
/**
|
|
185
448
|
* Get current device ID
|
|
186
449
|
*/
|
|
187
450
|
getDeviceId() {
|
|
188
|
-
return this.
|
|
451
|
+
return this.userManager.getDeviceId();
|
|
189
452
|
}
|
|
190
453
|
/**
|
|
191
454
|
* Get current user state
|
|
192
455
|
*/
|
|
193
456
|
getUserState() {
|
|
194
|
-
return this.
|
|
457
|
+
return this.userManager.getUserState();
|
|
195
458
|
}
|
|
196
459
|
/**
|
|
197
460
|
* Create an alias to link two distinct IDs
|
|
198
|
-
*
|
|
461
|
+
* Links anonymous session to account on signup
|
|
199
462
|
*
|
|
200
463
|
* @param alias - A unique identifier that you want to use for this user in the future
|
|
201
464
|
* @param original - The current identifier being used for this user (optional, defaults to current distinct_id)
|
|
@@ -209,11 +472,27 @@ class VTilt {
|
|
|
209
472
|
* vtilt.createAlias('user_12345', 'anonymous_abc123')
|
|
210
473
|
*/
|
|
211
474
|
createAlias(alias, original) {
|
|
212
|
-
|
|
475
|
+
// Get alias information from UserManager
|
|
476
|
+
const aliasInfo = this.userManager.createAlias(alias, original);
|
|
477
|
+
// If alias was created, send $alias event
|
|
478
|
+
if (aliasInfo) {
|
|
479
|
+
this.capture("$alias", {
|
|
480
|
+
$original_id: aliasInfo.original,
|
|
481
|
+
$alias_id: aliasInfo.distinct_id,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
// If alias matches original, use identify instead
|
|
486
|
+
const distinctId = original ||
|
|
487
|
+
this.userManager.getDistinctId() ||
|
|
488
|
+
this.userManager.getAnonymousId();
|
|
489
|
+
if (alias === distinctId) {
|
|
490
|
+
this.identify(alias);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
213
493
|
}
|
|
214
494
|
/**
|
|
215
495
|
* Capture initial pageview with visibility check
|
|
216
|
-
* Based on PostHog's _captureInitialPageview implementation
|
|
217
496
|
*/
|
|
218
497
|
_captureInitialPageview() {
|
|
219
498
|
if (!globals_1.document) {
|
|
@@ -235,18 +514,18 @@ class VTilt {
|
|
|
235
514
|
// Extra check here to guarantee we only ever trigger a single initial pageview event
|
|
236
515
|
if (!this._initialPageviewCaptured) {
|
|
237
516
|
this._initialPageviewCaptured = true;
|
|
238
|
-
// Wait a bit for SPA routers
|
|
517
|
+
// Wait a bit for SPA routers
|
|
239
518
|
setTimeout(() => {
|
|
240
519
|
// Double-check we're still in browser environment (defensive check)
|
|
241
520
|
if (!globals_1.document || !globals_1.location) {
|
|
242
521
|
return;
|
|
243
522
|
}
|
|
244
523
|
// Note: $current_url, $host, $pathname, $referrer, $referring_domain, title
|
|
245
|
-
// are automatically added by
|
|
524
|
+
// are automatically added by capture() for all events (title only for $pageview)
|
|
246
525
|
const payload = {
|
|
247
526
|
navigation_type: "initial_load",
|
|
248
527
|
};
|
|
249
|
-
this.
|
|
528
|
+
this.capture("$pageview", payload);
|
|
250
529
|
}, 300);
|
|
251
530
|
// After we've captured the initial pageview, we can remove the listener
|
|
252
531
|
if (this._visibilityStateListener) {
|
|
@@ -265,16 +544,22 @@ class VTilt {
|
|
|
265
544
|
* Get current session ID
|
|
266
545
|
*/
|
|
267
546
|
getSessionId() {
|
|
268
|
-
return this.
|
|
547
|
+
return this.sessionManager.getSessionId();
|
|
269
548
|
}
|
|
270
549
|
/**
|
|
271
550
|
* Update configuration
|
|
272
551
|
*/
|
|
273
552
|
updateConfig(config) {
|
|
274
553
|
this.configManager.updateConfig(config);
|
|
554
|
+
const fullConfig = this.configManager.getConfig();
|
|
275
555
|
// Recreate managers with new config
|
|
276
|
-
|
|
277
|
-
|
|
556
|
+
let domain = fullConfig.domain;
|
|
557
|
+
if (!domain && globals_1.location) {
|
|
558
|
+
domain = this.getCurrentDomain();
|
|
559
|
+
}
|
|
560
|
+
this.sessionManager = new session_1.SessionManager(fullConfig.storage || "cookie", domain);
|
|
561
|
+
this.userManager = new user_manager_1.UserManager(fullConfig.persistence || "localStorage", domain);
|
|
562
|
+
this.webVitalsManager = new web_vitals_1.WebVitalsManager(fullConfig, this);
|
|
278
563
|
}
|
|
279
564
|
/**
|
|
280
565
|
* _execute_array() deals with processing any vTilt function
|
|
@@ -310,14 +595,12 @@ class VTilt {
|
|
|
310
595
|
* Called when DOM is loaded - processes queued requests
|
|
311
596
|
*/
|
|
312
597
|
_dom_loaded() {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
this.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
this.trackingManager.__request_queue = [];
|
|
320
|
-
}
|
|
598
|
+
// Process all queued requests
|
|
599
|
+
this.__request_queue.forEach((item) => {
|
|
600
|
+
this._send_retriable_request(item);
|
|
601
|
+
});
|
|
602
|
+
// Clear the queue
|
|
603
|
+
this.__request_queue = [];
|
|
321
604
|
}
|
|
322
605
|
}
|
|
323
606
|
exports.VTilt = VTilt;
|
|
@@ -331,13 +614,12 @@ const SUPPORTS_REQUEST = typeof globals_1.XMLHttpRequest !== "undefined" || type
|
|
|
331
614
|
let ENQUEUE_REQUESTS = !SUPPORTS_REQUEST &&
|
|
332
615
|
(globals_1.userAgent === null || globals_1.userAgent === void 0 ? void 0 : globals_1.userAgent.indexOf("MSIE")) === -1 &&
|
|
333
616
|
(globals_1.userAgent === null || globals_1.userAgent === void 0 ? void 0 : globals_1.userAgent.indexOf("Mozilla")) === -1;
|
|
334
|
-
// Expose ENQUEUE_REQUESTS to window for
|
|
617
|
+
// Expose ENQUEUE_REQUESTS to window for request queuing
|
|
335
618
|
if (globals_1.window) {
|
|
336
619
|
globals_1.window.__VTILT_ENQUEUE_REQUESTS = ENQUEUE_REQUESTS;
|
|
337
620
|
}
|
|
338
621
|
/**
|
|
339
622
|
* Add DOM loaded handler to process queued requests
|
|
340
|
-
* Similar to PostHog's add_dom_loaded_handler
|
|
341
623
|
*/
|
|
342
624
|
const add_dom_loaded_handler = function () {
|
|
343
625
|
// Cross browser DOM Loaded support
|
|
@@ -380,7 +662,7 @@ const add_dom_loaded_handler = function () {
|
|
|
380
662
|
}
|
|
381
663
|
};
|
|
382
664
|
/**
|
|
383
|
-
* Initialize vTilt as a module
|
|
665
|
+
* Initialize vTilt as a module
|
|
384
666
|
* Returns an uninitialized vTilt instance that the user must call init() on
|
|
385
667
|
*/
|
|
386
668
|
function init_as_module() {
|
|
@@ -389,7 +671,7 @@ function init_as_module() {
|
|
|
389
671
|
return vTiltMain;
|
|
390
672
|
}
|
|
391
673
|
/**
|
|
392
|
-
* Initialize vTilt from snippet
|
|
674
|
+
* Initialize vTilt from snippet
|
|
393
675
|
* Processes queued calls from the snippet stub and replaces it with real instance
|
|
394
676
|
*
|
|
395
677
|
* The snippet uses some clever tricks to allow deferred loading of array.js (this code)
|