@driveflux/web-analytics 1.0.2

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/track.js ADDED
@@ -0,0 +1,445 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_without_holes(arr) {
7
+ if (Array.isArray(arr)) return _array_like_to_array(arr);
8
+ }
9
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
10
+ try {
11
+ var info = gen[key](arg);
12
+ var value = info.value;
13
+ } catch (error) {
14
+ reject(error);
15
+ return;
16
+ }
17
+ if (info.done) {
18
+ resolve(value);
19
+ } else {
20
+ Promise.resolve(value).then(_next, _throw);
21
+ }
22
+ }
23
+ function _async_to_generator(fn) {
24
+ return function() {
25
+ var self = this, args = arguments;
26
+ return new Promise(function(resolve, reject) {
27
+ var gen = fn.apply(self, args);
28
+ function _next(value) {
29
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
30
+ }
31
+ function _throw(err) {
32
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
33
+ }
34
+ _next(undefined);
35
+ });
36
+ };
37
+ }
38
+ function _define_property(obj, key, value) {
39
+ if (key in obj) {
40
+ Object.defineProperty(obj, key, {
41
+ value: value,
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true
45
+ });
46
+ } else {
47
+ obj[key] = value;
48
+ }
49
+ return obj;
50
+ }
51
+ function _iterable_to_array(iter) {
52
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
53
+ }
54
+ function _non_iterable_spread() {
55
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
56
+ }
57
+ function _object_spread(target) {
58
+ for(var i = 1; i < arguments.length; i++){
59
+ var source = arguments[i] != null ? arguments[i] : {};
60
+ var ownKeys = Object.keys(source);
61
+ if (typeof Object.getOwnPropertySymbols === "function") {
62
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
63
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
64
+ }));
65
+ }
66
+ ownKeys.forEach(function(key) {
67
+ _define_property(target, key, source[key]);
68
+ });
69
+ }
70
+ return target;
71
+ }
72
+ function _to_consumable_array(arr) {
73
+ return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
74
+ }
75
+ function _unsupported_iterable_to_array(o, minLen) {
76
+ if (!o) return;
77
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
78
+ var n = Object.prototype.toString.call(o).slice(8, -1);
79
+ if (n === "Object" && o.constructor) n = o.constructor.name;
80
+ if (n === "Map" || n === "Set") return Array.from(n);
81
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
82
+ }
83
+ function _ts_generator(thisArg, body) {
84
+ var f, y, t, g, _ = {
85
+ label: 0,
86
+ sent: function() {
87
+ if (t[0] & 1) throw t[1];
88
+ return t[1];
89
+ },
90
+ trys: [],
91
+ ops: []
92
+ };
93
+ return g = {
94
+ next: verb(0),
95
+ "throw": verb(1),
96
+ "return": verb(2)
97
+ }, typeof Symbol === "function" && (g[Symbol.iterator] = function() {
98
+ return this;
99
+ }), g;
100
+ function verb(n) {
101
+ return function(v) {
102
+ return step([
103
+ n,
104
+ v
105
+ ]);
106
+ };
107
+ }
108
+ function step(op) {
109
+ if (f) throw new TypeError("Generator is already executing.");
110
+ while(_)try {
111
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
112
+ if (y = 0, t) op = [
113
+ op[0] & 2,
114
+ t.value
115
+ ];
116
+ switch(op[0]){
117
+ case 0:
118
+ case 1:
119
+ t = op;
120
+ break;
121
+ case 4:
122
+ _.label++;
123
+ return {
124
+ value: op[1],
125
+ done: false
126
+ };
127
+ case 5:
128
+ _.label++;
129
+ y = op[1];
130
+ op = [
131
+ 0
132
+ ];
133
+ continue;
134
+ case 7:
135
+ op = _.ops.pop();
136
+ _.trys.pop();
137
+ continue;
138
+ default:
139
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
140
+ _ = 0;
141
+ continue;
142
+ }
143
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
144
+ _.label = op[1];
145
+ break;
146
+ }
147
+ if (op[0] === 6 && _.label < t[1]) {
148
+ _.label = t[1];
149
+ t = op;
150
+ break;
151
+ }
152
+ if (t && _.label < t[2]) {
153
+ _.label = t[2];
154
+ _.ops.push(op);
155
+ break;
156
+ }
157
+ if (t[2]) _.ops.pop();
158
+ _.trys.pop();
159
+ continue;
160
+ }
161
+ op = body.call(thisArg, _);
162
+ } catch (e) {
163
+ op = [
164
+ 6,
165
+ e
166
+ ];
167
+ y = 0;
168
+ } finally{
169
+ f = t = 0;
170
+ }
171
+ if (op[0] & 5) throw op[1];
172
+ return {
173
+ value: op[0] ? op[1] : void 0,
174
+ done: true
175
+ };
176
+ }
177
+ }
178
+ import { sendGTMEvent } from '@next/third-parties/google';
179
+ import isEqual from 'lodash/isEqual';
180
+ import { useRouter } from 'next/router';
181
+ import { useCallback, useEffect, useRef } from 'react';
182
+ var eventsQueue = new Set();
183
+ var drainEvents = function() {
184
+ var events = _to_consumable_array(eventsQueue);
185
+ eventsQueue.clear();
186
+ events.forEach(function(event) {
187
+ sendGTMEvent(event);
188
+ });
189
+ };
190
+ export function getGA4ClientId() {
191
+ if (typeof document === 'undefined') {
192
+ return null;
193
+ }
194
+ var cookies = document.cookie.split(';');
195
+ var gaCookie = cookies.find(function(cookie) {
196
+ return cookie.trim().startsWith('_ga=');
197
+ });
198
+ if (gaCookie) {
199
+ var matches = gaCookie.split('.');
200
+ if (matches.length > 2) {
201
+ return "".concat(matches[2], ".").concat(matches[3]) // Returns the GA4 client ID
202
+ ;
203
+ }
204
+ }
205
+ return null // If no GA cookie is found or format is unexpected
206
+ ;
207
+ }
208
+ function hashData(input) {
209
+ return _hashData.apply(this, arguments);
210
+ }
211
+ function _hashData() {
212
+ _hashData = _async_to_generator(function(input) {
213
+ var encoder, data, hashBuffer, hashArray, hashHex;
214
+ return _ts_generator(this, function(_state) {
215
+ switch(_state.label){
216
+ case 0:
217
+ encoder = new TextEncoder();
218
+ data = encoder.encode(input);
219
+ return [
220
+ 4,
221
+ crypto.subtle.digest('SHA-256', data)
222
+ ];
223
+ case 1:
224
+ hashBuffer = _state.sent();
225
+ hashArray = Array.from(new Uint8Array(hashBuffer)) // Convert buffer to byte array
226
+ ;
227
+ hashHex = hashArray.map(function(b) {
228
+ return b.toString(16).padStart(2, '0');
229
+ }).join('') // Convert bytes to hex string
230
+ ;
231
+ return [
232
+ 2,
233
+ hashHex
234
+ ];
235
+ }
236
+ });
237
+ });
238
+ return _hashData.apply(this, arguments);
239
+ }
240
+ var convertUserData = function() {
241
+ var _ref = _async_to_generator(function(dataInput) {
242
+ var _dataInput_addresses, _dataInput_addresses1, userAddress, _tmp, _tmp1, _tmp2, _tmp3, _tmp4, _tmp5;
243
+ return _ts_generator(this, function(_state) {
244
+ switch(_state.label){
245
+ case 0:
246
+ userAddress = ((_dataInput_addresses = dataInput.addresses) === null || _dataInput_addresses === void 0 ? void 0 : _dataInput_addresses.home) || ((_dataInput_addresses1 = dataInput.addresses) === null || _dataInput_addresses1 === void 0 ? void 0 : _dataInput_addresses1.billing);
247
+ _tmp = {
248
+ user_id: dataInput.id
249
+ };
250
+ return [
251
+ 4,
252
+ hashData(dataInput.email)
253
+ ];
254
+ case 1:
255
+ _tmp.email = _state.sent();
256
+ if (!dataInput.phoneNumber) return [
257
+ 3,
258
+ 3
259
+ ];
260
+ return [
261
+ 4,
262
+ hashData(dataInput.phoneNumber)
263
+ ];
264
+ case 2:
265
+ _tmp1 = _state.sent();
266
+ return [
267
+ 3,
268
+ 4
269
+ ];
270
+ case 3:
271
+ _tmp1 = undefined;
272
+ _state.label = 4;
273
+ case 4:
274
+ _tmp.phone_number = _tmp1;
275
+ if (!userAddress) return [
276
+ 3,
277
+ 11
278
+ ];
279
+ _tmp3 = {};
280
+ if (!dataInput.firstName) return [
281
+ 3,
282
+ 6
283
+ ];
284
+ return [
285
+ 4,
286
+ hashData(dataInput.firstName)
287
+ ];
288
+ case 5:
289
+ _tmp4 = _state.sent();
290
+ return [
291
+ 3,
292
+ 7
293
+ ];
294
+ case 6:
295
+ _tmp4 = undefined;
296
+ _state.label = 7;
297
+ case 7:
298
+ _tmp3.first_name = _tmp4;
299
+ if (!dataInput.lastName) return [
300
+ 3,
301
+ 9
302
+ ];
303
+ return [
304
+ 4,
305
+ hashData(dataInput.lastName)
306
+ ];
307
+ case 8:
308
+ _tmp5 = _state.sent();
309
+ return [
310
+ 3,
311
+ 10
312
+ ];
313
+ case 9:
314
+ _tmp5 = undefined;
315
+ _state.label = 10;
316
+ case 10:
317
+ _tmp2 = (_tmp3.last_name = _tmp5, _tmp3.street = [
318
+ userAddress.street1,
319
+ userAddress.street2
320
+ ].join(', '), _tmp3.city = userAddress.city, _tmp3.region = userAddress.state, _tmp3.postal_code = userAddress.postalCode, _tmp3.country = userAddress.country, _tmp3);
321
+ return [
322
+ 3,
323
+ 12
324
+ ];
325
+ case 11:
326
+ _tmp2 = undefined;
327
+ _state.label = 12;
328
+ case 12:
329
+ return [
330
+ 2,
331
+ (_tmp.address = _tmp2, _tmp)
332
+ ];
333
+ }
334
+ });
335
+ });
336
+ return function convertUserData(dataInput) {
337
+ return _ref.apply(this, arguments);
338
+ };
339
+ }();
340
+ export var useTrackEvent = function() {
341
+ var userData = useRef();
342
+ var track = useCallback(function(eventName, eventData) {
343
+ console.log('🚨🚨🚨 firing event', eventName, eventData);
344
+ if (!eventName) {
345
+ console.error('No event name provided');
346
+ return;
347
+ }
348
+ var event = _object_spread({
349
+ event: eventName
350
+ }, userData.current ? {
351
+ user_data: userData.current
352
+ } : {}, eventData);
353
+ if (!window.fluxDataLayer) {
354
+ eventsQueue.add(event);
355
+ return;
356
+ }
357
+ sendGTMEvent(event);
358
+ }, []);
359
+ var setUserData = useCallback(function() {
360
+ var _ref = _async_to_generator(function(dataInput) {
361
+ return _ts_generator(this, function(_state) {
362
+ switch(_state.label){
363
+ case 0:
364
+ return [
365
+ 4,
366
+ convertUserData(dataInput)
367
+ ];
368
+ case 1:
369
+ userData.current = _state.sent();
370
+ localStorage.setItem('ud', Buffer.from(JSON.stringify(userData.current), 'utf8').toString('base64'));
371
+ return [
372
+ 2
373
+ ];
374
+ }
375
+ });
376
+ });
377
+ return function(dataInput) {
378
+ return _ref.apply(this, arguments);
379
+ };
380
+ }(), []);
381
+ useEffect(function() {
382
+ var interval = setInterval(function() {
383
+ if (window.fluxDataLayer) {
384
+ drainEvents();
385
+ clearInterval(interval);
386
+ }
387
+ }, 100);
388
+ return function() {
389
+ return clearInterval(interval);
390
+ };
391
+ }, []);
392
+ useEffect(function() {
393
+ var ud = localStorage.getItem('ud');
394
+ if (!ud) {
395
+ return;
396
+ }
397
+ try {
398
+ var json = Buffer.from(ud, 'base64').toString('utf8');
399
+ userData.current = JSON.parse(json);
400
+ } catch (e) {}
401
+ }, []);
402
+ return {
403
+ track: track,
404
+ setUserData: setUserData
405
+ };
406
+ };
407
+ export var useAutoTrackEvent = function(eventName, eventData) {
408
+ var router = useRouter();
409
+ var canFireEvents = router.isReady;
410
+ var previousEventData = useRef();
411
+ var firedFirstEvent = useRef(false);
412
+ useEffect(function() {
413
+ var interval = setInterval(function() {
414
+ if (window.fluxDataLayer) {
415
+ drainEvents();
416
+ clearInterval(interval);
417
+ }
418
+ }, 100);
419
+ return function() {
420
+ return clearInterval(interval);
421
+ };
422
+ }, []);
423
+ useEffect(function() {
424
+ if (!canFireEvents) {
425
+ return;
426
+ }
427
+ if (isEqual(previousEventData.current, eventData) && firedFirstEvent.current) {
428
+ return;
429
+ }
430
+ firedFirstEvent.current = true;
431
+ console.log('🚨🚨🚨 firing event', eventName, eventData);
432
+ var event = _object_spread({
433
+ event: eventName
434
+ }, eventData);
435
+ if (!window.fluxDataLayer) {
436
+ eventsQueue.add(event);
437
+ return;
438
+ }
439
+ sendGTMEvent(event);
440
+ previousEventData.current = eventData;
441
+ }, [
442
+ canFireEvents,
443
+ eventData
444
+ ]);
445
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@driveflux/web-analytics",
3
+ "version": "1.0.2",
4
+ "type": "module",
5
+ "main": "./dist/cjs/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ "./track": {
10
+ "import": "./dist/track.js",
11
+ "require": "./dist/cjs/track.js",
12
+ "types": "./dist/track.d.ts"
13
+ }
14
+ },
15
+ "dependencies": {
16
+ "lodash": "^4.17.21"
17
+ },
18
+ "devDependencies": {
19
+ "@driveflux/eslint-config": "1.0.7",
20
+ "@driveflux/fab": "1.0.8",
21
+ "@driveflux/tsconfig": "1.0.2",
22
+ "@next/third-parties": "^14.2.3",
23
+ "@swc/cli": "^0.3.12",
24
+ "@swc/core": "1.6.3",
25
+ "@types/lodash": "^4.17.5",
26
+ "@types/node": "^20.14.5",
27
+ "del-cli": "^5.1.0",
28
+ "eslint": "9.5.0",
29
+ "next": "14.2.4",
30
+ "typescript": "^5.4.5"
31
+ },
32
+ "scripts": {
33
+ "build": "fab",
34
+ "clean": "fab clean",
35
+ "types": "tsc --noEmit",
36
+ "lint": "eslint \"src/**/*.ts\""
37
+ }
38
+ }
package/src/track.ts ADDED
@@ -0,0 +1,209 @@
1
+ import { sendGTMEvent } from '@next/third-parties/google'
2
+ import isEqual from 'lodash/isEqual'
3
+ import { useRouter } from 'next/router'
4
+ import { useCallback, useEffect, useRef } from 'react'
5
+
6
+ declare global {
7
+ interface Window {
8
+ fluxDataLayer?: Object[];
9
+ }
10
+ }
11
+
12
+ interface Address {
13
+ name?: string | null
14
+ street1: string
15
+ street2?: string | null
16
+ city: string
17
+ state: string
18
+ country: string
19
+ postalCode: string
20
+ }
21
+
22
+ interface UserLikeData {
23
+ id: string
24
+ email: string
25
+ phoneNumber?: string | null
26
+ firstName?: string | null
27
+ lastName?: string | null
28
+ addresses?: {
29
+ home?: Address | null
30
+ billing?: Address | null
31
+ } | null
32
+ }
33
+
34
+ interface TrackingUserData {
35
+ user_id: string
36
+ email: string
37
+ phone_number?: string
38
+ address?: {
39
+ first_name?: string
40
+ last_name?: string
41
+ street?: string
42
+ city?: string
43
+ region?: string
44
+ postal_code?: string
45
+ country?: string
46
+ }
47
+ }
48
+
49
+ const eventsQueue = new Set<Record<string, any>>()
50
+
51
+ const drainEvents = () => {
52
+ const events = [...eventsQueue]
53
+ eventsQueue.clear()
54
+ events.forEach((event) => {
55
+ sendGTMEvent(event)
56
+ })
57
+ }
58
+
59
+ export function getGA4ClientId() {
60
+ if (typeof document === 'undefined') {
61
+ return null
62
+ }
63
+
64
+ const cookies = document.cookie.split(';')
65
+ const gaCookie = cookies.find((cookie) => cookie.trim().startsWith('_ga='))
66
+ if (gaCookie) {
67
+ const matches = gaCookie.split('.')
68
+ if (matches.length > 2) {
69
+ return `${matches[2]}.${matches[3]}` // Returns the GA4 client ID
70
+ }
71
+ }
72
+ return null // If no GA cookie is found or format is unexpected
73
+ }
74
+
75
+ async function hashData(input: string) {
76
+ const encoder = new TextEncoder()
77
+ const data = encoder.encode(input)
78
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data)
79
+ const hashArray = Array.from(new Uint8Array(hashBuffer)) // Convert buffer to byte array
80
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('') // Convert bytes to hex string
81
+ return hashHex
82
+ }
83
+
84
+ const convertUserData = async (dataInput: UserLikeData): Promise<TrackingUserData> => {
85
+ const userAddress = dataInput.addresses?.home || dataInput.addresses?.billing
86
+ return {
87
+ user_id: dataInput.id,
88
+ email: await hashData(dataInput.email),
89
+ phone_number: dataInput.phoneNumber ? await hashData(dataInput.phoneNumber) : undefined,
90
+ address: userAddress
91
+ ? {
92
+ first_name: dataInput.firstName ? await hashData(dataInput.firstName) : undefined,
93
+ last_name: dataInput.lastName ? await hashData(dataInput.lastName) : undefined,
94
+ street: [userAddress.street1, userAddress.street2].join(', '),
95
+ city: userAddress.city,
96
+ region: userAddress.state,
97
+ postal_code: userAddress.postalCode,
98
+ country: userAddress.country
99
+ }
100
+ : undefined
101
+ }
102
+ }
103
+
104
+ export const useTrackEvent = () => {
105
+ const userData = useRef<TrackingUserData>()
106
+
107
+ const track = useCallback((eventName: string, eventData?: Record<string, any>) => {
108
+ console.log('🚨🚨🚨 firing event', eventName, eventData)
109
+
110
+ if (!eventName) {
111
+ console.error('No event name provided')
112
+ return
113
+ }
114
+
115
+ const event = {
116
+ event: eventName,
117
+ ...(userData.current
118
+ ? {
119
+ user_data: userData.current
120
+ }
121
+ : {}),
122
+ ...eventData
123
+ }
124
+
125
+ if(!window.fluxDataLayer) {
126
+ eventsQueue.add(event)
127
+ return
128
+ }
129
+
130
+
131
+ sendGTMEvent(event)
132
+ }, [])
133
+
134
+ const setUserData = useCallback(async (dataInput: UserLikeData) => {
135
+ userData.current = await convertUserData(dataInput)
136
+ localStorage.setItem('ud', Buffer.from(JSON.stringify(userData.current), 'utf8').toString('base64'))
137
+ }, [])
138
+
139
+ useEffect(() => {
140
+ const interval = setInterval(() => {
141
+ if (window.fluxDataLayer) {
142
+ drainEvents()
143
+ clearInterval(interval)
144
+ }
145
+ }, 100)
146
+
147
+ return () => clearInterval(interval)
148
+ }, [])
149
+
150
+ useEffect(() => {
151
+ const ud = localStorage.getItem('ud')
152
+ if (!ud) {
153
+ return
154
+ }
155
+ try {
156
+ const json = Buffer.from(ud, 'base64').toString('utf8')
157
+ userData.current = JSON.parse(json)
158
+ } catch (e) {}
159
+ }, [])
160
+
161
+ return {
162
+ track,
163
+ setUserData
164
+ }
165
+ }
166
+
167
+ export const useAutoTrackEvent = (eventName: string, eventData?: Record<string, any>) => {
168
+ const router = useRouter()
169
+ const canFireEvents = router.isReady
170
+ const previousEventData = useRef<Record<string, any> | undefined>()
171
+ const firedFirstEvent = useRef(false)
172
+
173
+ useEffect(() => {
174
+ const interval = setInterval(() => {
175
+ if (window.fluxDataLayer) {
176
+ drainEvents()
177
+ clearInterval(interval)
178
+ }
179
+ }, 100)
180
+
181
+ return () => clearInterval(interval)
182
+ }, [])
183
+
184
+ useEffect(() => {
185
+ if (!canFireEvents) {
186
+ return
187
+ }
188
+
189
+ if (isEqual(previousEventData.current, eventData) && firedFirstEvent.current) {
190
+ return
191
+ }
192
+
193
+ firedFirstEvent.current = true
194
+ console.log('🚨🚨🚨 firing event', eventName, eventData)
195
+ const event = {
196
+ event: eventName,
197
+ ...eventData
198
+ }
199
+
200
+ if(!window.fluxDataLayer) {
201
+ eventsQueue.add(event)
202
+ return
203
+ }
204
+
205
+ sendGTMEvent(event)
206
+
207
+ previousEventData.current = eventData
208
+ }, [canFireEvents, eventData])
209
+ }
package/track.cjs ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/cjs/track.js')
package/track.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/track'
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@driveflux/tsconfig/frontend.json",
4
+ "compilerOptions": {
5
+ "rootDir": "src",
6
+ "outDir": "./dist"
7
+ },
8
+ "include": [
9
+ "src/**/*.ts"
10
+ ],
11
+ "exclude": ["node_modules"]
12
+ }