swetrix 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Lib.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  import {
2
- isInBrowser, isLocalhost, isAutomated, getLocale, getTimezone, getReferrer,
3
- getUTMCampaign, getUTMMedium, getUTMSource, getPath,
2
+ isInBrowser,
3
+ isLocalhost,
4
+ isAutomated,
5
+ getLocale,
6
+ getTimezone,
7
+ getReferrer,
8
+ getUTMCampaign,
9
+ getUTMMedium,
10
+ getUTMSource,
11
+ getPath,
4
12
  } from './utils'
5
13
 
6
14
  export interface LibOptions {
7
15
  /**
8
- * When set to `true`, all tracking logs will be printed to console and localhost events will be sent to server.
16
+ * When set to `true`, localhost events will be sent to server.
9
17
  */
10
- debug?: boolean
18
+ devMode?: boolean
11
19
 
12
20
  /**
13
21
  * When set to `true`, the tracking library won't send any data to server.
@@ -37,6 +45,29 @@ export interface TrackEventOptions {
37
45
  }
38
46
  }
39
47
 
48
+ // Partial user-editable pageview payload
49
+ export interface IPageViewPayload {
50
+ lc: string | undefined
51
+ tz: string | undefined
52
+ ref: string | undefined
53
+ so: string | undefined
54
+ me: string | undefined
55
+ ca: string | undefined
56
+ pg: string
57
+ prev: string | null | undefined
58
+ }
59
+
60
+ interface IPerfPayload {
61
+ dns: number
62
+ tls: number
63
+ conn: number
64
+ response: number
65
+ render: number
66
+ dom_load: number
67
+ page_load: number
68
+ ttfb: number
69
+ }
70
+
40
71
  /**
41
72
  * The object returned by `trackPageViews()`, used to stop tracking pages.
42
73
  */
@@ -60,21 +91,9 @@ export interface PageViewsOptions {
60
91
  */
61
92
  unique?: boolean
62
93
 
63
- /** A list of Regular Expressions or string pathes to ignore. */
64
- ignore?: Array<string | RegExp>
65
-
66
- /** Do not send paths from ignore list to API. If set to `false`, the page view information will be sent to the Swetrix API, but the page will be displayed as a 'Redacted page' in the dashboard. */
67
- doNotAnonymise?: boolean
68
-
69
- /** Do not send Heartbeat requests to the server. */
70
- noHeartbeat?: boolean
71
-
72
94
  /** Send Heartbeat requests when the website tab is not active in the browser. */
73
95
  heartbeatOnBackground?: boolean
74
96
 
75
- /** Disable user-flow */
76
- noUserFlow?: boolean
77
-
78
97
  /**
79
98
  * Set to `true` to enable hash-based routing.
80
99
  * For example if you have pages like /#/path or want to track pages like /path#hash
@@ -86,6 +105,14 @@ export interface PageViewsOptions {
86
105
  * For example if you have pages like /path?search
87
106
  */
88
107
  search?: boolean
108
+
109
+ /**
110
+ * Callback to edit / prevent sending pageviews.
111
+ *
112
+ * @param payload - The pageview payload.
113
+ * @returns The edited payload or `false` to prevent sending the pageview. If `true` is returned, the payload will be sent as-is.
114
+ */
115
+ callback?: (payload: IPageViewPayload) => IPageViewPayload | boolean
89
116
  }
90
117
 
91
118
  export const defaultPageActions = {
@@ -111,6 +138,7 @@ export class Lib {
111
138
  }
112
139
 
113
140
  const data = {
141
+ ...event,
114
142
  pid: this.projectID,
115
143
  pg: this.activePage,
116
144
  lc: getLocale(),
@@ -119,7 +147,6 @@ export class Lib {
119
147
  so: getUTMSource(),
120
148
  me: getUTMMedium(),
121
149
  ca: getUTMCampaign(),
122
- ...event,
123
150
  }
124
151
  this.sendRequest('custom', data)
125
152
  }
@@ -134,15 +161,14 @@ export class Lib {
134
161
  }
135
162
 
136
163
  this.pageViewsOptions = options
137
- let hbInterval: NodeJS.Timeout, interval: NodeJS.Timeout
164
+ let interval: NodeJS.Timeout
165
+
138
166
  if (!options?.unique) {
139
167
  interval = setInterval(this.trackPathChange, 2000)
140
168
  }
141
169
 
142
- if (!options?.noHeartbeat) {
143
- setTimeout(this.heartbeat, 3000)
144
- hbInterval = setInterval(this.heartbeat, 28000)
145
- }
170
+ setTimeout(this.heartbeat, 3000)
171
+ const hbInterval = setInterval(this.heartbeat, 28000)
146
172
 
147
173
  const path = getPath({
148
174
  hash: options?.hash,
@@ -163,12 +189,12 @@ export class Lib {
163
189
  return this.pageData.actions
164
190
  }
165
191
 
166
- getPerformanceStats(): object {
192
+ getPerformanceStats(): IPerfPayload | {} {
167
193
  if (!this.canTrack() || this.perfStatsCollected || !window.performance?.getEntriesByType) {
168
194
  return {}
169
195
  }
170
196
 
171
- const perf = window.performance.getEntriesByType('navigation')[0]
197
+ const perf = window.performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming
172
198
 
173
199
  if (!perf) {
174
200
  return {}
@@ -178,25 +204,19 @@ export class Lib {
178
204
 
179
205
  return {
180
206
  // Network
181
- // @ts-ignore
182
207
  dns: perf.domainLookupEnd - perf.domainLookupStart, // DNS Resolution
183
- // @ts-ignore
184
208
  tls: perf.secureConnectionStart ? perf.requestStart - perf.secureConnectionStart : 0, // TLS Setup; checking if secureConnectionStart is not 0 (it's 0 for non-https websites)
185
- // @ts-ignore
186
- conn: perf.secureConnectionStart ? perf.secureConnectionStart - perf.connectStart : perf.connectEnd - perf.connectStart, // Connection time
187
- // @ts-ignore
209
+ conn: perf.secureConnectionStart
210
+ ? perf.secureConnectionStart - perf.connectStart
211
+ : perf.connectEnd - perf.connectStart, // Connection time
188
212
  response: perf.responseEnd - perf.responseStart, // Response Time (Download)
189
213
 
190
214
  // Frontend
191
- // @ts-ignore
192
215
  render: perf.domComplete - perf.domContentLoadedEventEnd, // Browser rendering the HTML time
193
- // @ts-ignore
194
216
  dom_load: perf.domContentLoadedEventEnd - perf.responseEnd, // DOM loading timing
195
- // @ts-ignore
196
217
  page_load: perf.loadEventStart, // Page load time
197
218
 
198
219
  // Backend
199
- // @ts-ignore
200
220
  ttfb: perf.responseStart - perf.requestStart,
201
221
  }
202
222
  }
@@ -213,19 +233,6 @@ export class Lib {
213
233
  this.sendRequest('hb', data)
214
234
  }
215
235
 
216
- private checkIgnore(path: string): boolean {
217
- const ignore = this.pageViewsOptions?.ignore
218
-
219
- if (Array.isArray(ignore)) {
220
- for (let i = 0; i < ignore.length; ++i) {
221
- if (ignore[i] === path) return true
222
- // @ts-ignore
223
- if (ignore[i] instanceof RegExp && ignore[i].test(path)) return true
224
- }
225
- }
226
- return false
227
- }
228
-
229
236
  // Tracking path changes. If path changes -> calling this.trackPage method
230
237
  private trackPathChange(): void {
231
238
  if (!this.pageData) return
@@ -244,13 +251,7 @@ export class Lib {
244
251
  // Assuming that this function is called in trackPage and this.activePage is not overwritten by new value yet
245
252
  // That method of getting previous page works for SPA websites
246
253
  if (this.activePage) {
247
- const shouldIgnore = this.checkIgnore(this.activePage)
248
-
249
- if (shouldIgnore && this.pageViewsOptions?.doNotAnonymise) {
250
- return null
251
- }
252
-
253
- return shouldIgnore ? null : this.activePage
254
+ return this.activePage
254
255
  }
255
256
 
256
257
  // Checking if URL is supported by the browser (for example, IE11 does not support it)
@@ -272,13 +273,7 @@ export class Lib {
272
273
  return null
273
274
  }
274
275
 
275
- const shouldIgnore = this.checkIgnore(pathname)
276
-
277
- if (shouldIgnore && this.pageViewsOptions?.doNotAnonymise) {
278
- return null
279
- }
280
-
281
- return shouldIgnore ? null : pathname
276
+ return pathname
282
277
  } catch {
283
278
  return null
284
279
  }
@@ -291,68 +286,62 @@ export class Lib {
291
286
  if (!this.pageData) return
292
287
  this.pageData.path = pg
293
288
 
294
- const shouldIgnore = this.checkIgnore(pg)
295
-
296
- if (shouldIgnore && this.pageViewsOptions?.doNotAnonymise) return
297
-
298
289
  const perf = this.getPerformanceStats()
299
290
 
300
- let prev
301
-
302
- if (!this.pageViewsOptions?.noUserFlow) {
303
- prev = this.getPreviousPage()
304
- }
291
+ const prev = this.getPreviousPage()
305
292
 
306
293
  this.activePage = pg
307
- this.submitPageView(shouldIgnore ? null : pg, prev, unique, perf)
294
+ this.submitPageView(pg, prev, unique, perf, true)
308
295
  }
309
296
 
310
- submitPageView(pg: null | string, prev: string | null | undefined, unique: boolean, perf: any): void {
311
- const data = {
297
+ submitPageView(
298
+ pg: string,
299
+ prev: string | null | undefined,
300
+ unique: boolean,
301
+ perf: IPerfPayload | {},
302
+ evokeCallback?: boolean,
303
+ ): void {
304
+ const privateData = {
312
305
  pid: this.projectID,
306
+ perf,
307
+ unique,
308
+ }
309
+ const pvPayload = {
313
310
  lc: getLocale(),
314
311
  tz: getTimezone(),
315
312
  ref: getReferrer(),
316
313
  so: getUTMSource(),
317
314
  me: getUTMMedium(),
318
315
  ca: getUTMCampaign(),
319
- unique,
320
316
  pg,
321
- perf,
322
317
  prev,
323
318
  }
324
319
 
325
- this.sendRequest('', data)
326
- }
320
+ if (evokeCallback && this.pageViewsOptions?.callback) {
321
+ const callbackResult = this.pageViewsOptions.callback(pvPayload)
327
322
 
328
- private debug(message: string): void {
329
- if (this.options?.debug) {
330
- console.log('[Swetrix]', message)
331
- }
332
- }
323
+ if (callbackResult === false) {
324
+ return
325
+ }
333
326
 
334
- private canTrack(): boolean {
335
- if (this.options?.disabled) {
336
- this.debug('Tracking disabled: the \'disabled\' setting is set to true.')
337
- return false
327
+ if (callbackResult && typeof callbackResult === 'object') {
328
+ Object.assign(pvPayload, callbackResult)
329
+ }
338
330
  }
339
331
 
340
- if (!isInBrowser()) {
341
- this.debug('Tracking disabled: script does not run in browser environment.')
342
- return false
343
- }
332
+ Object.assign(pvPayload, privateData)
344
333
 
345
- if (this.options?.respectDNT && window.navigator?.doNotTrack === '1') {
346
- this.debug('Tracking disabled: respecting user\'s \'Do Not Track\' preference.')
347
- return false
348
- }
349
-
350
- if (!this.options?.debug && isLocalhost()) {
351
- return false
352
- }
334
+ this.sendRequest('', pvPayload)
335
+ }
353
336
 
354
- if (isAutomated()) {
355
- this.debug('Tracking disabled: navigation is automated by WebDriver.')
337
+ private canTrack(): boolean {
338
+ if (
339
+ this.options?.disabled ||
340
+ !isInBrowser() ||
341
+ (this.options?.respectDNT && window.navigator?.doNotTrack === '1') ||
342
+ (!this.options?.devMode && isLocalhost()) ||
343
+ isAutomated()
344
+ ) {
356
345
  return false
357
346
  }
358
347
 
package/src/index.ts CHANGED
@@ -1,6 +1,4 @@
1
- import {
2
- Lib, LibOptions, TrackEventOptions, PageViewsOptions, PageActions, defaultPageActions,
3
- } from './Lib'
1
+ import { Lib, LibOptions, TrackEventOptions, PageViewsOptions, PageActions, defaultPageActions } from './Lib'
4
2
 
5
3
  export let LIB_INSTANCE: Lib | null = null
6
4
 
@@ -62,7 +60,7 @@ export function trackViews(options?: PageViewsOptions): Promise<PageActions> {
62
60
  /**
63
61
  * This function is used to manually track a page view event.
64
62
  * It's useful if your application uses esoteric routing which is not supported by Swetrix by default.
65
- *
63
+ *
66
64
  * @param path Path of the page to track (this will be sent to the Swetrix API and displayed in the dashboard).
67
65
  * @param prev Path of the previous page.
68
66
  * @param unique If set to `true`, only 1 event with the same ID will be saved per user session.