@codecademy/tracking 1.0.2 → 1.0.3-alpha.37044bcdd.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/dist/README.md +100 -0
- package/dist/events/index.d.ts +3 -3
- package/dist/events/track.d.ts +12 -12
- package/dist/events/types.d.ts +250 -250
- package/dist/events/user.d.ts +2 -2
- package/dist/index.cjs +711 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +595 -3
- package/dist/integrations/conditionallyLoadAnalytics.d.ts +9 -9
- package/dist/integrations/consent.d.ts +9 -9
- package/dist/integrations/device.d.ts +13 -13
- package/dist/integrations/fetchDestinationsForWriteKey.d.ts +6 -6
- package/dist/integrations/getConsentDecision.d.ts +8 -8
- package/dist/integrations/index.d.ts +31 -31
- package/dist/integrations/mapDestinations.d.ts +13 -13
- package/dist/integrations/onetrust.d.ts +6 -6
- package/dist/integrations/runSegmentSnippet.d.ts +7 -7
- package/dist/integrations/types.d.ts +24 -24
- package/dist/package.json +21 -0
- package/package.json +2 -2
- package/dist/events/index.js +0 -3
- package/dist/events/track.js +0 -115
- package/dist/events/types.js +0 -1
- package/dist/events/user.js +0 -41
- package/dist/integrations/conditionallyLoadAnalytics.js +0 -27
- package/dist/integrations/consent.js +0 -11
- package/dist/integrations/device.js +0 -25
- package/dist/integrations/fetchDestinationsForWriteKey.js +0 -88
- package/dist/integrations/getConsentDecision.js +0 -32
- package/dist/integrations/index.js +0 -90
- package/dist/integrations/mapDestinations.js +0 -55
- package/dist/integrations/onetrust.js +0 -53
- package/dist/integrations/runSegmentSnippet.js +0 -64
- package/dist/integrations/types.js +0 -1
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,711 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @returns Whether the site is running both in ChromeOS and in PWA mode.
|
|
7
|
+
*/
|
|
8
|
+
const isChromeOSPWA = () => isChromeOS() && 'getDigitalGoodsService' in window && // https://stackoverflow.com/questions/41742390/javascript-to-check-if-pwa-or-mobile-web
|
|
9
|
+
window.matchMedia('(display-mode: standalone)').matches;
|
|
10
|
+
/**
|
|
11
|
+
* @returns Whether the site is running in ChromeOS
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const isChromeOS = () => typeof navigator !== 'undefined' && // https://stackoverflow.com/questions/29657165/detecting-chrome-os-with-javascript
|
|
15
|
+
/\bCrOS\b/.test(navigator.userAgent);
|
|
16
|
+
var ClientTypes;
|
|
17
|
+
|
|
18
|
+
(function (ClientTypes) {
|
|
19
|
+
ClientTypes["PWA"] = "pwa";
|
|
20
|
+
ClientTypes["Default"] = "default";
|
|
21
|
+
})(ClientTypes || (ClientTypes = {}));
|
|
22
|
+
|
|
23
|
+
const getClientType = () => isChromeOSPWA() ? ClientTypes.PWA : ClientTypes.Default;
|
|
24
|
+
|
|
25
|
+
const conditionallyLoadAnalytics = ({
|
|
26
|
+
analytics,
|
|
27
|
+
destinationPreferences,
|
|
28
|
+
identifyPreferences,
|
|
29
|
+
user,
|
|
30
|
+
writeKey
|
|
31
|
+
}) => {
|
|
32
|
+
if (analytics.initialize) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
analytics.load(writeKey, {
|
|
37
|
+
integrations: destinationPreferences
|
|
38
|
+
});
|
|
39
|
+
analytics.page();
|
|
40
|
+
|
|
41
|
+
if (user) {
|
|
42
|
+
const identifyParams = {
|
|
43
|
+
email: user.email,
|
|
44
|
+
client: getClientType()
|
|
45
|
+
};
|
|
46
|
+
analytics.identify(user.id, identifyParams, {
|
|
47
|
+
integrations: identifyPreferences
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function _await$2(value, then, direct) {
|
|
53
|
+
if (direct) {
|
|
54
|
+
return then ? then(value) : value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!value || !value.then) {
|
|
58
|
+
value = Promise.resolve(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return then ? value.then(then) : value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const knownFetchFailures = ['Failed to fetch', 'Load failed', 'NetworkError when attempting to fetch resource', 'Resource blocked by content blocker'];
|
|
65
|
+
|
|
66
|
+
function _catch(body, recover) {
|
|
67
|
+
try {
|
|
68
|
+
var result = body();
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return recover(e);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (result && result.then) {
|
|
74
|
+
return result.then(void 0, recover);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function _async$3(f) {
|
|
81
|
+
return function () {
|
|
82
|
+
for (var args = [], i = 0; i < arguments.length; i++) {
|
|
83
|
+
args[i] = arguments[i];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
return Promise.resolve(f.apply(this, args));
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return Promise.reject(e);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const fetchDestinationsForWriteKey = _async$3(function ({
|
|
95
|
+
writeKey,
|
|
96
|
+
onError
|
|
97
|
+
}) {
|
|
98
|
+
const filteredOnError = error => {
|
|
99
|
+
if (!knownFetchFailures.some(failure => error.includes(failure))) {
|
|
100
|
+
onError(error);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return _catch(function () {
|
|
105
|
+
return _await$2(fetch(`https://cdn.segment.com/v1/projects/${writeKey}/integrations`), function (response) {
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
filteredOnError(`Failed to fetch integrations for write key ${writeKey}: HTTP ${response.status} ${response.statusText}`);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return _await$2(response.json(), function (destinations) {
|
|
112
|
+
for (const destination of destinations) {
|
|
113
|
+
destination.id = destination.creationName;
|
|
114
|
+
delete destination.creationName;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return destinations;
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}, function (error) {
|
|
121
|
+
filteredOnError(`Unknown error fetching Segment destinations for write key ${writeKey}: ${error}`);
|
|
122
|
+
return [];
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @see https://www.notion.so/codecademy/GDPR-Compliance-141ebcc7ffa542daa0da56e35f482b41
|
|
128
|
+
*/
|
|
129
|
+
var Consent;
|
|
130
|
+
|
|
131
|
+
(function (Consent) {
|
|
132
|
+
Consent["Functional"] = "C0003";
|
|
133
|
+
Consent["Performance"] = "C0002";
|
|
134
|
+
Consent["StrictlyNecessary"] = "C0001";
|
|
135
|
+
Consent["Targeting"] = "C0004";
|
|
136
|
+
})(Consent || (Consent = {}));
|
|
137
|
+
|
|
138
|
+
const OPT_OUT_DATALAYER_VAR = 'user_opted_out_external_tracking';
|
|
139
|
+
const getConsentDecision = ({
|
|
140
|
+
scope,
|
|
141
|
+
optedOutExternalTracking
|
|
142
|
+
}) => {
|
|
143
|
+
let consentDecision = [];
|
|
144
|
+
|
|
145
|
+
if (typeof scope.OnetrustActiveGroups === 'string') {
|
|
146
|
+
consentDecision = scope.OnetrustActiveGroups.split(',').filter(Boolean);
|
|
147
|
+
} else if (scope.OnetrustActiveGroups) {
|
|
148
|
+
consentDecision = scope.OnetrustActiveGroups;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (optedOutExternalTracking) {
|
|
152
|
+
var _scope$dataLayer;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* If user has already opted out of everything but the essentials
|
|
156
|
+
* don't force them to consent to Functional & Performance trackers
|
|
157
|
+
*/
|
|
158
|
+
if (consentDecision.length > 2) {
|
|
159
|
+
consentDecision = [Consent.StrictlyNecessary, Consent.Functional, Consent.Performance];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
(_scope$dataLayer = scope.dataLayer) != null ? _scope$dataLayer : scope.dataLayer = [];
|
|
163
|
+
scope.dataLayer.push({
|
|
164
|
+
[OPT_OUT_DATALAYER_VAR]: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return consentDecision;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const targetingCategories = ['Advertising', 'Attribution', 'Email Marketing'];
|
|
172
|
+
const performanceCategories = ['Analytics', 'Customer Success', 'Surveys', 'Heatmaps & Recording'];
|
|
173
|
+
const functionalCategories = ['SMS & Push Notifications'];
|
|
174
|
+
/**
|
|
175
|
+
* @see https://www.notion.so/codecademy/GDPR-Compliance-141ebcc7ffa542daa0da56e35f482b41
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
const mapDestinations = ({
|
|
179
|
+
consentDecision: _consentDecision = [Consent.StrictlyNecessary],
|
|
180
|
+
destinations
|
|
181
|
+
}) => {
|
|
182
|
+
const destinationPreferences = Object.assign({
|
|
183
|
+
'Segment.io': _consentDecision.includes(Consent.Functional)
|
|
184
|
+
}, ...destinations.map(dest => {
|
|
185
|
+
if (targetingCategories.includes(dest.category)) {
|
|
186
|
+
return {
|
|
187
|
+
[dest.id]: _consentDecision.includes(Consent.Targeting)
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (performanceCategories.includes(dest.category)) {
|
|
192
|
+
return {
|
|
193
|
+
[dest.id]: _consentDecision.includes(Consent.Performance)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (functionalCategories.includes(dest.category)) {
|
|
198
|
+
return {
|
|
199
|
+
[dest.id]: _consentDecision.includes(Consent.Functional)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
[dest.id]: true
|
|
205
|
+
};
|
|
206
|
+
}));
|
|
207
|
+
const identifyPreferences = {
|
|
208
|
+
All: false,
|
|
209
|
+
FullStory: _consentDecision.includes(Consent.Performance),
|
|
210
|
+
Hindsight: _consentDecision.includes(Consent.Targeting),
|
|
211
|
+
UserLeap: _consentDecision.includes(Consent.Performance)
|
|
212
|
+
};
|
|
213
|
+
return {
|
|
214
|
+
destinationPreferences,
|
|
215
|
+
identifyPreferences
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
function _async$2(f) {
|
|
220
|
+
return function () {
|
|
221
|
+
for (var args = [], i = 0; i < arguments.length; i++) {
|
|
222
|
+
args[i] = arguments[i];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
return Promise.resolve(f.apply(this, args));
|
|
227
|
+
} catch (e) {
|
|
228
|
+
return Promise.reject(e);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
} // For now, these three values duplicate theme colors from gamut-styles
|
|
232
|
+
// We don't want to take a full dependency on that package here...
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
const initializeOneTrust = _async$2(function ({
|
|
236
|
+
production,
|
|
237
|
+
scope
|
|
238
|
+
}) {
|
|
239
|
+
const script = document.createElement('script');
|
|
240
|
+
script.setAttribute('async', 'true');
|
|
241
|
+
script.setAttribute('src', 'https://cdn.cookielaw.org/scripttemplates/otSDKStub.js');
|
|
242
|
+
script.setAttribute('type', 'text/javascript');
|
|
243
|
+
script.setAttribute('data-domain-script', `cfa7b129-f37b-4f5a-9991-3f75ba7b85fb${production ? '' : '-test'}`);
|
|
244
|
+
document.body.appendChild(script);
|
|
245
|
+
const style = document.createElement('style');
|
|
246
|
+
style.textContent = rawStyles;
|
|
247
|
+
document.body.appendChild(style);
|
|
248
|
+
return new Promise(resolve => {
|
|
249
|
+
scope.OptanonWrapper = () => {
|
|
250
|
+
var _scope$dataLayer, _script$parentNode;
|
|
251
|
+
|
|
252
|
+
(_scope$dataLayer = scope.dataLayer) != null ? _scope$dataLayer : scope.dataLayer = [];
|
|
253
|
+
scope.dataLayer.push({
|
|
254
|
+
event: 'OneTrustGroupsUpdated'
|
|
255
|
+
});
|
|
256
|
+
resolve();
|
|
257
|
+
(_script$parentNode = script.parentNode) == null ? void 0 : _script$parentNode.removeChild(script);
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
const rawStyles = `
|
|
262
|
+
:root {
|
|
263
|
+
--onetrust-brand-purple: #3A10E5;
|
|
264
|
+
--onetrust-color-gray-500: #828285;
|
|
265
|
+
--onetrust-color-white: #fff;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
#onetrust-banner-sdk {
|
|
269
|
+
padding: 1rem !important;
|
|
270
|
+
}
|
|
271
|
+
#onetrust-banner-sdk > .ot-sdk-container {
|
|
272
|
+
width: 100% !important;
|
|
273
|
+
}
|
|
274
|
+
#onetrust-banner-sdk > .ot-sdk-container > .ot-sdk-row {
|
|
275
|
+
display: flex !important;
|
|
276
|
+
flex-direction: column !important;
|
|
277
|
+
align-items: center !important;
|
|
278
|
+
max-width: 1436px !important;
|
|
279
|
+
margin: 0 auto !important;
|
|
280
|
+
}
|
|
281
|
+
#onetrust-banner-sdk > .ot-sdk-container > .ot-sdk-row:after {
|
|
282
|
+
content: none !important;
|
|
283
|
+
}
|
|
284
|
+
#onetrust-group-container {
|
|
285
|
+
display: flex !important;
|
|
286
|
+
justify-content: center;
|
|
287
|
+
float: none !important;
|
|
288
|
+
width: 100% !important;
|
|
289
|
+
max-width: 1148px !important;
|
|
290
|
+
margin-left: 0 !important;
|
|
291
|
+
margin-bottom: 0.625rem !important;
|
|
292
|
+
}
|
|
293
|
+
#onetrust-policy,
|
|
294
|
+
#onetrust-policy-text {
|
|
295
|
+
margin: 0 !important;
|
|
296
|
+
font-size: 0.875rem !important;
|
|
297
|
+
line-height: 1.375rem !important;
|
|
298
|
+
text-align: center !important;
|
|
299
|
+
float: none !important;
|
|
300
|
+
}
|
|
301
|
+
#onetrust-policy-text a {
|
|
302
|
+
text-decoration: none;
|
|
303
|
+
line-height: 26px !important;
|
|
304
|
+
margin-left: 0 !important;
|
|
305
|
+
}
|
|
306
|
+
#onetrust-button-group-parent {
|
|
307
|
+
position: relative !important;
|
|
308
|
+
top: initial !important;
|
|
309
|
+
left: initial !important;
|
|
310
|
+
transform: initial !important;
|
|
311
|
+
width: 264px !important;
|
|
312
|
+
margin: 0 !important;
|
|
313
|
+
padding: 0 !important;
|
|
314
|
+
float: none !important;
|
|
315
|
+
}
|
|
316
|
+
#onetrust-button-group {
|
|
317
|
+
display: flex !important;
|
|
318
|
+
margin: 0 !important;
|
|
319
|
+
}
|
|
320
|
+
#onetrust-pc-btn-handler, #onetrust-accept-btn-handler {
|
|
321
|
+
min-width: initial !important;
|
|
322
|
+
padding: 0.375rem 1rem !important;
|
|
323
|
+
margin: 0 !important;
|
|
324
|
+
opacity: 1 !important;
|
|
325
|
+
border-radius: 2px !important;
|
|
326
|
+
line-height: 1.5 !important;
|
|
327
|
+
user-select: none !important;
|
|
328
|
+
font-size: 1rem !important;
|
|
329
|
+
}
|
|
330
|
+
#onetrust-pc-btn-handler:focus, #onetrust-accept-btn-handler:focus {
|
|
331
|
+
box-shadow: 0 0 0 2px var(--onetrust-color-white), 0 0 0 4px var(--onetrust-brand-purple);
|
|
332
|
+
text-decoration: none !important;
|
|
333
|
+
outline: none !important;
|
|
334
|
+
}
|
|
335
|
+
#onetrust-pc-btn-handler{
|
|
336
|
+
color: var(--onetrust-brand-purple) !important;
|
|
337
|
+
border: 1px solid var(--onetrust-brand-purple)!important;
|
|
338
|
+
background: var(--onetrust-color-white) !important
|
|
339
|
+
}
|
|
340
|
+
#onetrust-accept-btn-handler {
|
|
341
|
+
color: var(--onetrust-color-white) !important;
|
|
342
|
+
background: var(--onetrust-brand-purple)!important;
|
|
343
|
+
margin-left: 1rem !important;
|
|
344
|
+
}
|
|
345
|
+
#onetrust-close-btn-container {
|
|
346
|
+
display: none !important;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.pc-logo {
|
|
350
|
+
display: none !important;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
#accept-recommended-btn-handler,
|
|
354
|
+
.ot-pc-refuse-all-handler,
|
|
355
|
+
.save-preference-btn-handler {
|
|
356
|
+
margin-left: 4px !important;
|
|
357
|
+
font-size: 14px !important;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
#accept-recommended-btn-handler:focus,
|
|
361
|
+
#onetrust-pc-sdk .ot-pc-refuse-all-handler:focus,
|
|
362
|
+
#onetrust-pc-sdk .save-preference-btn-handler:focus {
|
|
363
|
+
box-shadow: 0 0 0 2px var(--onetrust-color-white), 0 0 0 4px var(--onetrust-brand-purple);
|
|
364
|
+
text-decoration: none !important;
|
|
365
|
+
outline: none !important;
|
|
366
|
+
opacity: 1 !important;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.ot-switch-label {
|
|
370
|
+
border: 1px solid var(--onetrust-color-gray-500) !important;
|
|
371
|
+
background-color: var(--onetrust-color-gray-500) !important;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.ot-switch-nob {
|
|
375
|
+
background: var(--onetrust-color-white) !important;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.ot-switch-inner:before {
|
|
379
|
+
background-color: var(--onetrust-brand-purple) !important;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.switch-checkbox:checked+.ot-switch-label .ot-switch-nob {
|
|
383
|
+
border-color: var(--onetrust-brand-purple) !important;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.ot-pc-footer-logo {
|
|
387
|
+
display: none !important;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
#onetrust-banner-sdk>.ot-sdk-container {
|
|
391
|
+
overflow: visible !important;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@media (max-width: 30rem) {
|
|
395
|
+
#accept-recommended-btn-handler,
|
|
396
|
+
.ot-pc-refuse-all-handler,
|
|
397
|
+
.save-preference-btn-handler {
|
|
398
|
+
width: 96% !important;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
@media (min-width: 37.5rem) {
|
|
403
|
+
#onetrust-banner-sdk {
|
|
404
|
+
padding: 0.875rem 1rem !important;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
@media (min-width: 48rem) {
|
|
408
|
+
#onetrust-banner-sdk {
|
|
409
|
+
padding: 0.875rem 1.25rem !important;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
@media (min-width: 1650px) {
|
|
413
|
+
#onetrust-banner-sdk > .ot-sdk-container > .ot-sdk-row {
|
|
414
|
+
flex-direction: row !important;
|
|
415
|
+
justify-content: space-between !important;
|
|
416
|
+
}
|
|
417
|
+
#onetrust-group-container {
|
|
418
|
+
margin-bottom: 0 !important;
|
|
419
|
+
}
|
|
420
|
+
#onetrust-button-group {
|
|
421
|
+
flex-direction: row !important;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
`;
|
|
425
|
+
|
|
426
|
+
// @ts-nocheck
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* This code is copypasta from the Segment documentation.
|
|
430
|
+
* It creates the global analytics object and loads the Segment Analytics API that uses it.
|
|
431
|
+
*
|
|
432
|
+
* @see https://segment.com/docs/connections/sources/catalog/libraries/website/javascript/quickstart/#step-2-copy-the-segment-snippet
|
|
433
|
+
*/
|
|
434
|
+
const runSegmentSnippet = () => {
|
|
435
|
+
var _window;
|
|
436
|
+
|
|
437
|
+
// Create a queue, but don't obliterate an existing one!
|
|
438
|
+
(_window = window).analytics || (_window.analytics = []);
|
|
439
|
+
const {
|
|
440
|
+
analytics
|
|
441
|
+
} = window; // If the real analytics.js is already on the page return.
|
|
442
|
+
|
|
443
|
+
if (analytics.initialize) return; // If the snippet was invoked already show an error.
|
|
444
|
+
|
|
445
|
+
if (analytics.invoked) {
|
|
446
|
+
console.error('Segment snippet included twice.');
|
|
447
|
+
return;
|
|
448
|
+
} // Invoked flag, to make sure the snippet
|
|
449
|
+
// is never invoked twice.
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
analytics.invoked = true; // A list of the methods in Analytics.js to stub.
|
|
453
|
+
|
|
454
|
+
analytics.methods = ['trackSubmit', 'trackClick', 'trackLink', 'trackForm', 'pageview', 'identify', 'reset', 'group', 'track', 'ready', 'alias', 'debug', 'page', 'once', 'off', 'on', 'addSourceMiddleware', 'addIntegrationMiddleware', 'setAnonymousId', 'addDestinationMiddleware']; // Define a factory to create stubs. These are placeholders
|
|
455
|
+
// for methods in Analytics.js so that you never have to wait
|
|
456
|
+
// for it to load to actually record data. The `method` is
|
|
457
|
+
// stored as the first argument, so we can replay the data.
|
|
458
|
+
|
|
459
|
+
analytics.factory = function (method) {
|
|
460
|
+
return function () {
|
|
461
|
+
const args = Array.prototype.slice.call(arguments);
|
|
462
|
+
args.unshift(method);
|
|
463
|
+
analytics.push(args);
|
|
464
|
+
return analytics;
|
|
465
|
+
};
|
|
466
|
+
}; // For each of our methods, generate a queueing stub.
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
for (let i = 0; i < analytics.methods.length; i += 1) {
|
|
470
|
+
const key = analytics.methods[i];
|
|
471
|
+
analytics[key] = analytics.factory(key);
|
|
472
|
+
} // Define a method to load Analytics.js from our CDN,
|
|
473
|
+
// and that will be sure to only ever load it once.
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
analytics.load = function (key, options) {
|
|
477
|
+
// Create an async script element based on your key.
|
|
478
|
+
const script = document.createElement('script');
|
|
479
|
+
script.type = 'text/javascript';
|
|
480
|
+
script.async = true;
|
|
481
|
+
script.src = 'https://cdn.segment.com/analytics.js/v1/' + key + '/analytics.min.js'; // Insert our script next to the first script element.
|
|
482
|
+
|
|
483
|
+
const first = document.getElementsByTagName('script')[0];
|
|
484
|
+
first.parentNode.insertBefore(script, first);
|
|
485
|
+
analytics._loadOptions = options;
|
|
486
|
+
}; // Add a version to keep track of what's in the wild.
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
analytics.SNIPPET_VERSION = '4.1.0';
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* @see README.md for details and usage.
|
|
494
|
+
*/
|
|
495
|
+
|
|
496
|
+
function _await$1(value, then, direct) {
|
|
497
|
+
if (direct) {
|
|
498
|
+
return then ? then(value) : value;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (!value || !value.then) {
|
|
502
|
+
value = Promise.resolve(value);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
return then ? value.then(then) : value;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function _async$1(f) {
|
|
509
|
+
return function () {
|
|
510
|
+
for (var args = [], i = 0; i < arguments.length; i++) {
|
|
511
|
+
args[i] = arguments[i];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
return Promise.resolve(f.apply(this, args));
|
|
516
|
+
} catch (e) {
|
|
517
|
+
return Promise.reject(e);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const initializeTrackingIntegrations = _async$1(function ({
|
|
523
|
+
onError,
|
|
524
|
+
production,
|
|
525
|
+
scope,
|
|
526
|
+
user,
|
|
527
|
+
optedOutExternalTracking,
|
|
528
|
+
writeKey
|
|
529
|
+
}) {
|
|
530
|
+
// 1. Wait 1000ms to allow any other post-hydration logic to run first
|
|
531
|
+
return _await$1(new Promise(resolve => setTimeout(resolve, 1000)), function () {
|
|
532
|
+
// 2. Load in OneTrust's banner and wait for its `OptanonWrapper` callback
|
|
533
|
+
return _await$1(initializeOneTrust({
|
|
534
|
+
scope,
|
|
535
|
+
production
|
|
536
|
+
}), function () {
|
|
537
|
+
// 3. Segment's copy-and-paste snippet is run to load the Segment global library
|
|
538
|
+
runSegmentSnippet(); // 4. Destination integrations for Segment are fetched
|
|
539
|
+
|
|
540
|
+
return _await$1(fetchDestinationsForWriteKey({
|
|
541
|
+
onError,
|
|
542
|
+
writeKey
|
|
543
|
+
}), function (destinations) {
|
|
544
|
+
if (!destinations) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const consentDecision = getConsentDecision({
|
|
549
|
+
scope,
|
|
550
|
+
optedOutExternalTracking
|
|
551
|
+
}); // 5. Those integrations are compared against the user's consent decisions into a list of allowed destinations
|
|
552
|
+
|
|
553
|
+
const {
|
|
554
|
+
destinationPreferences,
|
|
555
|
+
identifyPreferences
|
|
556
|
+
} = mapDestinations({
|
|
557
|
+
consentDecision,
|
|
558
|
+
destinations
|
|
559
|
+
}); // 6. We load only those allowed destinations using Segment's `analytics.load`
|
|
560
|
+
|
|
561
|
+
conditionallyLoadAnalytics({
|
|
562
|
+
analytics: scope.analytics,
|
|
563
|
+
destinationPreferences,
|
|
564
|
+
identifyPreferences,
|
|
565
|
+
user,
|
|
566
|
+
writeKey
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
function _extends() {
|
|
574
|
+
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
575
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
576
|
+
var source = arguments[i];
|
|
577
|
+
|
|
578
|
+
for (var key in source) {
|
|
579
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
580
|
+
target[key] = source[key];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return target;
|
|
586
|
+
};
|
|
587
|
+
return _extends.apply(this, arguments);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const browserSupportsKeepalive = () => 'keepalive' in window.Request.prototype;
|
|
591
|
+
|
|
592
|
+
const createTracker = ({
|
|
593
|
+
apiBaseUrl,
|
|
594
|
+
verbose
|
|
595
|
+
}) => {
|
|
596
|
+
const beacon = (endpoint, data) => {
|
|
597
|
+
const uri = new URL(endpoint, apiBaseUrl).toString();
|
|
598
|
+
const form = new FormData();
|
|
599
|
+
|
|
600
|
+
for (const [k, v] of Object.entries(data)) {
|
|
601
|
+
form.append(k, v.toString());
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
// Firefox allows users to disable navigator.sendBeacon, and very old Safari versions don't have it.
|
|
606
|
+
// [WEB-1700]: Additionally, Chrome 79-80 gives "Illegal invocation" with ?., so through 2022 we should support them.
|
|
607
|
+
// It seems similar to this: https://github.com/vercel/next.js/issues/23856
|
|
608
|
+
if (navigator.sendBeacon && navigator.sendBeacon(uri, form)) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
} catch (_unused) {// Even with the proper scoping, Chrome 79-80 still gives "Illegal invocation" crashes. Sigh.
|
|
612
|
+
} // Either way, we fall back to standard fetch if sendBeacon fails.
|
|
613
|
+
// We don't mind this rejecting with an error because it's tracking, and we'll know if that starts to fail.
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
window.fetch(uri, _extends({
|
|
617
|
+
method: 'POST',
|
|
618
|
+
body: form
|
|
619
|
+
}, browserSupportsKeepalive() && {
|
|
620
|
+
keepalive: true
|
|
621
|
+
}));
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
const event = (category, event, userData, options = {}) => {
|
|
625
|
+
const properties = _extends({}, userData, {
|
|
626
|
+
fullpath: window.location.pathname + window.location.search,
|
|
627
|
+
search: window.location.search,
|
|
628
|
+
path: window.location.pathname,
|
|
629
|
+
title: window.document.title,
|
|
630
|
+
url: window.location.href,
|
|
631
|
+
referrer: userData.referrer || window.document.referrer,
|
|
632
|
+
client: getClientType()
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
if (verbose) {
|
|
636
|
+
console.groupCollapsed(`%cTracking Event Fired: ${category}:${event}`, 'color: #4b35ef; font-style: italic;');
|
|
637
|
+
console.log({
|
|
638
|
+
category,
|
|
639
|
+
event,
|
|
640
|
+
properties
|
|
641
|
+
});
|
|
642
|
+
console.groupEnd();
|
|
643
|
+
} // This allows the UTM query params to get registered in the user session.
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
const queryParams = window.location.search;
|
|
647
|
+
beacon(`/analytics/${category}${queryParams}`, {
|
|
648
|
+
category,
|
|
649
|
+
event,
|
|
650
|
+
properties: JSON.stringify(properties),
|
|
651
|
+
gdpr_safe: `${options.gdprSafe}`
|
|
652
|
+
});
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
return {
|
|
656
|
+
event,
|
|
657
|
+
click: data => event('user', 'click', data),
|
|
658
|
+
impression: data => event('user', 'impression', data),
|
|
659
|
+
visit: data => event('user', 'visit', data),
|
|
660
|
+
pushDataLayerEvent: eventName => {
|
|
661
|
+
var _window;
|
|
662
|
+
|
|
663
|
+
((_window = window).dataLayer || (_window.dataLayer = [])).push({
|
|
664
|
+
event: eventName
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
|
|
670
|
+
function _await(value, then, direct) {
|
|
671
|
+
if (direct) {
|
|
672
|
+
return then ? then(value) : value;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (!value || !value.then) {
|
|
676
|
+
value = Promise.resolve(value);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
return then ? value.then(then) : value;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function _async(f) {
|
|
683
|
+
return function () {
|
|
684
|
+
for (var args = [], i = 0; i < arguments.length; i++) {
|
|
685
|
+
args[i] = arguments[i];
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
try {
|
|
689
|
+
return Promise.resolve(f.apply(this, args));
|
|
690
|
+
} catch (e) {
|
|
691
|
+
return Promise.reject(e);
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const fetchUser = _async(function (apiBaseUrl) {
|
|
697
|
+
return _await(fetch(`${apiBaseUrl}/users/web`, {
|
|
698
|
+
method: 'GET',
|
|
699
|
+
headers: {
|
|
700
|
+
'Content-type': 'application/json',
|
|
701
|
+
Accept: 'application/json'
|
|
702
|
+
},
|
|
703
|
+
credentials: 'include'
|
|
704
|
+
}), function (response) {
|
|
705
|
+
return response.json();
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
exports.createTracker = createTracker;
|
|
710
|
+
exports.fetchUser = fetchUser;
|
|
711
|
+
exports.initializeTrackingIntegrations = initializeTrackingIntegrations;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './integrations';
|
|
2
|
-
export type { TrackingWindow } from './integrations/types';
|
|
3
|
-
export * from './events';
|
|
1
|
+
export * from './integrations';
|
|
2
|
+
export type { TrackingWindow } from './integrations/types';
|
|
3
|
+
export * from './events';
|