@v-tilt/browser 1.0.6 → 1.0.7

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/lib/tracking.js CHANGED
@@ -4,7 +4,7 @@ exports.TrackingManager = void 0;
4
4
  const session_1 = require("./session");
5
5
  const user_manager_1 = require("./user-manager");
6
6
  const utils_1 = require("./utils");
7
- const geolocation_1 = require("./geolocation");
7
+ const event_utils_1 = require("./utils/event-utils");
8
8
  const globals_1 = require("./utils/globals");
9
9
  class TrackingManager {
10
10
  constructor(config) {
@@ -53,6 +53,9 @@ class TrackingManager {
53
53
  }
54
54
  /**
55
55
  * Send event to endpoint
56
+ * PostHog-style: Automatically adds common properties to all events
57
+ * ($current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.)
58
+ * Also adds title property for $pageview events only
56
59
  */
57
60
  async sendEvent(name, payload) {
58
61
  this.sessionManager.setSessionId();
@@ -64,9 +67,26 @@ class TrackingManager {
64
67
  return;
65
68
  }
66
69
  const url = this.buildUrl();
70
+ // Add properties to all events (PostHog-style)
71
+ // This includes: $current_url, $host, $pathname, $referrer, $referring_domain, $browser_language, etc.
72
+ const eventProperties = (0, event_utils_1.getEventProperties)();
73
+ // Get session and window IDs (PostHog-style)
74
+ // Both methods ensure IDs always exist (generate if needed)
75
+ const session_id = this.sessionManager.getSessionId();
76
+ const window_id = this.sessionManager.getWindowId();
77
+ const enrichedPayload = {
78
+ ...eventProperties, // Base properties for all events
79
+ $session_id: session_id, // PostHog-style: session ID in properties
80
+ $window_id: window_id, // PostHog-style: window ID in properties
81
+ ...payload, // User-provided payload (can override base properties)
82
+ };
83
+ // Add title only to $pageview events (PostHog-style)
84
+ if (name === "$pageview" && globals_1.document) {
85
+ enrichedPayload.title = globals_1.document.title;
86
+ }
67
87
  let processedPayload;
68
88
  if (this.config.stringifyPayload !== false) {
69
- processedPayload = (0, utils_1.maskSuspiciousAttributes)(payload);
89
+ processedPayload = (0, utils_1.maskSuspiciousAttributes)(enrichedPayload);
70
90
  processedPayload = Object.assign({}, JSON.parse(processedPayload), this.config.globalAttributes);
71
91
  processedPayload = JSON.stringify(processedPayload);
72
92
  if (!(0, utils_1.isValidPayload)(processedPayload)) {
@@ -74,20 +94,19 @@ class TrackingManager {
74
94
  }
75
95
  }
76
96
  else {
77
- processedPayload = Object.assign({}, payload, this.config.globalAttributes);
97
+ processedPayload = Object.assign({}, enrichedPayload, this.config.globalAttributes);
78
98
  const maskedStr = (0, utils_1.maskSuspiciousAttributes)(processedPayload);
79
99
  if (!(0, utils_1.isValidPayload)(maskedStr)) {
80
100
  return;
81
101
  }
82
102
  processedPayload = JSON.parse(maskedStr);
83
103
  }
84
- const session_id = this.sessionManager.getSessionId() || (0, utils_1.uuidv4)();
104
+ // session_id is already in payload as $session_id (PostHog-style)
85
105
  const distinct_id = this.userManager.getDistinctId();
86
106
  const anonymous_id = this.userManager.getAnonymousId();
87
107
  const trackingEvent = {
88
108
  timestamp: new Date().toISOString(),
89
109
  event: name,
90
- session_id,
91
110
  tenant_id: this.config.projectId || "",
92
111
  domain: this.config.domain || this.getCurrentDomain(), // Use config domain or current domain
93
112
  payload: processedPayload,
@@ -148,44 +167,6 @@ class TrackingManager {
148
167
  _send_retriable_request(item) {
149
168
  this.sendRequest(item.url, item.event, false);
150
169
  }
151
- /**
152
- * Track page hit
153
- * Based on PostHog's pageview tracking
154
- *
155
- * @param navigationType - Type of navigation that triggered the pageview
156
- */
157
- trackPageHit(navigationType) {
158
- // If test environment
159
- if ((0, utils_1.isTestEnvironment)()) {
160
- return;
161
- }
162
- // Only track page hits in browser environment (not SSR)
163
- if (!globals_1.window || !globals_1.document || !globals_1.navigator || !globals_1.location) {
164
- return;
165
- }
166
- const { country, locale } = (0, geolocation_1.getCountryAndLocale)();
167
- // Wait a bit for SPA routers
168
- setTimeout(() => {
169
- // Double-check we're still in browser environment (defensive check)
170
- if (!globals_1.window || !globals_1.document || !globals_1.navigator || !globals_1.location) {
171
- return;
172
- }
173
- const payload = {
174
- "user-agent": globals_1.navigator.userAgent,
175
- locale,
176
- location: country,
177
- referrer: globals_1.document.referrer,
178
- pathname: globals_1.location.pathname,
179
- href: globals_1.location.href,
180
- title: globals_1.document.title, // Include title like PostHog
181
- };
182
- // Include navigation_type if provided (PostHog-style)
183
- if (navigationType) {
184
- payload.navigation_type = navigationType;
185
- }
186
- this.sendEvent("page_hit", payload);
187
- }, 300);
188
- }
189
170
  /**
190
171
  * Get current session ID
191
172
  */
@@ -279,74 +260,79 @@ class TrackingManager {
279
260
  this.sendAliasEvent(aliasEvent);
280
261
  });
281
262
  }
263
+ /**
264
+ * Get session and window IDs (PostHog-style)
265
+ * Both methods ensure IDs always exist (generate if needed)
266
+ */
267
+ _getSessionAndWindowIds() {
268
+ return {
269
+ session_id: this.sessionManager.getSessionId(),
270
+ window_id: this.sessionManager.getWindowId(),
271
+ };
272
+ }
273
+ /**
274
+ * Create base tracking event structure
275
+ */
276
+ _createTrackingEvent(event, payload, distinct_id) {
277
+ return {
278
+ timestamp: new Date().toISOString(),
279
+ event,
280
+ tenant_id: this.config.projectId || "",
281
+ domain: this.config.domain || this.getCurrentDomain(),
282
+ payload,
283
+ distinct_id,
284
+ };
285
+ }
282
286
  /**
283
287
  * Send identify event for session merging
284
288
  */
285
289
  sendIdentifyEvent(distinctId, anonymousId, deviceId, properties) {
286
- const url = this.buildUrl();
287
- const session_id = this.sessionManager.getSessionId() || (0, utils_1.uuidv4)();
290
+ const { session_id, window_id } = this._getSessionAndWindowIds();
288
291
  // Payload contains only properties, not event or distinct_id (those are in trackingEvent)
292
+ // Add $session_id and $window_id to payload (PostHog-style)
289
293
  const identifyPayload = {
294
+ $session_id: session_id,
295
+ $window_id: window_id,
290
296
  $anon_distinct_id: anonymousId,
291
297
  $device_id: deviceId,
292
298
  ...properties,
293
299
  };
294
- const trackingEvent = {
295
- timestamp: new Date().toISOString(),
296
- event: "$identify",
297
- session_id,
298
- tenant_id: this.config.projectId || "",
299
- domain: this.config.domain || this.getCurrentDomain(),
300
- payload: identifyPayload,
301
- distinct_id: distinctId,
302
- };
303
- this.sendRequest(url, trackingEvent, true);
300
+ const trackingEvent = this._createTrackingEvent("$identify", identifyPayload, distinctId);
301
+ this.sendRequest(this.buildUrl(), trackingEvent, true);
304
302
  }
305
303
  /**
306
304
  * Send $set event for property updates (PostHog behavior)
307
305
  * This notifies Tinybird when user properties are updated
308
306
  */
309
307
  sendSetEvent(userPropertiesToSet, userPropertiesToSetOnce) {
310
- const url = this.buildUrl();
311
- const session_id = this.sessionManager.getSessionId() || (0, utils_1.uuidv4)();
308
+ const { session_id, window_id } = this._getSessionAndWindowIds();
312
309
  const distinct_id = this.userManager.getDistinctId();
313
310
  const anonymous_id = this.userManager.getAnonymousId();
311
+ // Add $session_id and $window_id to payload (PostHog-style)
314
312
  const setEventPayload = {
313
+ $session_id: session_id,
314
+ $window_id: window_id,
315
315
  $set: userPropertiesToSet || {},
316
316
  $set_once: userPropertiesToSetOnce || {},
317
317
  };
318
- const trackingEvent = {
319
- timestamp: new Date().toISOString(),
320
- event: "$set",
321
- session_id,
322
- tenant_id: this.config.projectId || "",
323
- domain: this.config.domain || this.getCurrentDomain(),
324
- payload: setEventPayload,
325
- distinct_id: distinct_id || anonymous_id,
326
- };
327
- this.sendRequest(url, trackingEvent, true);
318
+ const trackingEvent = this._createTrackingEvent("$set", setEventPayload, distinct_id || anonymous_id);
319
+ this.sendRequest(this.buildUrl(), trackingEvent, true);
328
320
  }
329
321
  /**
330
322
  * Send alias event for identity linking
331
323
  * PostHog format: { alias: alias, distinct_id: original }
332
324
  */
333
325
  sendAliasEvent(aliasEvent) {
334
- const url = this.buildUrl();
335
- const session_id = this.sessionManager.getSessionId() || (0, utils_1.uuidv4)();
326
+ const { session_id, window_id } = this._getSessionAndWindowIds();
327
+ // Add $session_id and $window_id to payload (PostHog-style)
336
328
  const aliasPayload = {
329
+ $session_id: session_id,
330
+ $window_id: window_id,
337
331
  $original_id: aliasEvent.original,
338
332
  $alias_id: aliasEvent.distinct_id,
339
333
  };
340
- const trackingEvent = {
341
- timestamp: new Date().toISOString(),
342
- event: "$alias",
343
- session_id,
344
- tenant_id: this.config.projectId || "",
345
- domain: this.config.domain || this.getCurrentDomain(),
346
- payload: aliasPayload,
347
- distinct_id: aliasEvent.distinct_id,
348
- };
349
- this.sendRequest(url, trackingEvent, true);
334
+ const trackingEvent = this._createTrackingEvent("$alias", aliasPayload, aliasEvent.distinct_id);
335
+ this.sendRequest(this.buildUrl(), trackingEvent, true);
350
336
  }
351
337
  }
352
338
  exports.TrackingManager = TrackingManager;
package/lib/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface VTiltConfig {
2
2
  projectId: string;
3
3
  token: string;
4
+ name?: string;
4
5
  host?: string;
5
6
  scriptHost?: string;
6
7
  proxy?: string;
@@ -35,7 +36,6 @@ export interface EventPayload {
35
36
  export interface TrackingEvent {
36
37
  timestamp: string;
37
38
  event: string;
38
- session_id: string;
39
39
  tenant_id: string;
40
40
  domain: string;
41
41
  payload: EventPayload;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Get browser language (PostHog-style)
3
+ * Returns the browser's language setting (e.g., "en-US")
4
+ */
5
+ export declare function getBrowserLanguage(): string | undefined;
6
+ /**
7
+ * Get browser language prefix (PostHog-style)
8
+ * Returns the language code without region (e.g., "en" from "en-US")
9
+ */
10
+ export declare function getBrowserLanguagePrefix(): string | undefined;
11
+ /**
12
+ * Get referrer (PostHog-style)
13
+ * Returns document.referrer or '$direct' if no referrer
14
+ */
15
+ export declare function getReferrer(): string;
16
+ /**
17
+ * Get referring domain (PostHog-style)
18
+ * Returns the hostname of the referrer URL or '$direct' if no referrer
19
+ */
20
+ export declare function getReferringDomain(): string;
21
+ /**
22
+ * Get timezone (PostHog-style)
23
+ * Returns the timezone (e.g., "America/New_York")
24
+ */
25
+ export declare function getTimezone(): string | undefined;
26
+ /**
27
+ * Get timezone offset (PostHog-style)
28
+ * Returns the timezone offset in minutes
29
+ */
30
+ export declare function getTimezoneOffset(): number | undefined;
31
+ /**
32
+ * Get event properties that should be added to all events (PostHog-style)
33
+ * Matches PostHog's getEventProperties() implementation
34
+ */
35
+ export declare function getEventProperties(): Record<string, any>;
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getBrowserLanguage = getBrowserLanguage;
4
+ exports.getBrowserLanguagePrefix = getBrowserLanguagePrefix;
5
+ exports.getReferrer = getReferrer;
6
+ exports.getReferringDomain = getReferringDomain;
7
+ exports.getTimezone = getTimezone;
8
+ exports.getTimezoneOffset = getTimezoneOffset;
9
+ exports.getEventProperties = getEventProperties;
10
+ const globals_1 = require("./globals");
11
+ const user_agent_utils_1 = require("./user-agent-utils");
12
+ // Library version - should match package.json version
13
+ const LIB_VERSION = "1.0.7"; // TODO: Auto-import from package.json
14
+ /**
15
+ * Get browser language (PostHog-style)
16
+ * Returns the browser's language setting (e.g., "en-US")
17
+ */
18
+ function getBrowserLanguage() {
19
+ if (typeof globals_1.navigator === "undefined") {
20
+ return undefined;
21
+ }
22
+ return (globals_1.navigator.language || // Any modern browser
23
+ globals_1.navigator.userLanguage // IE11
24
+ );
25
+ }
26
+ /**
27
+ * Get browser language prefix (PostHog-style)
28
+ * Returns the language code without region (e.g., "en" from "en-US")
29
+ */
30
+ function getBrowserLanguagePrefix() {
31
+ const lang = getBrowserLanguage();
32
+ return typeof lang === "string" ? lang.split("-")[0] : undefined;
33
+ }
34
+ /**
35
+ * Get referrer (PostHog-style)
36
+ * Returns document.referrer or '$direct' if no referrer
37
+ */
38
+ function getReferrer() {
39
+ return (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer) || "$direct";
40
+ }
41
+ /**
42
+ * Get referring domain (PostHog-style)
43
+ * Returns the hostname of the referrer URL or '$direct' if no referrer
44
+ */
45
+ function getReferringDomain() {
46
+ if (!(globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.referrer)) {
47
+ return "$direct";
48
+ }
49
+ try {
50
+ const url = new URL(globals_1.document.referrer);
51
+ return url.host || "$direct";
52
+ }
53
+ catch (_a) {
54
+ return "$direct";
55
+ }
56
+ }
57
+ /**
58
+ * Get timezone (PostHog-style)
59
+ * Returns the timezone (e.g., "America/New_York")
60
+ */
61
+ function getTimezone() {
62
+ try {
63
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
64
+ }
65
+ catch (_a) {
66
+ return undefined;
67
+ }
68
+ }
69
+ /**
70
+ * Get timezone offset (PostHog-style)
71
+ * Returns the timezone offset in minutes
72
+ */
73
+ function getTimezoneOffset() {
74
+ try {
75
+ return new Date().getTimezoneOffset();
76
+ }
77
+ catch (_a) {
78
+ return undefined;
79
+ }
80
+ }
81
+ /**
82
+ * Generate insert ID for deduplication (PostHog-style)
83
+ */
84
+ function generateInsertId() {
85
+ return (Math.random().toString(36).substring(2, 10) +
86
+ Math.random().toString(36).substring(2, 10));
87
+ }
88
+ /**
89
+ * Get event properties that should be added to all events (PostHog-style)
90
+ * Matches PostHog's getEventProperties() implementation
91
+ */
92
+ function getEventProperties() {
93
+ const props = {};
94
+ if (!globals_1.userAgent) {
95
+ return props;
96
+ }
97
+ // Device/OS properties
98
+ const [os_name, os_version] = (0, user_agent_utils_1.detectOS)(globals_1.userAgent);
99
+ if (os_name) {
100
+ props.$os = os_name;
101
+ }
102
+ if (os_version) {
103
+ props.$os_version = os_version;
104
+ }
105
+ const browser = (0, user_agent_utils_1.detectBrowser)(globals_1.userAgent, globals_1.navigator === null || globals_1.navigator === void 0 ? void 0 : globals_1.navigator.vendor);
106
+ if (browser) {
107
+ props.$browser = browser;
108
+ }
109
+ const browserVersion = (0, user_agent_utils_1.detectBrowserVersion)(globals_1.userAgent, globals_1.navigator === null || globals_1.navigator === void 0 ? void 0 : globals_1.navigator.vendor);
110
+ if (browserVersion) {
111
+ props.$browser_version = browserVersion;
112
+ }
113
+ const device = (0, user_agent_utils_1.detectDevice)(globals_1.userAgent);
114
+ if (device) {
115
+ props.$device = device;
116
+ }
117
+ const deviceType = (0, user_agent_utils_1.detectDeviceType)(globals_1.userAgent);
118
+ if (deviceType) {
119
+ props.$device_type = deviceType;
120
+ }
121
+ // Timezone properties
122
+ const timezone = getTimezone();
123
+ if (timezone) {
124
+ props.$timezone = timezone;
125
+ }
126
+ const timezoneOffset = getTimezoneOffset();
127
+ if (timezoneOffset !== undefined) {
128
+ props.$timezone_offset = timezoneOffset;
129
+ }
130
+ // URL properties (added to all events)
131
+ if (globals_1.location) {
132
+ props.$current_url = globals_1.location.href;
133
+ props.$host = globals_1.location.host;
134
+ props.$pathname = globals_1.location.pathname;
135
+ }
136
+ // User agent
137
+ if (globals_1.userAgent) {
138
+ props.$raw_user_agent =
139
+ globals_1.userAgent.length > 1000 ? globals_1.userAgent.substring(0, 997) + "..." : globals_1.userAgent;
140
+ }
141
+ // Browser language (added to all events)
142
+ const browserLanguage = getBrowserLanguage();
143
+ const browserLanguagePrefix = getBrowserLanguagePrefix();
144
+ if (browserLanguage) {
145
+ props.$browser_language = browserLanguage;
146
+ }
147
+ if (browserLanguagePrefix) {
148
+ props.$browser_language_prefix = browserLanguagePrefix;
149
+ }
150
+ // Screen/viewport properties
151
+ if (globals_1.window === null || globals_1.window === void 0 ? void 0 : globals_1.window.screen) {
152
+ if (globals_1.window.screen.height) {
153
+ props.$screen_height = globals_1.window.screen.height;
154
+ }
155
+ if (globals_1.window.screen.width) {
156
+ props.$screen_width = globals_1.window.screen.width;
157
+ }
158
+ }
159
+ if (globals_1.window) {
160
+ if (globals_1.window.innerHeight) {
161
+ props.$viewport_height = globals_1.window.innerHeight;
162
+ }
163
+ if (globals_1.window.innerWidth) {
164
+ props.$viewport_width = globals_1.window.innerWidth;
165
+ }
166
+ }
167
+ // Library info
168
+ props.$lib = "web";
169
+ props.$lib_version = LIB_VERSION;
170
+ // Insert ID for deduplication
171
+ props.$insert_id = generateInsertId();
172
+ // Timestamp (epoch time in seconds)
173
+ props.$time = Date.now() / 1000;
174
+ // Referrer properties (added to all events)
175
+ props.$referrer = getReferrer();
176
+ props.$referring_domain = getReferringDomain();
177
+ return props;
178
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This function detects which browser is running this script.
3
+ * The order of the checks are important since many user agents
4
+ * include keywords used in later checks.
5
+ */
6
+ export declare const detectBrowser: (user_agent: string, vendor: string | undefined) => string;
7
+ /**
8
+ * This function detects which browser version is running this script,
9
+ * parsing major and minor version (e.g., 42.1). User agent strings from:
10
+ * http://www.useragentstring.com/pages/useragentstring.php
11
+ *
12
+ * `navigator.vendor` is passed in and used to help with detecting certain browsers
13
+ * NB `navigator.vendor` is deprecated and not present in every browser
14
+ */
15
+ export declare const detectBrowserVersion: (userAgent: string, vendor: string | undefined) => number | null;
16
+ export declare const detectOS: (user_agent: string) => [string, string];
17
+ export declare const detectDevice: (user_agent: string) => string;
18
+ export declare const detectDeviceType: (user_agent: string) => string;