@splendidlabz/tracking 0.1.22 → 0.2.1
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/fb/consts.js +425 -0
- package/lib/fb/node.js +141 -0
- package/lib/fb/readme.md +54 -0
- package/lib/fb/utils.js +15 -337
- package/lib/fb/web.js +148 -0
- package/lib/fb/web.test.js +47 -0
- package/lib/posthog/node.js +140 -0
- package/lib/posthog/readme.md +17 -0
- package/lib/posthog/web.js +79 -0
- package/package.json +6 -6
- package/dist/fb.js +0 -949
- package/lib/fb/browser.js +0 -184
- package/lib/fb/browser.test.js +0 -127
- package/lib/fb/server.js +0 -73
- /package/lib/posthog/{CrossDomainTracking.svelte → old/CrossDomainTracking.svelte} +0 -0
- /package/lib/posthog/{cross-domain.js → old/cross-domain.js} +0 -0
- /package/lib/posthog/{index.js → old/index.js} +0 -0
package/dist/fb.js
DELETED
|
@@ -1,949 +0,0 @@
|
|
|
1
|
-
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.FB = factory());
|
|
5
|
-
})(this, (function () { 'use strict';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Gets a cookie by name
|
|
9
|
-
* Thanks to https://stackoverflow.com/a/21125098
|
|
10
|
-
* @param {string} name Cookie name
|
|
11
|
-
* @returns string Cookie value
|
|
12
|
-
*/
|
|
13
|
-
function getCookie(name) {
|
|
14
|
-
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
|
15
|
-
if (match) return match[2]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async function sha256Hash(string) {
|
|
19
|
-
const utf8 = new TextEncoder().encode(string);
|
|
20
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', utf8);
|
|
21
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
22
|
-
const hashHex = hashArray
|
|
23
|
-
.map(bytes => bytes.toString(16).padStart(2, '0'))
|
|
24
|
-
.join('');
|
|
25
|
-
return hashHex
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// For browsers only
|
|
29
|
-
// Check the node version for node
|
|
30
|
-
async function randomString(length = 10) {
|
|
31
|
-
return crypto.getRandomValues(new Uint32Array(length)).join('')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function isEmpty(obj) {
|
|
35
|
-
if (typeof obj === 'undefined') return true
|
|
36
|
-
if (obj === null) return true
|
|
37
|
-
if (Array.isArray(obj)) return obj.length === 0
|
|
38
|
-
return Object.keys(obj).length === 0
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Split on object up based on the keys provided.
|
|
43
|
-
* @param {Object} obj
|
|
44
|
-
* @param {Array} keys
|
|
45
|
-
* @returns
|
|
46
|
-
*/
|
|
47
|
-
function splitObject(obj, keys) {
|
|
48
|
-
const picked = {};
|
|
49
|
-
const omitted = { ...obj };
|
|
50
|
-
|
|
51
|
-
keys.forEach(key => {
|
|
52
|
-
if (key in obj) {
|
|
53
|
-
picked[key] = obj[key];
|
|
54
|
-
delete omitted[key];
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return { picked, omitted }
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function formatUserData(data) {
|
|
62
|
-
if (isEmpty(data)) return {}
|
|
63
|
-
|
|
64
|
-
// Lowercase all the values
|
|
65
|
-
for (const [key, value] of Object.entries(data)) {
|
|
66
|
-
data[key] = value.toLowerCase();
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const {
|
|
70
|
-
email,
|
|
71
|
-
phone,
|
|
72
|
-
firstName,
|
|
73
|
-
lastName,
|
|
74
|
-
birthdate,
|
|
75
|
-
gender,
|
|
76
|
-
city,
|
|
77
|
-
state,
|
|
78
|
-
zip,
|
|
79
|
-
country,
|
|
80
|
-
} = data;
|
|
81
|
-
|
|
82
|
-
const ret = {};
|
|
83
|
-
|
|
84
|
-
if (email) ret.em = await sha256Hash(email);
|
|
85
|
-
if (phone) ret.ph = await sha256Hash(phone);
|
|
86
|
-
if (firstName) ret.fn = await sha256Hash(firstName);
|
|
87
|
-
if (lastName) ret.ln = await sha256Hash(lastName);
|
|
88
|
-
if (birthdate) ret.db = await sha256Hash(birthdate);
|
|
89
|
-
if (gender) ret.ge = await sha256Hash(gender);
|
|
90
|
-
if (city) ret.ct = await sha256Hash(city.replace(' ', ''));
|
|
91
|
-
|
|
92
|
-
if (country) {
|
|
93
|
-
const c = COUNTRY_CODES[country];
|
|
94
|
-
ret.country = await sha256Hash(c);
|
|
95
|
-
|
|
96
|
-
if (c === 'us' && state) {
|
|
97
|
-
ret.st = await sha256Hash(US_STATE_CODES[state]);
|
|
98
|
-
}
|
|
99
|
-
if (c !== 'us' && state) {
|
|
100
|
-
ret.st = await sha256Hash(state.replace(' ', '').replace('-', ''));
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if (zip) ret.zp = await sha256Hash(zip);
|
|
104
|
-
return ret
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const US_STATE_CODES = {
|
|
108
|
-
alabama: 'al',
|
|
109
|
-
alaska: 'ak',
|
|
110
|
-
'american samoa': 'as',
|
|
111
|
-
arizona: 'az',
|
|
112
|
-
arkansas: 'ar',
|
|
113
|
-
'baker island': 'bi',
|
|
114
|
-
california: 'ca',
|
|
115
|
-
colorado: 'co',
|
|
116
|
-
connecticut: 'ct',
|
|
117
|
-
delaware: 'de',
|
|
118
|
-
'district of columbia': 'dc',
|
|
119
|
-
florida: 'fl',
|
|
120
|
-
'federated states of micronesia': 'fm',
|
|
121
|
-
georgia: 'ga',
|
|
122
|
-
guam: 'gu',
|
|
123
|
-
hawaii: 'hi',
|
|
124
|
-
'howland island': 'hi',
|
|
125
|
-
idaho: 'id',
|
|
126
|
-
illinois: 'il',
|
|
127
|
-
indiana: 'in',
|
|
128
|
-
iowa: 'ia',
|
|
129
|
-
'jarvis island': 'ji',
|
|
130
|
-
'johnston atoll': 'ja',
|
|
131
|
-
kansas: 'ks',
|
|
132
|
-
kentucky: 'ky',
|
|
133
|
-
'kingman reef': 'kr',
|
|
134
|
-
louisiana: 'la',
|
|
135
|
-
maine: 'me',
|
|
136
|
-
'marshall islands': 'mh',
|
|
137
|
-
maryland: 'md',
|
|
138
|
-
massachusetts: 'ma',
|
|
139
|
-
michigan: 'mi',
|
|
140
|
-
'midway islands': 'mi',
|
|
141
|
-
minnesota: 'mn',
|
|
142
|
-
mississippi: 'ms',
|
|
143
|
-
missouri: 'mo',
|
|
144
|
-
montana: 'mt',
|
|
145
|
-
'navassa island': 'ni',
|
|
146
|
-
nebraska: 'ne',
|
|
147
|
-
nevada: 'nv',
|
|
148
|
-
'new hampshire': 'nh',
|
|
149
|
-
'new jersey': 'nj',
|
|
150
|
-
'new mexico': 'nm',
|
|
151
|
-
'new york': 'ny',
|
|
152
|
-
'north carolina': 'nc',
|
|
153
|
-
'north dakota': 'nd',
|
|
154
|
-
'northern mariana islands': 'mp',
|
|
155
|
-
ohio: 'oh',
|
|
156
|
-
oklahoma: 'ok',
|
|
157
|
-
oregon: 'or',
|
|
158
|
-
palau: 'pw',
|
|
159
|
-
'palmyra atoll': 'pa',
|
|
160
|
-
pennsylvania: 'pa',
|
|
161
|
-
'puerto rico': 'pr',
|
|
162
|
-
'rhode island': 'ri',
|
|
163
|
-
'south carolina': 'sc',
|
|
164
|
-
'south dakota': 'sd',
|
|
165
|
-
tennessee: 'tn',
|
|
166
|
-
texas: 'tx',
|
|
167
|
-
'u.s. minor outlying islands': 'um',
|
|
168
|
-
utah: 'ut',
|
|
169
|
-
vermont: 'vt',
|
|
170
|
-
virginia: 'va',
|
|
171
|
-
'virgin islands of the u.s.': 'vi',
|
|
172
|
-
'wake island': 'wi',
|
|
173
|
-
washington: 'wa',
|
|
174
|
-
'west virginia': 'wv',
|
|
175
|
-
wisconsin: 'wi',
|
|
176
|
-
wyoming: 'wy',
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const COUNTRY_CODES = {
|
|
180
|
-
afghanistan: 'af',
|
|
181
|
-
'åland islands': 'ax',
|
|
182
|
-
albania: 'al',
|
|
183
|
-
algeria: 'dz',
|
|
184
|
-
'american samoa': 'as',
|
|
185
|
-
andorra: 'ad',
|
|
186
|
-
angola: 'ao',
|
|
187
|
-
anguilla: 'ai',
|
|
188
|
-
antarctica: 'aq',
|
|
189
|
-
'antigua and barbuda': 'ag',
|
|
190
|
-
argentina: 'ar',
|
|
191
|
-
armenia: 'am',
|
|
192
|
-
aruba: 'aw',
|
|
193
|
-
australia: 'au',
|
|
194
|
-
austria: 'at',
|
|
195
|
-
azerbaijan: 'az',
|
|
196
|
-
bahamas: 'bs',
|
|
197
|
-
bahrain: 'bh',
|
|
198
|
-
bangladesh: 'bd',
|
|
199
|
-
barbados: 'bb',
|
|
200
|
-
belarus: 'by',
|
|
201
|
-
belgium: 'be',
|
|
202
|
-
belize: 'bz',
|
|
203
|
-
benin: 'bj',
|
|
204
|
-
bermuda: 'bm',
|
|
205
|
-
bhutan: 'bt',
|
|
206
|
-
'bolivia (plurinational state of)': 'bo',
|
|
207
|
-
'bonaire, sint eustatius and saba': 'bq',
|
|
208
|
-
'bosnia and herzegovina': 'ba',
|
|
209
|
-
botswana: 'bw',
|
|
210
|
-
'bouvet island': 'bv',
|
|
211
|
-
brazil: 'br',
|
|
212
|
-
'british indian ocean territory': 'io',
|
|
213
|
-
'brunei darussalam': 'bn',
|
|
214
|
-
brunei: 'bn',
|
|
215
|
-
bulgaria: 'bg',
|
|
216
|
-
'burkina faso': 'bf',
|
|
217
|
-
burundi: 'bi',
|
|
218
|
-
'cabo verde': 'cv',
|
|
219
|
-
cambodia: 'kh',
|
|
220
|
-
cameroon: 'cm',
|
|
221
|
-
canada: 'ca',
|
|
222
|
-
'cayman islands': 'ky',
|
|
223
|
-
'central african republic': 'cf',
|
|
224
|
-
chad: 'td',
|
|
225
|
-
chile: 'cl',
|
|
226
|
-
china: 'cn',
|
|
227
|
-
'christmas island': 'cx',
|
|
228
|
-
'cocos (keeling) islands': 'cc',
|
|
229
|
-
colombia: 'co',
|
|
230
|
-
comoros: 'km',
|
|
231
|
-
'democratic republic of the congo': 'cd',
|
|
232
|
-
'cook islands': 'ck',
|
|
233
|
-
'costa rica': 'cr',
|
|
234
|
-
"côte d'ivoire": 'ci',
|
|
235
|
-
croatia: 'hr',
|
|
236
|
-
cuba: 'cu',
|
|
237
|
-
curaçao: 'cw',
|
|
238
|
-
cyprus: 'cy',
|
|
239
|
-
czechia: 'cz',
|
|
240
|
-
denmark: 'dk',
|
|
241
|
-
djibouti: 'dj',
|
|
242
|
-
dominica: 'dm',
|
|
243
|
-
'dominican republic': 'do',
|
|
244
|
-
ecuador: 'ec',
|
|
245
|
-
egypt: 'eg',
|
|
246
|
-
'el salvador': 'sv',
|
|
247
|
-
'equatorial guinea': 'gq',
|
|
248
|
-
eritrea: 'er',
|
|
249
|
-
estonia: 'ee',
|
|
250
|
-
eswatini: 'sz',
|
|
251
|
-
ethiopia: 'et',
|
|
252
|
-
'falkland islands': 'fk',
|
|
253
|
-
'faroe islands': 'fo',
|
|
254
|
-
fiji: 'fj',
|
|
255
|
-
finland: 'fi',
|
|
256
|
-
france: 'fr',
|
|
257
|
-
'french guiana': 'gf',
|
|
258
|
-
'french polynesia': 'pf',
|
|
259
|
-
'french southern territories': 'tf',
|
|
260
|
-
gabon: 'ga',
|
|
261
|
-
gambia: 'gm',
|
|
262
|
-
georgia: 'ge',
|
|
263
|
-
germany: 'de',
|
|
264
|
-
ghana: 'gh',
|
|
265
|
-
gibraltar: 'gi',
|
|
266
|
-
greece: 'gr',
|
|
267
|
-
greenland: 'gl',
|
|
268
|
-
grenada: 'gd',
|
|
269
|
-
guadeloupe: 'gp',
|
|
270
|
-
guam: 'gu',
|
|
271
|
-
guatemala: 'gt',
|
|
272
|
-
guernsey: 'gg',
|
|
273
|
-
guinea: 'gn',
|
|
274
|
-
'guinea-bissau': 'gw',
|
|
275
|
-
guyana: 'gy',
|
|
276
|
-
haiti: 'ht',
|
|
277
|
-
'heard island and mcdonald islands': 'hm',
|
|
278
|
-
'holy see': 'va',
|
|
279
|
-
honduras: 'hn',
|
|
280
|
-
'hong kong': 'hk',
|
|
281
|
-
hungary: 'hu',
|
|
282
|
-
iceland: 'is',
|
|
283
|
-
india: 'in',
|
|
284
|
-
indonesia: 'id',
|
|
285
|
-
iran: 'ir',
|
|
286
|
-
iraq: 'iq',
|
|
287
|
-
ireland: 'ie',
|
|
288
|
-
'isle of man': 'im',
|
|
289
|
-
israel: 'il',
|
|
290
|
-
italy: 'it',
|
|
291
|
-
jamaica: 'jm',
|
|
292
|
-
japan: 'jp',
|
|
293
|
-
jersey: 'je',
|
|
294
|
-
jordan: 'jo',
|
|
295
|
-
kazakhstan: 'kz',
|
|
296
|
-
kenya: 'ke',
|
|
297
|
-
kiribati: 'ki',
|
|
298
|
-
'north korea': 'kp',
|
|
299
|
-
'south korea': 'kr',
|
|
300
|
-
kuwait: 'kw',
|
|
301
|
-
kyrgyzstan: 'kg',
|
|
302
|
-
laos: 'la',
|
|
303
|
-
latvia: 'lv',
|
|
304
|
-
lebanon: 'lb',
|
|
305
|
-
lesotho: 'ls',
|
|
306
|
-
liberia: 'lr',
|
|
307
|
-
libya: 'ly',
|
|
308
|
-
liechtenstein: 'li',
|
|
309
|
-
lithuania: 'lt',
|
|
310
|
-
luxembourg: 'lu',
|
|
311
|
-
macao: 'mo',
|
|
312
|
-
madagascar: 'mg',
|
|
313
|
-
malawi: 'mw',
|
|
314
|
-
malaysia: 'my',
|
|
315
|
-
maldives: 'mv',
|
|
316
|
-
mali: 'ml',
|
|
317
|
-
malta: 'mt',
|
|
318
|
-
'marshall islands': 'mh',
|
|
319
|
-
martinique: 'mq',
|
|
320
|
-
mauritania: 'mr',
|
|
321
|
-
mauritius: 'mu',
|
|
322
|
-
mayotte: 'yt',
|
|
323
|
-
mexico: 'mx',
|
|
324
|
-
micronesia: 'fm',
|
|
325
|
-
moldova: 'md',
|
|
326
|
-
monaco: 'mc',
|
|
327
|
-
mongolia: 'mn',
|
|
328
|
-
montenegro: 'me',
|
|
329
|
-
montserrat: 'ms',
|
|
330
|
-
morocco: 'ma',
|
|
331
|
-
mozambique: 'mz',
|
|
332
|
-
myanmar: 'mm',
|
|
333
|
-
namibia: 'na',
|
|
334
|
-
nauru: 'nr',
|
|
335
|
-
nepal: 'np',
|
|
336
|
-
netherlands: 'nl',
|
|
337
|
-
'new caledonia': 'nc',
|
|
338
|
-
'new zealand': 'nz',
|
|
339
|
-
nicaragua: 'ni',
|
|
340
|
-
niger: 'ne',
|
|
341
|
-
nigeria: 'ng',
|
|
342
|
-
niue: 'nu',
|
|
343
|
-
'norfolk island': 'nf',
|
|
344
|
-
'north macedonia': 'mk',
|
|
345
|
-
'northern mariana islands': 'mp',
|
|
346
|
-
norway: 'no',
|
|
347
|
-
oman: 'om',
|
|
348
|
-
pakistan: 'pk',
|
|
349
|
-
palau: 'pw',
|
|
350
|
-
palestine: 'ps',
|
|
351
|
-
panama: 'pa',
|
|
352
|
-
'papua new guinea': 'pg',
|
|
353
|
-
paraguay: 'py',
|
|
354
|
-
peru: 'pe',
|
|
355
|
-
philippines: 'ph',
|
|
356
|
-
pitcairn: 'pn',
|
|
357
|
-
poland: 'pl',
|
|
358
|
-
portugal: 'pt',
|
|
359
|
-
'puerto rico': 'pr',
|
|
360
|
-
qatar: 'qa',
|
|
361
|
-
réunion: 're',
|
|
362
|
-
romania: 'ro',
|
|
363
|
-
'russian federation': 'ru',
|
|
364
|
-
rwanda: 'rw',
|
|
365
|
-
'saint barthélemy': 'bl',
|
|
366
|
-
'saint helena': 'sh',
|
|
367
|
-
'saint kitts and nevis': 'kn',
|
|
368
|
-
'saint lucia': 'lc',
|
|
369
|
-
'saint martin': 'mf',
|
|
370
|
-
'saint pierre and miquelon': 'pm',
|
|
371
|
-
'saint vincent and the grenadines': 'vc',
|
|
372
|
-
samoa: 'ws',
|
|
373
|
-
'san marino': 'sm',
|
|
374
|
-
'sao tome and principe': 'st',
|
|
375
|
-
'saudi arabia': 'sa',
|
|
376
|
-
senegal: 'sn',
|
|
377
|
-
serbia: 'rs',
|
|
378
|
-
seychelles: 'sc',
|
|
379
|
-
'sierra leone': 'sl',
|
|
380
|
-
singapore: 'sg',
|
|
381
|
-
'sint maarten': 'sx',
|
|
382
|
-
slovakia: 'sk',
|
|
383
|
-
slovenia: 'si',
|
|
384
|
-
'solomon islands': 'sb',
|
|
385
|
-
somalia: 'so',
|
|
386
|
-
'south africa': 'za',
|
|
387
|
-
'south georgia and the south sandwich islands': 'gs',
|
|
388
|
-
'south sudan': 'ss',
|
|
389
|
-
spain: 'es',
|
|
390
|
-
'sri lanka': 'lk',
|
|
391
|
-
sudan: 'sd',
|
|
392
|
-
suriname: 'sr',
|
|
393
|
-
'svalbard and jan mayen': 'sj',
|
|
394
|
-
sweden: 'se',
|
|
395
|
-
switzerland: 'ch',
|
|
396
|
-
'syrian arab republic': 'sy',
|
|
397
|
-
taiwan: 'tw',
|
|
398
|
-
tajikistan: 'tj',
|
|
399
|
-
tanzania: 'tz',
|
|
400
|
-
thailand: 'th',
|
|
401
|
-
'timor-leste': 'tl',
|
|
402
|
-
togo: 'tg',
|
|
403
|
-
tokelau: 'tk',
|
|
404
|
-
tonga: 'to',
|
|
405
|
-
'trinidad and tobago': 'tt',
|
|
406
|
-
tunisia: 'tn',
|
|
407
|
-
türkiye: 'tr',
|
|
408
|
-
turkmenistan: 'tm',
|
|
409
|
-
'turks and caicos islands': 'tc',
|
|
410
|
-
tuvalu: 'tv',
|
|
411
|
-
uganda: 'ug',
|
|
412
|
-
ukraine: 'ua',
|
|
413
|
-
'united arab emirates': 'ae',
|
|
414
|
-
'united kingdom': 'gb',
|
|
415
|
-
'united states minor outlying islands': 'um',
|
|
416
|
-
'united states': 'us',
|
|
417
|
-
uruguay: 'uy',
|
|
418
|
-
uzbekistan: 'uz',
|
|
419
|
-
vanuatu: 'vu',
|
|
420
|
-
venezuela: 've',
|
|
421
|
-
'viet nam': 'vn',
|
|
422
|
-
'british virgin islands': 'vg',
|
|
423
|
-
'u.s. virgin islands': 'vi',
|
|
424
|
-
'wallis and futuna': 'wf',
|
|
425
|
-
'western sahara': 'eh',
|
|
426
|
-
yemen: 'ye',
|
|
427
|
-
zambia: 'zm',
|
|
428
|
-
zimbabwe: 'zw',
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
function createRequestOptions(options = {}) {
|
|
432
|
-
const opts = Object.assign({}, options);
|
|
433
|
-
|
|
434
|
-
// Note: headers get mutated after setHeaders is called.
|
|
435
|
-
// This is why we get the content type here to know what the user originally set.
|
|
436
|
-
|
|
437
|
-
const headers = new Headers(options.headers);
|
|
438
|
-
const userContentType = headers.get('content-type');
|
|
439
|
-
|
|
440
|
-
opts.url = setUrl(opts);
|
|
441
|
-
opts.method = setMethod(opts);
|
|
442
|
-
opts.headers = setHeaders(opts);
|
|
443
|
-
|
|
444
|
-
// If Content Type is set explicitly, we expect the user to pass in the appropriate data. So we don't treat the body
|
|
445
|
-
opts.body = !userContentType ? setBody(opts) : opts.body;
|
|
446
|
-
return opts
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Appends queries to URL
|
|
451
|
-
* @param {Object} opts
|
|
452
|
-
*/
|
|
453
|
-
function setUrl(options) {
|
|
454
|
-
const { url, queries, query, params, param } = options;
|
|
455
|
-
|
|
456
|
-
// Merge queries, query, and params — treat them as the same item
|
|
457
|
-
// So users don't have to remember singular or plural forms
|
|
458
|
-
const q = Object.assign({}, queries, query, params, param);
|
|
459
|
-
|
|
460
|
-
if (isEmptyObject(q)) return options.url
|
|
461
|
-
|
|
462
|
-
const searchParams = new URLSearchParams(q);
|
|
463
|
-
return `${url}?${searchParams.toString()}`
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function isEmptyObject(obj) {
|
|
467
|
-
return (
|
|
468
|
-
obj && // 👈 null and undefined check
|
|
469
|
-
Object.keys(obj).length === 0 &&
|
|
470
|
-
Object.getPrototypeOf(obj) === Object.prototype
|
|
471
|
-
)
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function setMethod(options) {
|
|
475
|
-
// Method set to GET by default unless otherwise specified
|
|
476
|
-
const method = options.method || 'get';
|
|
477
|
-
return method
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// ========================
|
|
481
|
-
// Set Headers
|
|
482
|
-
// We set the headers depending on the request body and method
|
|
483
|
-
// ========================
|
|
484
|
-
function setHeaders(options) {
|
|
485
|
-
let headers = new Headers(options.headers);
|
|
486
|
-
headers = contentTypeHeader(options, headers);
|
|
487
|
-
headers = authHeader(options, headers);
|
|
488
|
-
|
|
489
|
-
return headers
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
function contentTypeHeader(options, headers) {
|
|
493
|
-
// For preflight requests, we don't want to set headers.
|
|
494
|
-
// This allows requests to remain simple.
|
|
495
|
-
if (options.method === 'options') return headers
|
|
496
|
-
|
|
497
|
-
// For GET requests, we also don't want to set headers.
|
|
498
|
-
// This allows requests to remain simple.
|
|
499
|
-
if (options.method === 'get') return headers
|
|
500
|
-
|
|
501
|
-
// If a content type is aleady set, we return the content type as is.
|
|
502
|
-
// This allows users to set their own content type.
|
|
503
|
-
if (headers.get('content-type')) return headers
|
|
504
|
-
|
|
505
|
-
// If the body is a string, we assume it's a query string.
|
|
506
|
-
// So we set headers to Content-Type: application/x-www-form-urlencoded.
|
|
507
|
-
if (typeof options.body === 'string') {
|
|
508
|
-
headers.set('content-type', 'application/x-www-form-urlencoded');
|
|
509
|
-
return headers
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// If the body is an object and not FormData, we set it to `application/json`. Checking for FormData is important here because Form Data requires another content type.
|
|
513
|
-
if (typeof options.body === 'object' && !isFormData(options.body)) {
|
|
514
|
-
headers.set('content-type', 'application/json');
|
|
515
|
-
return headers
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// What's left here is FormData.
|
|
519
|
-
// We don't set the content type here because fetch will set it automatically.
|
|
520
|
-
return headers
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Sets the auth headers as necessary
|
|
524
|
-
function authHeader(options, headers) {
|
|
525
|
-
// If no auth options, means we don't have to set Authorization headers
|
|
526
|
-
const { auth } = options;
|
|
527
|
-
if (!auth) return headers
|
|
528
|
-
|
|
529
|
-
// Set Bearer authentication when user passes in a string into auth
|
|
530
|
-
if (typeof auth === 'string') {
|
|
531
|
-
headers.set('Authorization', `Bearer ${auth}`);
|
|
532
|
-
return headers
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Set Basic Authentication headers when user passes in username and password into auth.
|
|
536
|
-
const btoa = getBtoa();
|
|
537
|
-
|
|
538
|
-
// Password field can be empty for implicit grant
|
|
539
|
-
const { username, password = '' } = auth;
|
|
540
|
-
|
|
541
|
-
if (!username) {
|
|
542
|
-
throw new Error(
|
|
543
|
-
'Username required to create Authorization Header for a Basic Authentication'
|
|
544
|
-
)
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
const encodedValue = btoa(`${username}:${password}`);
|
|
548
|
-
headers.set('Authorization', `Basic ${encodedValue}`);
|
|
549
|
-
return headers
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// Gets Btoa for creating basic auth.
|
|
553
|
-
function getBtoa() {
|
|
554
|
-
if (typeof window !== 'undefined' && window.btoa) {
|
|
555
|
-
return window.btoa
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
return function (string) {
|
|
559
|
-
return Buffer.from(string).toString('base64')
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// ========================
|
|
564
|
-
// Set Body
|
|
565
|
-
// ========================
|
|
566
|
-
function setBody(options) {
|
|
567
|
-
// Return empty body for preflight requests
|
|
568
|
-
const method = options.method;
|
|
569
|
-
if (['get', 'head', 'options'].includes(method)) return
|
|
570
|
-
|
|
571
|
-
// If the body is form data, we return it as it is because users will have to set it up appropriately themselves.
|
|
572
|
-
if (isFormData(options.body)) return options.body
|
|
573
|
-
|
|
574
|
-
// If the body is a string, we assume it's a query string.
|
|
575
|
-
// We ask users to use toQueryString to convert an object to a query string as they send in the request.
|
|
576
|
-
if (typeof options.body === 'string') return options.body
|
|
577
|
-
|
|
578
|
-
// If it's an object, we convert it to JSON
|
|
579
|
-
if (typeof options.body === 'object') {
|
|
580
|
-
return JSON.stringify(options.body)
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// If the above conditions don't trigger, we return the body as is.
|
|
584
|
-
// One example that will reach here is FormData
|
|
585
|
-
return options.body
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
// Checks if body is FormData
|
|
589
|
-
// Only works on browsers.
|
|
590
|
-
// Returns false in Node because Node doesn't have FormData.
|
|
591
|
-
function isFormData(body) {
|
|
592
|
-
if (typeof window === 'undefined') return false
|
|
593
|
-
return body instanceof FormData
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function handleResponse(response, options) {
|
|
597
|
-
// Lets user use custom response parser because some people want to do so.
|
|
598
|
-
// See https://github.com/zellwk/zl-fetch/issues/2
|
|
599
|
-
if (options?.customResponseParser) {
|
|
600
|
-
return response
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const contentType = response.headers.get('content-type');
|
|
604
|
-
const type = getResponseType(contentType);
|
|
605
|
-
return parseResponse(response, { ...options, type })
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* Formats all errors into zlFetch style error
|
|
610
|
-
* @param {Object} error - The error object
|
|
611
|
-
*/
|
|
612
|
-
function handleError(error) {
|
|
613
|
-
if (error.message === 'Failed to fetch') {
|
|
614
|
-
/* eslint-disable */
|
|
615
|
-
return Promise.reject({ error })
|
|
616
|
-
/* eslint-enable */
|
|
617
|
-
}
|
|
618
|
-
return Promise.reject(error)
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ========================
|
|
622
|
-
// Internal Functions
|
|
623
|
-
// ========================
|
|
624
|
-
function getResponseType(type) {
|
|
625
|
-
if (!type) return null // Handles 204 No Content
|
|
626
|
-
if (type.includes('json')) return 'json'
|
|
627
|
-
if (type.includes('text')) return 'text'
|
|
628
|
-
if (type.includes('blob')) return 'blob'
|
|
629
|
-
if (type.includes('x-www-form-urlencoded')) return 'formData'
|
|
630
|
-
|
|
631
|
-
// Need to check for FormData, Blob and ArrayBuffer content types
|
|
632
|
-
throw new Error(`zlFetch does not support content-type ${type} yet`)
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
async function parseResponse(response, options) {
|
|
636
|
-
// Parse formData into JavaScript object
|
|
637
|
-
if (options.type === 'formData') {
|
|
638
|
-
let body = await response.text();
|
|
639
|
-
const query = new URLSearchParams(body);
|
|
640
|
-
body = Object.fromEntries(query);
|
|
641
|
-
return createOutput({ response, body, options })
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const body = await response[options.type]();
|
|
645
|
-
return createOutput({ response, body, options })
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
function createOutput({ response, body, options }) {
|
|
649
|
-
const headers = getHeaders(response);
|
|
650
|
-
const returnValue = {
|
|
651
|
-
body,
|
|
652
|
-
headers,
|
|
653
|
-
response,
|
|
654
|
-
status: response.status,
|
|
655
|
-
statusText: response.statusText,
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
// Resolves if successful response
|
|
659
|
-
// Rejects if unsuccessful response
|
|
660
|
-
if (!options.returnError) {
|
|
661
|
-
return response.ok
|
|
662
|
-
? Promise.resolve(returnValue)
|
|
663
|
-
: Promise.reject(returnValue)
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Returns both successful and unsuccessful response
|
|
667
|
-
if (options.returnError) {
|
|
668
|
-
let data;
|
|
669
|
-
let error;
|
|
670
|
-
|
|
671
|
-
if (response.ok) {
|
|
672
|
-
data = returnValue;
|
|
673
|
-
error = null;
|
|
674
|
-
} else {
|
|
675
|
-
data = null;
|
|
676
|
-
error = returnValue;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
return Promise.resolve({
|
|
680
|
-
response: data,
|
|
681
|
-
error,
|
|
682
|
-
})
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
function getHeaders(response) {
|
|
687
|
-
return response.headers.entries
|
|
688
|
-
? getBrowserFetchHeaders(response)
|
|
689
|
-
: getNodeFetchHeaders(response)
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// window.fetch response headers contains entries method.
|
|
693
|
-
function getBrowserFetchHeaders(response) {
|
|
694
|
-
const headers = {};
|
|
695
|
-
for (const [header, value] of response.headers.entries()) {
|
|
696
|
-
headers[header] = value;
|
|
697
|
-
}
|
|
698
|
-
return headers
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Node fetch response headers does not contain entries method.
|
|
702
|
-
function getNodeFetchHeaders(response) {
|
|
703
|
-
const headers = {};
|
|
704
|
-
const h = response.headers._headers;
|
|
705
|
-
for (const header in h) {
|
|
706
|
-
headers[header] = h[header].join('');
|
|
707
|
-
}
|
|
708
|
-
return headers
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
* Main Fetch Function
|
|
713
|
-
* @param {string} url - endpoint
|
|
714
|
-
* @param {object} options - zlFetch options
|
|
715
|
-
* @param {string} options.method - HTTP method
|
|
716
|
-
* @param {object} options.headers - HTTP headers
|
|
717
|
-
* @param {object} options.body - Body content
|
|
718
|
-
* @param {string} options.auth - Authentication information
|
|
719
|
-
* @param {string} options.debug - Logs the request options for debugging
|
|
720
|
-
* @param {string} options.returnError - Returns the error instead of rejecting it
|
|
721
|
-
* @param {string} options.customResponseParser - Use a custome response parser
|
|
722
|
-
*/
|
|
723
|
-
|
|
724
|
-
function coreFetch(url, options) {
|
|
725
|
-
return fetchInstance({ url, ...options })
|
|
726
|
-
}
|
|
727
|
-
// ========================
|
|
728
|
-
// Internal Functions
|
|
729
|
-
// ========================
|
|
730
|
-
async function fetchInstance(options) {
|
|
731
|
-
const requestOptions = createRequestOptions(options);
|
|
732
|
-
|
|
733
|
-
// Remove options that are not native to a fetch request
|
|
734
|
-
delete requestOptions.fetch;
|
|
735
|
-
delete requestOptions.queries;
|
|
736
|
-
delete requestOptions.query;
|
|
737
|
-
delete requestOptions.params;
|
|
738
|
-
delete requestOptions.param;
|
|
739
|
-
delete requestOptions.auth;
|
|
740
|
-
delete requestOptions.debug;
|
|
741
|
-
delete requestOptions.returnError;
|
|
742
|
-
|
|
743
|
-
// Performs the fetch request
|
|
744
|
-
return fetch(requestOptions.url, requestOptions)
|
|
745
|
-
.then(response => handleResponse(response, options))
|
|
746
|
-
.then(response => {
|
|
747
|
-
if (!options.debug) return response
|
|
748
|
-
return { ...response, debug: debugHeaders(requestOptions) }
|
|
749
|
-
})
|
|
750
|
-
.catch(handleError)
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
function debugHeaders(requestOptions) {
|
|
754
|
-
const clone = Object.assign({}, requestOptions);
|
|
755
|
-
const headers = {};
|
|
756
|
-
for (const [header, value] of clone.headers) {
|
|
757
|
-
headers[header] = value;
|
|
758
|
-
}
|
|
759
|
-
clone.headers = headers;
|
|
760
|
-
return clone
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function createShorthandMethods(fn) {
|
|
764
|
-
const methods = ['get', 'post', 'put', 'patch', 'delete'];
|
|
765
|
-
|
|
766
|
-
for (const method of methods) {
|
|
767
|
-
fn[method] = function (url, options) {
|
|
768
|
-
return coreFetch(url, {
|
|
769
|
-
...options,
|
|
770
|
-
method,
|
|
771
|
-
})
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
return fn
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
let fn = coreFetch;
|
|
779
|
-
fn = createShorthandMethods(fn);
|
|
780
|
-
|
|
781
|
-
var zlFetch = fn;
|
|
782
|
-
|
|
783
|
-
// TODO: Figure a way to put the Pixel Code into the DOM... if possible!
|
|
784
|
-
function FB({ pixelID, testEventCode, capiEndpoint }) {
|
|
785
|
-
const _fb = {
|
|
786
|
-
options: { pixelID, testEventCode, capiEndpoint },
|
|
787
|
-
|
|
788
|
-
// Initializes Facebook
|
|
789
|
-
init() {
|
|
790
|
-
!(function (f, b, e, v, n, t, s) {
|
|
791
|
-
if (f.fbq) return
|
|
792
|
-
n = f.fbq = function () {
|
|
793
|
-
n.callMethod
|
|
794
|
-
? n.callMethod.apply(n, arguments)
|
|
795
|
-
: n.queue.push(arguments);
|
|
796
|
-
};
|
|
797
|
-
if (!f._fbq) f._fbq = n;
|
|
798
|
-
n.push = n;
|
|
799
|
-
n.loaded = !0;
|
|
800
|
-
n.version = '2.0';
|
|
801
|
-
n.queue = [];
|
|
802
|
-
t = b.createElement(e);
|
|
803
|
-
t.async = !0;
|
|
804
|
-
t.src = v;
|
|
805
|
-
s = b.getElementsByTagName(e)[0];
|
|
806
|
-
s.parentNode.insertBefore(t, s);
|
|
807
|
-
})(
|
|
808
|
-
window,
|
|
809
|
-
document,
|
|
810
|
-
'script',
|
|
811
|
-
'https://connect.facebook.net/en_US/fbevents.js'
|
|
812
|
-
);
|
|
813
|
-
fbq('init', pixelID);
|
|
814
|
-
},
|
|
815
|
-
|
|
816
|
-
async sendEvent(eventName, props = {}) {
|
|
817
|
-
const eventData = await _fb.createFBEvent(eventName, props);
|
|
818
|
-
|
|
819
|
-
// Question whether we want to verbose log this thing in the future so users can know whether these events are sent.
|
|
820
|
-
_fb.sendPixelEvent(eventData);
|
|
821
|
-
return _fb.sendCAPIEvent(eventData)
|
|
822
|
-
},
|
|
823
|
-
|
|
824
|
-
async createFBEvent(eventName, props = {}) {
|
|
825
|
-
const { eventURL, actionSource, ...rest } = props;
|
|
826
|
-
|
|
827
|
-
const { picked: userProperties, omitted: customData } = splitObject(
|
|
828
|
-
rest,
|
|
829
|
-
[...USER_PROPERTIES_TO_HASH, ...USER_PROPERTIES_NOT_TO_HASH]
|
|
830
|
-
);
|
|
831
|
-
|
|
832
|
-
const { picked: userPropsToHash, omitted: otherUserProps } = splitObject(
|
|
833
|
-
userProperties,
|
|
834
|
-
USER_PROPERTIES_TO_HASH
|
|
835
|
-
);
|
|
836
|
-
|
|
837
|
-
const userData = {
|
|
838
|
-
...(await formatUserData(userPropsToHash)),
|
|
839
|
-
...otherUserProps,
|
|
840
|
-
};
|
|
841
|
-
|
|
842
|
-
const ret = {
|
|
843
|
-
// Data for both Pixel and CAPI
|
|
844
|
-
pixelID,
|
|
845
|
-
eventID: await randomString(),
|
|
846
|
-
eventName: eventName,
|
|
847
|
-
userData,
|
|
848
|
-
customData,
|
|
849
|
-
|
|
850
|
-
// Data for CAPI only
|
|
851
|
-
fbp: getCookie('_fbp'),
|
|
852
|
-
fbc: getCookie('_fbc'),
|
|
853
|
-
eventTime: Math.floor(new Date() / 1000),
|
|
854
|
-
eventURL: eventURL || window.location.href,
|
|
855
|
-
actionSource: actionSource || 'website',
|
|
856
|
-
};
|
|
857
|
-
if (testEventCode) ret.testEventCode = testEventCode;
|
|
858
|
-
|
|
859
|
-
return ret
|
|
860
|
-
},
|
|
861
|
-
|
|
862
|
-
// Facebook Pixel Code
|
|
863
|
-
// Pixel Standard Reference
|
|
864
|
-
// https://developers.facebook.com/docs/meta-pixel/reference#standard-events
|
|
865
|
-
sendPixelEvent(data) {
|
|
866
|
-
const {
|
|
867
|
-
eventID,
|
|
868
|
-
eventName,
|
|
869
|
-
userData = {},
|
|
870
|
-
customData = {},
|
|
871
|
-
pixelID,
|
|
872
|
-
} = data;
|
|
873
|
-
const props = { ...userData, ...customData };
|
|
874
|
-
|
|
875
|
-
let action = 'track';
|
|
876
|
-
let evtName = eventName;
|
|
877
|
-
if (evtName === 'init') action = 'init';
|
|
878
|
-
|
|
879
|
-
// When we use fbq init, we have to add the pixelID instead of the event name as the 2nd parameter.
|
|
880
|
-
if (action === 'init') evtName = pixelID;
|
|
881
|
-
|
|
882
|
-
// If the event is a custom event, we need to use `trackCustom` instead of `track`.
|
|
883
|
-
if (action !== 'init' && !standardEvents.includes(evtName))
|
|
884
|
-
action = 'trackCustom';
|
|
885
|
-
|
|
886
|
-
fbq(action, evtName, props, { eventID });
|
|
887
|
-
},
|
|
888
|
-
|
|
889
|
-
// CAPI Parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
|
|
890
|
-
sendCAPIEvent(data) {
|
|
891
|
-
return zlFetch.post(capiEndpoint, { body: data })
|
|
892
|
-
},
|
|
893
|
-
};
|
|
894
|
-
|
|
895
|
-
return _fb
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
const standardEvents = [
|
|
899
|
-
'AddPaymentInfo',
|
|
900
|
-
'AddToCart',
|
|
901
|
-
'AddToWishlist',
|
|
902
|
-
'CompleteRegistration',
|
|
903
|
-
'Contact',
|
|
904
|
-
'CustomizeProduct',
|
|
905
|
-
'Donate',
|
|
906
|
-
'FindLocation',
|
|
907
|
-
'InitiateCheckout',
|
|
908
|
-
'Lead',
|
|
909
|
-
'Purchase',
|
|
910
|
-
'Schedule',
|
|
911
|
-
'Search',
|
|
912
|
-
'StartTrial',
|
|
913
|
-
'SubmitApplication',
|
|
914
|
-
'Subscribe',
|
|
915
|
-
'ViewContent',
|
|
916
|
-
];
|
|
917
|
-
|
|
918
|
-
// https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
|
|
919
|
-
const USER_PROPERTIES_TO_HASH = [
|
|
920
|
-
'email',
|
|
921
|
-
'phone',
|
|
922
|
-
'firstName',
|
|
923
|
-
'lastName',
|
|
924
|
-
'birthdate',
|
|
925
|
-
'gender',
|
|
926
|
-
'city',
|
|
927
|
-
'state',
|
|
928
|
-
'zip',
|
|
929
|
-
'country',
|
|
930
|
-
'externalID',
|
|
931
|
-
];
|
|
932
|
-
|
|
933
|
-
// Other optional FB Stuff — doesn't seem very useful
|
|
934
|
-
const USER_PROPERTIES_NOT_TO_HASH = [
|
|
935
|
-
'subscription_id',
|
|
936
|
-
'fb_login_id',
|
|
937
|
-
'lead_id',
|
|
938
|
-
'anon_id',
|
|
939
|
-
'madid',
|
|
940
|
-
'page_id',
|
|
941
|
-
'page_scoped_user_id',
|
|
942
|
-
'ctwa_clid',
|
|
943
|
-
'ig_account_id',
|
|
944
|
-
'ig_sid',
|
|
945
|
-
];
|
|
946
|
-
|
|
947
|
-
return FB;
|
|
948
|
-
|
|
949
|
-
}));
|