saasco-sdk 0.1.45 → 0.2.4
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/index.cjs +3043 -0
- package/dist/index.d.cts +535 -0
- package/dist/index.d.ts +535 -0
- package/dist/index.js +3006 -0
- package/dist/support.cjs +18301 -0
- package/dist/support.d.cts +183 -0
- package/dist/support.d.ts +183 -0
- package/dist/support.js +18303 -0
- package/package.json +54 -10
- package/README.md +0 -546
- package/index.cjs.d.ts +0 -1
- package/index.cjs.js +0 -2166
- package/index.esm.d.ts +0 -1
- package/index.esm.js +0 -2152
- package/src/index.d.ts +0 -5
- package/src/lib/analytics.d.ts +0 -134
- package/src/lib/getBrowserContext.d.ts +0 -2
- package/src/lib/integrations/facebook-pixel.d.ts +0 -34
- package/src/lib/integrations/index.d.ts +0 -4
- package/src/lib/integrations/integration-manager.d.ts +0 -119
- package/src/lib/integrations/pinterest-tag.d.ts +0 -37
- package/src/lib/integrations/tiktok-pixel.d.ts +0 -59
- package/src/lib/self-execute-analytics.d.ts +0 -6
- package/src/lib/timezones.d.ts +0 -3
- package/src/lib/tracking/index.d.ts +0 -1
- package/src/lib/tracking/types.d.ts +0 -73
- package/src/lib/utils/getIntegrationLoggerLevel.d.ts +0 -8
- package/src/lib/utils/index.d.ts +0 -3
- package/src/lib/utils/logger.d.ts +0 -30
- package/src/lib/utils/uuid.d.ts +0 -5
package/index.cjs.js
DELETED
|
@@ -1,2166 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var tslib = require('tslib');
|
|
6
|
-
var psl = require('psl');
|
|
7
|
-
var uuid$1 = require('@lukeed/uuid');
|
|
8
|
-
var jsSha256 = require('js-sha256');
|
|
9
|
-
var zod = require('zod');
|
|
10
|
-
|
|
11
|
-
var version = "0.1.45";
|
|
12
|
-
|
|
13
|
-
const timezones = {
|
|
14
|
-
'Asia/Barnaul': 'RU',
|
|
15
|
-
'Africa/Nouakchott': 'MR',
|
|
16
|
-
'Africa/Lusaka': 'ZM',
|
|
17
|
-
'Asia/Pyongyang': 'KP',
|
|
18
|
-
'Europe/Bratislava': 'SK',
|
|
19
|
-
'America/Belize': 'BZ',
|
|
20
|
-
'America/Maceio': 'BR',
|
|
21
|
-
'Pacific/Chuuk': 'FM',
|
|
22
|
-
'Indian/Comoro': 'KM',
|
|
23
|
-
'Pacific/Palau': 'PW',
|
|
24
|
-
'Asia/Jakarta': 'ID',
|
|
25
|
-
'Africa/Windhoek': 'NA',
|
|
26
|
-
'America/Chihuahua': 'MX',
|
|
27
|
-
'America/Nome': 'US',
|
|
28
|
-
'Africa/Mbabane': 'SZ',
|
|
29
|
-
'Africa/Porto-Novo': 'BJ',
|
|
30
|
-
'Europe/San_Marino': 'SM',
|
|
31
|
-
'Pacific/Fakaofo': 'TK',
|
|
32
|
-
'America/Denver': 'US',
|
|
33
|
-
'Europe/Belgrade': 'RS',
|
|
34
|
-
'America/Indiana/Tell_City': 'US',
|
|
35
|
-
'America/Fortaleza': 'BR',
|
|
36
|
-
'America/Halifax': 'CA',
|
|
37
|
-
'Europe/Bucharest': 'RO',
|
|
38
|
-
'America/Indiana/Petersburg': 'US',
|
|
39
|
-
'Europe/Kirov': 'RU',
|
|
40
|
-
'Europe/Athens': 'GR',
|
|
41
|
-
'America/Argentina/Ushuaia': 'AR',
|
|
42
|
-
'Europe/Monaco': 'MC',
|
|
43
|
-
'Europe/Vilnius': 'LT',
|
|
44
|
-
'Europe/Copenhagen': 'DK',
|
|
45
|
-
'Pacific/Kanton': 'KI',
|
|
46
|
-
'America/Caracas': 'VE',
|
|
47
|
-
'Asia/Almaty': 'KZ',
|
|
48
|
-
'Europe/Paris': 'FR',
|
|
49
|
-
'Africa/Blantyre': 'MW',
|
|
50
|
-
'Asia/Muscat': 'OM',
|
|
51
|
-
'America/North_Dakota/Beulah': 'US',
|
|
52
|
-
'America/Matamoros': 'MX',
|
|
53
|
-
'Asia/Irkutsk': 'RU',
|
|
54
|
-
'America/Costa_Rica': 'CR',
|
|
55
|
-
'America/Araguaina': 'BR',
|
|
56
|
-
'Atlantic/Canary': 'ES',
|
|
57
|
-
'America/Santo_Domingo': 'DO',
|
|
58
|
-
'America/Vancouver': 'CA',
|
|
59
|
-
'Africa/Addis_Ababa': 'ET',
|
|
60
|
-
'Africa/Accra': 'GH',
|
|
61
|
-
'Pacific/Kwajalein': 'MH',
|
|
62
|
-
'Asia/Baghdad': 'IQ',
|
|
63
|
-
'Australia/Adelaide': 'AU',
|
|
64
|
-
'Australia/Hobart': 'AU',
|
|
65
|
-
'America/Guayaquil': 'EC',
|
|
66
|
-
'America/Argentina/Tucuman': 'AR',
|
|
67
|
-
'Australia/Lindeman': 'AU',
|
|
68
|
-
'America/New_York': 'US',
|
|
69
|
-
'Pacific/Fiji': 'FJ',
|
|
70
|
-
'America/Antigua': 'AG',
|
|
71
|
-
'Africa/Casablanca': 'MA',
|
|
72
|
-
'America/Paramaribo': 'SR',
|
|
73
|
-
'Africa/Cairo': 'EG',
|
|
74
|
-
'America/Cayenne': 'GF',
|
|
75
|
-
'America/Detroit': 'US',
|
|
76
|
-
'Antarctica/Syowa': 'AQ',
|
|
77
|
-
'Africa/Douala': 'CM',
|
|
78
|
-
'America/Argentina/La_Rioja': 'AR',
|
|
79
|
-
'Africa/Lagos': 'NG',
|
|
80
|
-
'America/St_Barthelemy': 'BL',
|
|
81
|
-
'Asia/Nicosia': 'CY',
|
|
82
|
-
'Asia/Macau': 'MO',
|
|
83
|
-
'Europe/Riga': 'LV',
|
|
84
|
-
'Asia/Ashgabat': 'TM',
|
|
85
|
-
'Indian/Antananarivo': 'MG',
|
|
86
|
-
'America/Argentina/San_Juan': 'AR',
|
|
87
|
-
'Asia/Aden': 'YE',
|
|
88
|
-
'Asia/Tomsk': 'RU',
|
|
89
|
-
'America/Asuncion': 'PY',
|
|
90
|
-
'Pacific/Bougainville': 'PG',
|
|
91
|
-
'Asia/Vientiane': 'LA',
|
|
92
|
-
'America/Mazatlan': 'MX',
|
|
93
|
-
'Africa/Luanda': 'AO',
|
|
94
|
-
'Europe/Oslo': 'NO',
|
|
95
|
-
'Africa/Kinshasa': 'CD',
|
|
96
|
-
'Europe/Warsaw': 'PL',
|
|
97
|
-
'America/Grand_Turk': 'TC',
|
|
98
|
-
'Asia/Seoul': 'KR',
|
|
99
|
-
'Africa/Tripoli': 'LY',
|
|
100
|
-
'America/St_Thomas': 'VI',
|
|
101
|
-
'Asia/Kathmandu': 'NP',
|
|
102
|
-
'Pacific/Pitcairn': 'PN',
|
|
103
|
-
'Pacific/Nauru': 'NR',
|
|
104
|
-
'America/Curacao': 'CW',
|
|
105
|
-
'Asia/Kabul': 'AF',
|
|
106
|
-
'Pacific/Tongatapu': 'TO',
|
|
107
|
-
'Europe/Simferopol': 'UA',
|
|
108
|
-
'Asia/Ust-Nera': 'RU',
|
|
109
|
-
'Africa/Mogadishu': 'SO',
|
|
110
|
-
'Indian/Mayotte': 'YT',
|
|
111
|
-
'Pacific/Niue': 'NU',
|
|
112
|
-
'America/Thunder_Bay': 'CA',
|
|
113
|
-
'Atlantic/Azores': 'PT',
|
|
114
|
-
'Pacific/Gambier': 'PF',
|
|
115
|
-
'Europe/Stockholm': 'SE',
|
|
116
|
-
'Africa/Libreville': 'GA',
|
|
117
|
-
'America/Punta_Arenas': 'CL',
|
|
118
|
-
'America/Guatemala': 'GT',
|
|
119
|
-
'America/Noronha': 'BR',
|
|
120
|
-
'Europe/Helsinki': 'FI',
|
|
121
|
-
'Asia/Gaza': 'PS',
|
|
122
|
-
'Pacific/Kosrae': 'FM',
|
|
123
|
-
'America/Aruba': 'AW',
|
|
124
|
-
'America/Nassau': 'BS',
|
|
125
|
-
'Asia/Choibalsan': 'MN',
|
|
126
|
-
'America/Winnipeg': 'CA',
|
|
127
|
-
'America/Anguilla': 'AI',
|
|
128
|
-
'Asia/Thimphu': 'BT',
|
|
129
|
-
'Asia/Beirut': 'LB',
|
|
130
|
-
'Atlantic/Faroe': 'FO',
|
|
131
|
-
'Europe/Berlin': 'DE',
|
|
132
|
-
'Europe/Amsterdam': 'NL',
|
|
133
|
-
'Pacific/Honolulu': 'US',
|
|
134
|
-
'America/Regina': 'CA',
|
|
135
|
-
'America/Scoresbysund': 'GL',
|
|
136
|
-
'Europe/Vienna': 'AT',
|
|
137
|
-
'Europe/Tirane': 'AL',
|
|
138
|
-
'Africa/El_Aaiun': 'EH',
|
|
139
|
-
'America/Creston': 'CA',
|
|
140
|
-
'Asia/Qostanay': 'KZ',
|
|
141
|
-
'Asia/Ho_Chi_Minh': 'VN',
|
|
142
|
-
'Europe/Samara': 'RU',
|
|
143
|
-
'Europe/Rome': 'IT',
|
|
144
|
-
'Australia/Eucla': 'AU',
|
|
145
|
-
'America/El_Salvador': 'SV',
|
|
146
|
-
'America/Chicago': 'US',
|
|
147
|
-
'Africa/Abidjan': 'CI',
|
|
148
|
-
'Asia/Kamchatka': 'RU',
|
|
149
|
-
'Pacific/Tarawa': 'KI',
|
|
150
|
-
'America/Santiago': 'CL',
|
|
151
|
-
'America/Bahia': 'BR',
|
|
152
|
-
'Indian/Christmas': 'CX',
|
|
153
|
-
'Asia/Atyrau': 'KZ',
|
|
154
|
-
'Asia/Dushanbe': 'TJ',
|
|
155
|
-
'Europe/Ulyanovsk': 'RU',
|
|
156
|
-
'America/Yellowknife': 'CA',
|
|
157
|
-
'America/Recife': 'BR',
|
|
158
|
-
'Australia/Sydney': 'AU',
|
|
159
|
-
'America/Fort_Nelson': 'CA',
|
|
160
|
-
'Pacific/Efate': 'VU',
|
|
161
|
-
'Europe/Saratov': 'RU',
|
|
162
|
-
'Africa/Banjul': 'GM',
|
|
163
|
-
'Asia/Omsk': 'RU',
|
|
164
|
-
'Europe/Ljubljana': 'SI',
|
|
165
|
-
'Europe/Budapest': 'HU',
|
|
166
|
-
'Europe/Astrakhan': 'RU',
|
|
167
|
-
'America/Argentina/Buenos_Aires': 'AR',
|
|
168
|
-
'Pacific/Chatham': 'NZ',
|
|
169
|
-
'America/Argentina/Salta': 'AR',
|
|
170
|
-
'Africa/Niamey': 'NE',
|
|
171
|
-
'Asia/Pontianak': 'ID',
|
|
172
|
-
'Indian/Reunion': 'RE',
|
|
173
|
-
'Asia/Hong_Kong': 'HK',
|
|
174
|
-
'Antarctica/McMurdo': 'AQ',
|
|
175
|
-
'Africa/Malabo': 'GQ',
|
|
176
|
-
'America/Los_Angeles': 'US',
|
|
177
|
-
'America/Argentina/Cordoba': 'AR',
|
|
178
|
-
'Pacific/Pohnpei': 'FM',
|
|
179
|
-
'America/Tijuana': 'MX',
|
|
180
|
-
'America/Campo_Grande': 'BR',
|
|
181
|
-
'America/Dawson_Creek': 'CA',
|
|
182
|
-
'Asia/Novosibirsk': 'RU',
|
|
183
|
-
'Pacific/Pago_Pago': 'AS',
|
|
184
|
-
'Asia/Jerusalem': 'IL',
|
|
185
|
-
'Europe/Sarajevo': 'BA',
|
|
186
|
-
'Africa/Freetown': 'SL',
|
|
187
|
-
'Asia/Yekaterinburg': 'RU',
|
|
188
|
-
'America/Juneau': 'US',
|
|
189
|
-
'Africa/Ouagadougou': 'BF',
|
|
190
|
-
'Africa/Monrovia': 'LR',
|
|
191
|
-
'Europe/Kiev': 'UA',
|
|
192
|
-
'America/Argentina/San_Luis': 'AR',
|
|
193
|
-
'Asia/Tokyo': 'JP',
|
|
194
|
-
'Asia/Qatar': 'QA',
|
|
195
|
-
'America/La_Paz': 'BO',
|
|
196
|
-
'America/Bogota': 'CO',
|
|
197
|
-
'America/Thule': 'GL',
|
|
198
|
-
'Asia/Manila': 'PH',
|
|
199
|
-
'Asia/Hovd': 'MN',
|
|
200
|
-
'Asia/Tehran': 'IR',
|
|
201
|
-
'Atlantic/Madeira': 'PT',
|
|
202
|
-
'America/Metlakatla': 'US',
|
|
203
|
-
'Europe/Vatican': 'VA',
|
|
204
|
-
'Asia/Bishkek': 'KG',
|
|
205
|
-
'Asia/Dili': 'TL',
|
|
206
|
-
'Antarctica/Palmer': 'AQ',
|
|
207
|
-
'Atlantic/Cape_Verde': 'CV',
|
|
208
|
-
'Indian/Chagos': 'IO',
|
|
209
|
-
'America/Kentucky/Monticello': 'US',
|
|
210
|
-
'Africa/Algiers': 'DZ',
|
|
211
|
-
'Africa/Maseru': 'LS',
|
|
212
|
-
'Asia/Kuala_Lumpur': 'MY',
|
|
213
|
-
'Africa/Khartoum': 'SD',
|
|
214
|
-
'America/Argentina/Rio_Gallegos': 'AR',
|
|
215
|
-
'America/Blanc-Sablon': 'CA',
|
|
216
|
-
'Africa/Maputo': 'MZ',
|
|
217
|
-
'America/Tortola': 'VG',
|
|
218
|
-
'Atlantic/Bermuda': 'BM',
|
|
219
|
-
'America/Argentina/Catamarca': 'AR',
|
|
220
|
-
'America/Cayman': 'KY',
|
|
221
|
-
'America/Puerto_Rico': 'PR',
|
|
222
|
-
'Pacific/Majuro': 'MH',
|
|
223
|
-
'Europe/Busingen': 'DE',
|
|
224
|
-
'Pacific/Midway': 'UM',
|
|
225
|
-
'Indian/Cocos': 'CC',
|
|
226
|
-
'Asia/Singapore': 'SG',
|
|
227
|
-
'America/Boise': 'US',
|
|
228
|
-
'America/Nuuk': 'GL',
|
|
229
|
-
'America/Goose_Bay': 'CA',
|
|
230
|
-
'Australia/Broken_Hill': 'AU',
|
|
231
|
-
'Africa/Dar_es_Salaam': 'TZ',
|
|
232
|
-
'Africa/Asmara': 'ER',
|
|
233
|
-
'Asia/Samarkand': 'UZ',
|
|
234
|
-
'Asia/Tbilisi': 'GE',
|
|
235
|
-
'America/Argentina/Jujuy': 'AR',
|
|
236
|
-
'America/Indiana/Winamac': 'US',
|
|
237
|
-
'America/Porto_Velho': 'BR',
|
|
238
|
-
'Asia/Magadan': 'RU',
|
|
239
|
-
'Europe/Zaporozhye': 'UA',
|
|
240
|
-
'Antarctica/Casey': 'AQ',
|
|
241
|
-
'Asia/Shanghai': 'CN',
|
|
242
|
-
'Pacific/Norfolk': 'NF',
|
|
243
|
-
'Europe/Guernsey': 'GG',
|
|
244
|
-
'Australia/Brisbane': 'AU',
|
|
245
|
-
'Antarctica/DumontDUrville': 'AQ',
|
|
246
|
-
'America/Havana': 'CU',
|
|
247
|
-
'America/Atikokan': 'CA',
|
|
248
|
-
'America/Mexico_City': 'MX',
|
|
249
|
-
'America/Rankin_Inlet': 'CA',
|
|
250
|
-
'America/Cuiaba': 'BR',
|
|
251
|
-
'America/Resolute': 'CA',
|
|
252
|
-
'Africa/Ceuta': 'ES',
|
|
253
|
-
'Arctic/Longyearbyen': 'SJ',
|
|
254
|
-
'Pacific/Guam': 'GU',
|
|
255
|
-
'Asia/Damascus': 'SY',
|
|
256
|
-
'Asia/Colombo': 'LK',
|
|
257
|
-
'Asia/Yerevan': 'AM',
|
|
258
|
-
'America/Montserrat': 'MS',
|
|
259
|
-
'America/Belem': 'BR',
|
|
260
|
-
'Europe/Kaliningrad': 'RU',
|
|
261
|
-
'Atlantic/South_Georgia': 'GS',
|
|
262
|
-
'Asia/Tashkent': 'UZ',
|
|
263
|
-
'Asia/Kolkata': 'IN',
|
|
264
|
-
'America/St_Johns': 'CA',
|
|
265
|
-
'Asia/Srednekolymsk': 'RU',
|
|
266
|
-
'Asia/Yakutsk': 'RU',
|
|
267
|
-
'Europe/Prague': 'CZ',
|
|
268
|
-
'Africa/Djibouti': 'DJ',
|
|
269
|
-
'Asia/Dubai': 'AE',
|
|
270
|
-
'Europe/Uzhgorod': 'UA',
|
|
271
|
-
'America/Edmonton': 'CA',
|
|
272
|
-
'Asia/Famagusta': 'CY',
|
|
273
|
-
'America/Indiana/Knox': 'US',
|
|
274
|
-
'Asia/Hebron': 'PS',
|
|
275
|
-
'Asia/Taipei': 'TW',
|
|
276
|
-
'Europe/London': 'GB',
|
|
277
|
-
'Africa/Dakar': 'SN',
|
|
278
|
-
'Australia/Darwin': 'AU',
|
|
279
|
-
'America/Glace_Bay': 'CA',
|
|
280
|
-
'Antarctica/Vostok': 'AQ',
|
|
281
|
-
'America/Indiana/Vincennes': 'US',
|
|
282
|
-
'America/Nipigon': 'CA',
|
|
283
|
-
'Asia/Kuwait': 'KW',
|
|
284
|
-
'Pacific/Guadalcanal': 'SB',
|
|
285
|
-
'America/Toronto': 'CA',
|
|
286
|
-
'Africa/Gaborone': 'BW',
|
|
287
|
-
'Africa/Bujumbura': 'BI',
|
|
288
|
-
'Africa/Lubumbashi': 'CD',
|
|
289
|
-
'America/Merida': 'MX',
|
|
290
|
-
'America/Marigot': 'MF',
|
|
291
|
-
'Europe/Zagreb': 'HR',
|
|
292
|
-
'Pacific/Easter': 'CL',
|
|
293
|
-
'America/Santarem': 'BR',
|
|
294
|
-
'Pacific/Noumea': 'NC',
|
|
295
|
-
'America/Sitka': 'US',
|
|
296
|
-
'Atlantic/Stanley': 'FK',
|
|
297
|
-
'Pacific/Funafuti': 'TV',
|
|
298
|
-
'America/Iqaluit': 'CA',
|
|
299
|
-
'America/Rainy_River': 'CA',
|
|
300
|
-
'America/Anchorage': 'US',
|
|
301
|
-
'America/Lima': 'PE',
|
|
302
|
-
'Asia/Baku': 'AZ',
|
|
303
|
-
'America/Indiana/Vevay': 'US',
|
|
304
|
-
'Asia/Ulaanbaatar': 'MN',
|
|
305
|
-
'America/Managua': 'NI',
|
|
306
|
-
'Asia/Krasnoyarsk': 'RU',
|
|
307
|
-
'Asia/Qyzylorda': 'KZ',
|
|
308
|
-
'America/Eirunepe': 'BR',
|
|
309
|
-
'Europe/Podgorica': 'ME',
|
|
310
|
-
'Europe/Chisinau': 'MD',
|
|
311
|
-
'Europe/Mariehamn': 'AX',
|
|
312
|
-
'Europe/Volgograd': 'RU',
|
|
313
|
-
'Africa/Nairobi': 'KE',
|
|
314
|
-
'Europe/Isle_of_Man': 'IM',
|
|
315
|
-
'America/Menominee': 'US',
|
|
316
|
-
'Africa/Harare': 'ZW',
|
|
317
|
-
'Asia/Anadyr': 'RU',
|
|
318
|
-
'America/Moncton': 'CA',
|
|
319
|
-
'Indian/Maldives': 'MV',
|
|
320
|
-
'America/Whitehorse': 'CA',
|
|
321
|
-
'Antarctica/Mawson': 'AQ',
|
|
322
|
-
'Europe/Madrid': 'ES',
|
|
323
|
-
'America/Argentina/Mendoza': 'AR',
|
|
324
|
-
'America/Manaus': 'BR',
|
|
325
|
-
'Africa/Bangui': 'CF',
|
|
326
|
-
'Indian/Mauritius': 'MU',
|
|
327
|
-
'Africa/Tunis': 'TN',
|
|
328
|
-
'Australia/Lord_Howe': 'AU',
|
|
329
|
-
'America/Kentucky/Louisville': 'US',
|
|
330
|
-
'America/North_Dakota/Center': 'US',
|
|
331
|
-
'Asia/Novokuznetsk': 'RU',
|
|
332
|
-
'Asia/Makassar': 'ID',
|
|
333
|
-
'America/Port_of_Spain': 'TT',
|
|
334
|
-
'America/Bahia_Banderas': 'MX',
|
|
335
|
-
'Pacific/Auckland': 'NZ',
|
|
336
|
-
'America/Sao_Paulo': 'BR',
|
|
337
|
-
'Asia/Dhaka': 'BD',
|
|
338
|
-
'America/Pangnirtung': 'CA',
|
|
339
|
-
'Europe/Dublin': 'IE',
|
|
340
|
-
'Asia/Brunei': 'BN',
|
|
341
|
-
'Africa/Brazzaville': 'CG',
|
|
342
|
-
'America/Montevideo': 'UY',
|
|
343
|
-
'America/Jamaica': 'JM',
|
|
344
|
-
'America/Indiana/Indianapolis': 'US',
|
|
345
|
-
'America/Kralendijk': 'BQ',
|
|
346
|
-
'Europe/Gibraltar': 'GI',
|
|
347
|
-
'Pacific/Marquesas': 'PF',
|
|
348
|
-
'Pacific/Apia': 'WS',
|
|
349
|
-
'Europe/Jersey': 'JE',
|
|
350
|
-
'America/Phoenix': 'US',
|
|
351
|
-
'Africa/Ndjamena': 'TD',
|
|
352
|
-
'Asia/Karachi': 'PK',
|
|
353
|
-
'Africa/Kampala': 'UG',
|
|
354
|
-
'Asia/Sakhalin': 'RU',
|
|
355
|
-
'America/Martinique': 'MQ',
|
|
356
|
-
'Europe/Moscow': 'RU',
|
|
357
|
-
'Africa/Conakry': 'GN',
|
|
358
|
-
'America/Barbados': 'BB',
|
|
359
|
-
'Africa/Lome': 'TG',
|
|
360
|
-
'America/Ojinaga': 'MX',
|
|
361
|
-
'America/Tegucigalpa': 'HN',
|
|
362
|
-
'Asia/Bangkok': 'TH',
|
|
363
|
-
'Africa/Johannesburg': 'ZA',
|
|
364
|
-
'Europe/Vaduz': 'LI',
|
|
365
|
-
'Africa/Sao_Tome': 'ST',
|
|
366
|
-
'America/Cambridge_Bay': 'CA',
|
|
367
|
-
'America/Lower_Princes': 'SX',
|
|
368
|
-
'America/Miquelon': 'PM',
|
|
369
|
-
'America/St_Kitts': 'KN',
|
|
370
|
-
'Australia/Melbourne': 'AU',
|
|
371
|
-
'Europe/Minsk': 'BY',
|
|
372
|
-
'Asia/Vladivostok': 'RU',
|
|
373
|
-
'Europe/Sofia': 'BG',
|
|
374
|
-
'Antarctica/Davis': 'AQ',
|
|
375
|
-
'Pacific/Galapagos': 'EC',
|
|
376
|
-
'America/North_Dakota/New_Salem': 'US',
|
|
377
|
-
'Asia/Amman': 'JO',
|
|
378
|
-
'Pacific/Wallis': 'WF',
|
|
379
|
-
'America/Hermosillo': 'MX',
|
|
380
|
-
'Pacific/Kiritimati': 'KI',
|
|
381
|
-
'Antarctica/Macquarie': 'AU',
|
|
382
|
-
'America/Guyana': 'GY',
|
|
383
|
-
'Asia/Riyadh': 'SA',
|
|
384
|
-
'Pacific/Tahiti': 'PF',
|
|
385
|
-
'America/St_Vincent': 'VC',
|
|
386
|
-
'America/Cancun': 'MX',
|
|
387
|
-
'America/Grenada': 'GD',
|
|
388
|
-
'Pacific/Wake': 'UM',
|
|
389
|
-
'America/Dawson': 'CA',
|
|
390
|
-
'Europe/Brussels': 'BE',
|
|
391
|
-
'Indian/Kerguelen': 'TF',
|
|
392
|
-
'America/Yakutat': 'US',
|
|
393
|
-
'Indian/Mahe': 'SC',
|
|
394
|
-
'Atlantic/Reykjavik': 'IS',
|
|
395
|
-
'America/Panama': 'PA',
|
|
396
|
-
'America/Guadeloupe': 'GP',
|
|
397
|
-
'Europe/Malta': 'MT',
|
|
398
|
-
'Antarctica/Troll': 'AQ',
|
|
399
|
-
'Asia/Jayapura': 'ID',
|
|
400
|
-
'Asia/Bahrain': 'BH',
|
|
401
|
-
'Asia/Chita': 'RU',
|
|
402
|
-
'Europe/Tallinn': 'EE',
|
|
403
|
-
'Asia/Khandyga': 'RU',
|
|
404
|
-
'America/Rio_Branco': 'BR',
|
|
405
|
-
'Atlantic/St_Helena': 'SH',
|
|
406
|
-
'Africa/Juba': 'SS',
|
|
407
|
-
'America/Adak': 'US',
|
|
408
|
-
'Pacific/Saipan': 'MP',
|
|
409
|
-
'America/St_Lucia': 'LC',
|
|
410
|
-
'America/Inuvik': 'CA',
|
|
411
|
-
'Europe/Luxembourg': 'LU',
|
|
412
|
-
'Africa/Bissau': 'GW',
|
|
413
|
-
'Asia/Oral': 'KZ',
|
|
414
|
-
'America/Boa_Vista': 'BR',
|
|
415
|
-
'Europe/Skopje': 'MK',
|
|
416
|
-
'America/Port-au-Prince': 'HT',
|
|
417
|
-
'Pacific/Port_Moresby': 'PG',
|
|
418
|
-
'Europe/Andorra': 'AD',
|
|
419
|
-
'America/Indiana/Marengo': 'US',
|
|
420
|
-
'Africa/Kigali': 'RW',
|
|
421
|
-
'Africa/Bamako': 'ML',
|
|
422
|
-
'America/Dominica': 'DM',
|
|
423
|
-
'Asia/Aqtobe': 'KZ',
|
|
424
|
-
'Europe/Istanbul': 'TR',
|
|
425
|
-
'Pacific/Rarotonga': 'CK',
|
|
426
|
-
'America/Danmarkshavn': 'GL',
|
|
427
|
-
'Europe/Zurich': 'CH',
|
|
428
|
-
'Asia/Yangon': 'MM',
|
|
429
|
-
'America/Monterrey': 'MX',
|
|
430
|
-
'Europe/Lisbon': 'PT',
|
|
431
|
-
'Asia/Kuching': 'MY',
|
|
432
|
-
'Antarctica/Rothera': 'AQ',
|
|
433
|
-
'Australia/Perth': 'AU',
|
|
434
|
-
'Asia/Phnom_Penh': 'KH',
|
|
435
|
-
'America/Swift_Current': 'CA',
|
|
436
|
-
'Asia/Aqtau': 'KZ',
|
|
437
|
-
'Asia/Urumqi': 'CN',
|
|
438
|
-
'Asia/Calcutta': 'IN'
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
function getBrowserContext() {
|
|
442
|
-
var _a;
|
|
443
|
-
const isBrowser = typeof window !== 'undefined';
|
|
444
|
-
if (!isBrowser) throw new Error('getBrowserContext can only be called in browser');
|
|
445
|
-
const customNavigator = navigator;
|
|
446
|
-
const $locale = (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages) && (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages.length) ? customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.languages[0] : (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.userLanguage) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.language) || (customNavigator === null || customNavigator === void 0 ? void 0 : customNavigator.browserLanguage) || 'en';
|
|
447
|
-
// https://caniuse.com/?search=Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
448
|
-
// Only has 96.63% global support, so we need to check for undefined
|
|
449
|
-
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
450
|
-
const $location = timezones[timezone] || ((_a = $locale === null || $locale === void 0 ? void 0 : $locale.split('-')) === null || _a === void 0 ? void 0 : _a[1]) || 'undefined';
|
|
451
|
-
const $href = window.location.href;
|
|
452
|
-
const $pathname = window.location.pathname;
|
|
453
|
-
const $referrer = document.referrer;
|
|
454
|
-
const $screenDPI = window.devicePixelRatio;
|
|
455
|
-
const $screenHeight = screen.height;
|
|
456
|
-
const $screenWidth = screen.width;
|
|
457
|
-
const $title = document.title;
|
|
458
|
-
const $userAgent = window.navigator.userAgent;
|
|
459
|
-
const params = new URLSearchParams(window.location.search);
|
|
460
|
-
const $utmSource = params.get('utm_source');
|
|
461
|
-
const $utmMedium = params.get('utm_medium');
|
|
462
|
-
const $utmCampaign = params.get('utm_campaign');
|
|
463
|
-
const $utmTerm = params.get('utm_term');
|
|
464
|
-
const $utmContent = params.get('utm_content');
|
|
465
|
-
const $utmId = params.get('utm_id');
|
|
466
|
-
const $utmSourcePlatform = params.get('utm_source_platform');
|
|
467
|
-
const $utmCampaignId = params.get('utm_campaign_id');
|
|
468
|
-
const $utmCreativeFormat = params.get('utm_creative_format');
|
|
469
|
-
const $utmMarketingTactic = params.get('utm_marketing_tactic');
|
|
470
|
-
let $utmAdSource = params.get('utm_ad_source');
|
|
471
|
-
let $utmAdId = params.get('utm_ad_id');
|
|
472
|
-
// If the adId is not set directly attempt to get it from each ad network
|
|
473
|
-
// Also set the ad source to the ad network if it's not set
|
|
474
|
-
if (!$utmAdId) {
|
|
475
|
-
const fbAdId = params.get('fbadid');
|
|
476
|
-
if (fbAdId) {
|
|
477
|
-
$utmAdId = fbAdId;
|
|
478
|
-
if (!$utmAdSource) {
|
|
479
|
-
$utmAdSource = 'facebook';
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
const googleAdId = params.get('gadid');
|
|
483
|
-
if (googleAdId) {
|
|
484
|
-
$utmAdId = googleAdId;
|
|
485
|
-
if (!$utmAdSource) {
|
|
486
|
-
$utmAdSource = 'google';
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
const xAdId = params.get('xadid');
|
|
490
|
-
if (xAdId) {
|
|
491
|
-
$utmAdId = xAdId;
|
|
492
|
-
if (!$utmAdSource) {
|
|
493
|
-
$utmAdSource = 'x';
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
const pinterestAdId = params.get('padid');
|
|
497
|
-
if (pinterestAdId) {
|
|
498
|
-
$utmAdId = pinterestAdId;
|
|
499
|
-
if (!$utmAdSource) {
|
|
500
|
-
$utmAdSource = 'pinterest';
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
const tiktokAdId = params.get('ttadid');
|
|
504
|
-
if (tiktokAdId) {
|
|
505
|
-
$utmAdId = tiktokAdId;
|
|
506
|
-
if (!$utmAdSource) {
|
|
507
|
-
$utmAdSource = 'tiktok';
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
const snapchatAdId = params.get('scadid');
|
|
511
|
-
if (snapchatAdId) {
|
|
512
|
-
$utmAdId = snapchatAdId;
|
|
513
|
-
if (!$utmAdSource) {
|
|
514
|
-
$utmAdSource = 'snapchat';
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
const redditAdId = params.get('radid');
|
|
518
|
-
if (redditAdId) {
|
|
519
|
-
$utmAdId = redditAdId;
|
|
520
|
-
if (!$utmAdSource) {
|
|
521
|
-
$utmAdSource = 'reddit';
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
const linkedinAdId = params.get('liadid');
|
|
525
|
-
if (linkedinAdId) {
|
|
526
|
-
$utmAdId = linkedinAdId;
|
|
527
|
-
if (!$utmAdSource) {
|
|
528
|
-
$utmAdSource = 'linkedin';
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
return {
|
|
533
|
-
$locale,
|
|
534
|
-
$location,
|
|
535
|
-
$href,
|
|
536
|
-
$pathname,
|
|
537
|
-
$referrer,
|
|
538
|
-
$screenDPI,
|
|
539
|
-
$screenHeight,
|
|
540
|
-
$screenWidth,
|
|
541
|
-
$title,
|
|
542
|
-
$userAgent,
|
|
543
|
-
$utmSource,
|
|
544
|
-
$utmMedium,
|
|
545
|
-
$utmCampaign,
|
|
546
|
-
$utmTerm,
|
|
547
|
-
$utmContent,
|
|
548
|
-
$utmId,
|
|
549
|
-
$utmSourcePlatform,
|
|
550
|
-
$utmCampaignId,
|
|
551
|
-
$utmCreativeFormat,
|
|
552
|
-
$utmMarketingTactic,
|
|
553
|
-
$utmAdSource,
|
|
554
|
-
$utmAdId
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
var LogLevel;
|
|
559
|
-
(function (LogLevel) {
|
|
560
|
-
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
|
561
|
-
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
|
562
|
-
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
563
|
-
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
|
564
|
-
})(LogLevel || (LogLevel = {}));
|
|
565
|
-
class AnalyticsLogger {
|
|
566
|
-
constructor(config) {
|
|
567
|
-
this.config = config;
|
|
568
|
-
}
|
|
569
|
-
debug(...args) {
|
|
570
|
-
if (this.config.level < LogLevel.DEBUG) return;
|
|
571
|
-
const icon = '◍';
|
|
572
|
-
const message = `${icon} ${this.config.label}`;
|
|
573
|
-
console.debug(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Log debug information
|
|
577
|
-
*/
|
|
578
|
-
info(...args) {
|
|
579
|
-
if (this.config.level < LogLevel.INFO) return;
|
|
580
|
-
const icon = '◍';
|
|
581
|
-
const message = `${icon} ${this.config.label}`;
|
|
582
|
-
console.info(`%c ${message}`, `background: #eee; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
583
|
-
}
|
|
584
|
-
/**
|
|
585
|
-
* Log warning information
|
|
586
|
-
*/
|
|
587
|
-
warn(...args) {
|
|
588
|
-
if (this.config.level < LogLevel.WARN) return;
|
|
589
|
-
const icon = '◍';
|
|
590
|
-
const message = `${icon} ${this.config.label}`;
|
|
591
|
-
console.warn(`%c ${message}`, `background: #ffa500; color: #000; padding-right: 6px; border-radius: 2px;`, ...args);
|
|
592
|
-
}
|
|
593
|
-
/**
|
|
594
|
-
* Log error information
|
|
595
|
-
*/
|
|
596
|
-
error(...args) {
|
|
597
|
-
const icon = '◍';
|
|
598
|
-
const message = `${icon} ${this.config.label} Error`;
|
|
599
|
-
console.error(`%c ${message}`, 'background: red; color: white; padding-right: 6px; border-radius: 2px;', ...args);
|
|
600
|
-
return {
|
|
601
|
-
success: false,
|
|
602
|
-
message: args.join(' ')
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const isBrowser$4 = typeof window !== 'undefined';
|
|
608
|
-
const isServer$4 = !isBrowser$4;
|
|
609
|
-
/**
|
|
610
|
-
* Get the logger level for a specific integration
|
|
611
|
-
* Supports URL parameters like saasco-debug-{integration-type}=true
|
|
612
|
-
* If debug is true or URL param is true, sets level to DEBUG (3), otherwise WARN
|
|
613
|
-
*/
|
|
614
|
-
function getIntegrationLoggerLevel(integrationName, debug) {
|
|
615
|
-
// Check URL parameters for debug overrides (browser only)
|
|
616
|
-
let urlDebug = false;
|
|
617
|
-
if (!isServer$4) {
|
|
618
|
-
try {
|
|
619
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
620
|
-
const paramName = `saasco-debug-${integrationName}`;
|
|
621
|
-
const paramValue = urlParams.get(paramName);
|
|
622
|
-
if (paramValue === 'true') {
|
|
623
|
-
urlDebug = true;
|
|
624
|
-
}
|
|
625
|
-
} catch (error) {
|
|
626
|
-
// Ignore URL parsing errors
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// URL parameters override config, config overrides default (WARN)
|
|
630
|
-
return urlDebug || debug ? LogLevel.DEBUG : LogLevel.WARN;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
/**
|
|
634
|
-
* Generate a collision-resistant UUID
|
|
635
|
-
* Uses lukeed's UUID v4 implementation for consistent, fast UUID generation
|
|
636
|
-
*/
|
|
637
|
-
function uuid() {
|
|
638
|
-
return uuid$1.v4();
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
const isBrowser$3 = typeof window !== 'undefined';
|
|
642
|
-
const isServer$3 = !isBrowser$3;
|
|
643
|
-
const standardFacebookEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'CompleteRegistration', 'Contact', 'CustomizeProduct', 'Donate', 'FindLocation', 'InitiateCheckout', 'Lead', 'Purchase', 'Schedule', 'Search', 'StartTrial', 'SubmitApplication', 'Subscribe', 'ViewContent', 'PageView'];
|
|
644
|
-
/**
|
|
645
|
-
* Create a Facebook Pixel integration instance
|
|
646
|
-
*/
|
|
647
|
-
function createFacebookPixelIntegration(config, debug) {
|
|
648
|
-
let isPixelReady = false;
|
|
649
|
-
// Create integration logger
|
|
650
|
-
const integrationLoggerLevel = getIntegrationLoggerLevel('facebook-pixel', debug);
|
|
651
|
-
const logger = new AnalyticsLogger({
|
|
652
|
-
level: integrationLoggerLevel,
|
|
653
|
-
label: 'Saasco facebook-pixel'
|
|
654
|
-
});
|
|
655
|
-
return {
|
|
656
|
-
name: 'facebook-pixel',
|
|
657
|
-
environments: ['client'],
|
|
658
|
-
init: () => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
659
|
-
if (isServer$3) {
|
|
660
|
-
logger.debug('Facebook Pixel init skipped on server');
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
logger.info('Initializing Facebook Pixel', {
|
|
664
|
-
pixelId: config.pixelId
|
|
665
|
-
});
|
|
666
|
-
// Check if Facebook Pixel is already loaded
|
|
667
|
-
if (window.fbq || window._fbq) {
|
|
668
|
-
logger.warn('Facebook Pixel is already initialized');
|
|
669
|
-
isPixelReady = true;
|
|
670
|
-
return;
|
|
671
|
-
}
|
|
672
|
-
// Initialize Facebook Pixel
|
|
673
|
-
return new Promise((resolve, reject) => {
|
|
674
|
-
try {
|
|
675
|
-
(function (f, b, e, v, n, t, s) {
|
|
676
|
-
if (f.fbq) return;
|
|
677
|
-
n = f.fbq = function () {
|
|
678
|
-
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
|
|
679
|
-
};
|
|
680
|
-
if (!f._fbq) f._fbq = n;
|
|
681
|
-
n.push = n;
|
|
682
|
-
n.loaded = !0;
|
|
683
|
-
n.version = '2.0';
|
|
684
|
-
n.queue = [];
|
|
685
|
-
t = b.createElement(e);
|
|
686
|
-
t.async = !0;
|
|
687
|
-
t.src = v;
|
|
688
|
-
// Add load event listener
|
|
689
|
-
t.onload = () => {
|
|
690
|
-
isPixelReady = true;
|
|
691
|
-
logger.info('Facebook Pixel script loaded successfully');
|
|
692
|
-
resolve();
|
|
693
|
-
};
|
|
694
|
-
t.onerror = () => {
|
|
695
|
-
logger.error('Failed to load Facebook Pixel script');
|
|
696
|
-
reject(new Error('Failed to load Facebook Pixel script'));
|
|
697
|
-
};
|
|
698
|
-
s = b.getElementsByTagName(e)[0];
|
|
699
|
-
s.parentNode.insertBefore(t, s);
|
|
700
|
-
})(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');
|
|
701
|
-
// Prevent automatic PageView tracking
|
|
702
|
-
window.fbq.disablePushState = true;
|
|
703
|
-
// Allow duplicate PageView events
|
|
704
|
-
window.fbq.allowDuplicatePageViews = true;
|
|
705
|
-
if (config.automaticConfiguration === false) {
|
|
706
|
-
logger.debug('Disabling automatic configuration');
|
|
707
|
-
window.fbq('set', 'autoConfig', false, config.pixelId);
|
|
708
|
-
}
|
|
709
|
-
// Initialize pixel
|
|
710
|
-
logger.debug('Initializing Facebook Pixel with ID', config.pixelId);
|
|
711
|
-
window.fbq('init', config.pixelId);
|
|
712
|
-
// If script loads synchronously, mark as ready
|
|
713
|
-
if (window.fbq && typeof window.fbq === 'function') {
|
|
714
|
-
isPixelReady = true;
|
|
715
|
-
logger.info('Facebook Pixel initialized synchronously');
|
|
716
|
-
resolve();
|
|
717
|
-
}
|
|
718
|
-
} catch (error) {
|
|
719
|
-
logger.error('Error during Facebook Pixel initialization', error);
|
|
720
|
-
reject(error);
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
}),
|
|
724
|
-
track: (eventName, properties, context) => {
|
|
725
|
-
if (!isPixelReady || !window.fbq) {
|
|
726
|
-
logger.warn('Facebook Pixel not ready, skipping track event', {
|
|
727
|
-
eventName
|
|
728
|
-
});
|
|
729
|
-
return;
|
|
730
|
-
}
|
|
731
|
-
const fbEventName = getFacebookEventName(eventName, config.eventMapping);
|
|
732
|
-
const eventsWithParams = ['Purchase', 'StartTrial', 'Subscribe'];
|
|
733
|
-
const isStandardEvent = standardFacebookEvents.includes(fbEventName);
|
|
734
|
-
const trackType = isStandardEvent ? 'track' : 'trackCustom';
|
|
735
|
-
logger.debug('Tracking Facebook Pixel event', {
|
|
736
|
-
originalEvent: eventName,
|
|
737
|
-
fbEventName,
|
|
738
|
-
trackType,
|
|
739
|
-
isStandardEvent,
|
|
740
|
-
properties
|
|
741
|
-
});
|
|
742
|
-
if (eventsWithParams.includes(fbEventName) && properties) {
|
|
743
|
-
const fbParams = {};
|
|
744
|
-
if (properties['value'] !== undefined) fbParams['value'] = properties['value'];
|
|
745
|
-
if (properties['currency'] !== undefined) fbParams['currency'] = properties['currency'];
|
|
746
|
-
if (properties['predicted_ltv'] !== undefined) fbParams['predicted_ltv'] = properties['predicted_ltv'];
|
|
747
|
-
logger.info('Tracking Facebook Pixel event with parameters', {
|
|
748
|
-
event: fbEventName,
|
|
749
|
-
params: fbParams
|
|
750
|
-
});
|
|
751
|
-
window.fbq(trackType, fbEventName, fbParams);
|
|
752
|
-
} else {
|
|
753
|
-
logger.info('Tracking Facebook Pixel event', {
|
|
754
|
-
event: fbEventName
|
|
755
|
-
});
|
|
756
|
-
window.fbq(trackType, fbEventName);
|
|
757
|
-
}
|
|
758
|
-
},
|
|
759
|
-
identify: (userId, properties, context) => {
|
|
760
|
-
try {
|
|
761
|
-
if (typeof window !== 'undefined' && window.fbq) {
|
|
762
|
-
const amValues = coerceMetaConversionsAmValues(Object.assign(Object.assign({}, properties || {}), userId ? {
|
|
763
|
-
external_id: userId
|
|
764
|
-
} : {}));
|
|
765
|
-
// Advanced Matching
|
|
766
|
-
// Add event to identify user and hash email field
|
|
767
|
-
// FB pixel handles hashing automatically
|
|
768
|
-
// https://developers.facebook.com/docs/meta-pixel/advanced/advanced-matching/
|
|
769
|
-
const am = {};
|
|
770
|
-
if (amValues.external_id) am['external_id'] = amValues.external_id;
|
|
771
|
-
if (amValues.em) am['em'] = jsSha256.sha256(amValues.em);
|
|
772
|
-
if (amValues.fn) am['fn'] = amValues.fn;
|
|
773
|
-
if (amValues.ln) am['ln'] = amValues.ln;
|
|
774
|
-
if (amValues.ph) am['ph'] = amValues.ph;
|
|
775
|
-
if (amValues.ge) am['ge'] = amValues.ge;
|
|
776
|
-
if (amValues.db) am['db'] = amValues.db;
|
|
777
|
-
if (amValues.ct) am['ct'] = amValues.ct;
|
|
778
|
-
if (amValues.st) am['st'] = amValues.st;
|
|
779
|
-
if (amValues.zp) am['zp'] = amValues.zp;
|
|
780
|
-
if (amValues.country) am['country'] = amValues.country;
|
|
781
|
-
if (Object.keys(am).length > 0) {
|
|
782
|
-
window.fbq('init', config.pixelId, am);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
} catch (error) {
|
|
786
|
-
console.error('Error identifying user in Meta Pixel:', error);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
function getFacebookEventName(eventName, eventMapping) {
|
|
792
|
-
// Convert default saasco Page View event by default
|
|
793
|
-
if (eventName === 'Page View') return 'PageView';
|
|
794
|
-
if (!eventMapping) return eventName;
|
|
795
|
-
return eventMapping[eventName] || eventName;
|
|
796
|
-
}
|
|
797
|
-
/**
|
|
798
|
-
* Get Advanced Matching values from properties
|
|
799
|
-
*
|
|
800
|
-
* Documentation for Pixel
|
|
801
|
-
* https://developers.facebook.com/docs/meta-pixel/advanced/advanced-matching/
|
|
802
|
-
*
|
|
803
|
-
* Documentation for Conversions API
|
|
804
|
-
* https://developers.secure.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
|
|
805
|
-
*
|
|
806
|
-
* @param properties
|
|
807
|
-
* @returns
|
|
808
|
-
*/
|
|
809
|
-
function coerceMetaConversionsAmValues(properties) {
|
|
810
|
-
const coerce = [{
|
|
811
|
-
key: 'em',
|
|
812
|
-
coerceFrom: ['email', 'user_email', 'userEmail', 'email_address', 'emailAddress', 'e_mail', 'E_Mail', 'mail', 'Mail', 'contact_email', 'contactEmail', 'primary_email', 'primaryEmail'],
|
|
813
|
-
transform: value => {
|
|
814
|
-
if (typeof value !== 'string') return undefined;
|
|
815
|
-
const normalized = value.trim().toLowerCase();
|
|
816
|
-
return normalized || undefined;
|
|
817
|
-
}
|
|
818
|
-
}, {
|
|
819
|
-
key: 'fn',
|
|
820
|
-
coerceFrom: ['first_name', 'firstName', 'firstname', 'given_name', 'givenName', 'user_first_name', 'userFirstName', 'name_first', 'nameFirst', 'f_name', 'fName'],
|
|
821
|
-
transform: value => {
|
|
822
|
-
if (typeof value !== 'string') return undefined;
|
|
823
|
-
const normalized = value.trim().toLowerCase();
|
|
824
|
-
return normalized || undefined;
|
|
825
|
-
}
|
|
826
|
-
}, {
|
|
827
|
-
key: 'ln',
|
|
828
|
-
coerceFrom: ['last_name', 'lastName', 'lastname', 'surname', 'user_last_name', 'userLastName', 'name_last', 'nameLast', 'l_name', 'lName'],
|
|
829
|
-
transform: value => {
|
|
830
|
-
if (typeof value !== 'string') return undefined;
|
|
831
|
-
const normalized = value.trim().toLowerCase();
|
|
832
|
-
return normalized || undefined;
|
|
833
|
-
}
|
|
834
|
-
}, {
|
|
835
|
-
key: 'ph',
|
|
836
|
-
coerceFrom: ['phone', 'phone_number', 'phoneNumber', 'mobile', 'mobile_number', 'mobileNumber', 'contact_number', 'contactNumber', 'tel', 'telephone'],
|
|
837
|
-
transform: value => {
|
|
838
|
-
if (typeof value !== 'string' && typeof value !== 'number') return undefined;
|
|
839
|
-
const phoneStr = String(value).replace(/\D/g, '');
|
|
840
|
-
return phoneStr || undefined;
|
|
841
|
-
}
|
|
842
|
-
}, {
|
|
843
|
-
key: 'external_id',
|
|
844
|
-
coerceFrom: ['external_id', 'externalId', 'user_id', 'userId', 'id', 'distinctId', 'distinct_id', 'customer_id', 'customerId', 'member_id', 'memberId', 'loyalty_id', 'loyaltyId'],
|
|
845
|
-
transform: value => {
|
|
846
|
-
if (value == null) return undefined;
|
|
847
|
-
return String(value) || undefined;
|
|
848
|
-
}
|
|
849
|
-
}, {
|
|
850
|
-
key: 'ge',
|
|
851
|
-
coerceFrom: ['gender', 'user_gender', 'userGender', 'sex', 'user_sex', 'userSex'],
|
|
852
|
-
transform: value => {
|
|
853
|
-
if (typeof value !== 'string') return undefined;
|
|
854
|
-
const normalized = value.trim().toLowerCase();
|
|
855
|
-
if (normalized === 'female' || normalized === 'f') return 'f';
|
|
856
|
-
if (normalized === 'male' || normalized === 'm') return 'm';
|
|
857
|
-
return undefined;
|
|
858
|
-
}
|
|
859
|
-
}, {
|
|
860
|
-
key: 'db',
|
|
861
|
-
coerceFrom: ['birthday', 'birth_date', 'birthDate', 'date_of_birth', 'dateOfBirth', 'dob', 'Dob', 'birth_day', 'birthDay'],
|
|
862
|
-
transform: value => {
|
|
863
|
-
if (!value) return undefined;
|
|
864
|
-
let dateStr;
|
|
865
|
-
if (value instanceof Date) {
|
|
866
|
-
dateStr = value.toISOString().split('T')[0];
|
|
867
|
-
} else {
|
|
868
|
-
dateStr = String(value);
|
|
869
|
-
}
|
|
870
|
-
const dateMatch = dateStr.match(/(\d{4})-(\d{2})-(\d{2})/);
|
|
871
|
-
if (dateMatch) {
|
|
872
|
-
const [, year, month, day] = dateMatch;
|
|
873
|
-
return `${year}${month}${day}`;
|
|
874
|
-
}
|
|
875
|
-
const numericMatch = dateStr.replace(/\D/g, '');
|
|
876
|
-
if (numericMatch.length === 8) {
|
|
877
|
-
return numericMatch;
|
|
878
|
-
}
|
|
879
|
-
return undefined;
|
|
880
|
-
}
|
|
881
|
-
}, {
|
|
882
|
-
key: 'ct',
|
|
883
|
-
coerceFrom: ['city', 'user_city', 'userCity', 'location_city', 'locationCity', 'address_city', 'addressCity'],
|
|
884
|
-
transform: value => {
|
|
885
|
-
if (typeof value !== 'string') return undefined;
|
|
886
|
-
const normalized = value.trim().toLowerCase().replace(/\s+/g, '');
|
|
887
|
-
return normalized || undefined;
|
|
888
|
-
}
|
|
889
|
-
}, {
|
|
890
|
-
key: 'st',
|
|
891
|
-
coerceFrom: ['state', 'user_state', 'userState', 'province', 'user_province', 'userProvince', 'region', 'user_region', 'userRegion', 'location_state', 'locationState', 'address_state', 'addressState'],
|
|
892
|
-
transform: value => {
|
|
893
|
-
if (typeof value !== 'string') return undefined;
|
|
894
|
-
const normalized = value.trim().toLowerCase();
|
|
895
|
-
if (normalized.length === 2) return normalized;
|
|
896
|
-
return undefined;
|
|
897
|
-
}
|
|
898
|
-
}, {
|
|
899
|
-
key: 'zp',
|
|
900
|
-
coerceFrom: ['zip', 'zipcode', 'zip_code', 'postal_code', 'postalCode', 'postcode', 'user_zip', 'userZip', 'address_zip', 'addressZip'],
|
|
901
|
-
transform: value => {
|
|
902
|
-
if (typeof value !== 'string' && typeof value !== 'number') return undefined;
|
|
903
|
-
return String(value).trim() || undefined;
|
|
904
|
-
}
|
|
905
|
-
}, {
|
|
906
|
-
key: 'country',
|
|
907
|
-
coerceFrom: ['country', 'country_code', 'countryCode', 'user_country', 'userCountry', 'location_country', 'locationCountry', 'address_country', 'addressCountry'],
|
|
908
|
-
transform: value => {
|
|
909
|
-
if (typeof value !== 'string') return undefined;
|
|
910
|
-
const normalized = value.trim().toLowerCase();
|
|
911
|
-
if (normalized.length === 2) return normalized;
|
|
912
|
-
return undefined;
|
|
913
|
-
}
|
|
914
|
-
}];
|
|
915
|
-
const result = {};
|
|
916
|
-
for (const {
|
|
917
|
-
key,
|
|
918
|
-
coerceFrom,
|
|
919
|
-
transform
|
|
920
|
-
} of coerce) {
|
|
921
|
-
const foundValue = findFirstProperty$1(properties, coerceFrom);
|
|
922
|
-
if (foundValue !== null && transform) {
|
|
923
|
-
const transformedValue = transform(foundValue);
|
|
924
|
-
if (transformedValue) {
|
|
925
|
-
result[key] = transformedValue;
|
|
926
|
-
}
|
|
927
|
-
} else if (foundValue !== null && typeof foundValue === 'string') {
|
|
928
|
-
result[key] = foundValue;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
return result;
|
|
932
|
-
}
|
|
933
|
-
function findFirstProperty$1(properties, keys) {
|
|
934
|
-
for (const key of keys) {
|
|
935
|
-
if (properties[key] !== undefined) {
|
|
936
|
-
return properties[key];
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return null;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
const isBrowser$2 = typeof window !== 'undefined';
|
|
943
|
-
const isServer$2 = !isBrowser$2;
|
|
944
|
-
/**
|
|
945
|
-
* Standard Pinterest events
|
|
946
|
-
* Reference: https://www.pinterest.com/_/_/help/business/article/event-code
|
|
947
|
-
*/
|
|
948
|
-
const standardPinterestEvents = ['checkout', 'addtocart', 'pagevisit', 'signup', 'watchvideo', 'lead', 'search', 'viewcategory', 'custom', 'addpaymentinfo', 'addtowishlist', 'initiatecheckout', 'subscribe', 'viewcontent'];
|
|
949
|
-
/**
|
|
950
|
-
* Create a Pinterest Tag integration instance
|
|
951
|
-
* Documentation: https://help.pinterest.com/en/business/article/install-the-pinterest-tag
|
|
952
|
-
*/
|
|
953
|
-
function createPinterestTagIntegration(config, debug) {
|
|
954
|
-
let isTagReady = false;
|
|
955
|
-
// Create integration logger
|
|
956
|
-
const integrationLoggerLevel = getIntegrationLoggerLevel('pinterest-tag', debug);
|
|
957
|
-
const logger = new AnalyticsLogger({
|
|
958
|
-
level: integrationLoggerLevel,
|
|
959
|
-
label: 'Saasco Pinterest Tag'
|
|
960
|
-
});
|
|
961
|
-
return {
|
|
962
|
-
name: 'pinterest-tag',
|
|
963
|
-
environments: ['client'],
|
|
964
|
-
init: context => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
965
|
-
if (isServer$2) {
|
|
966
|
-
logger.debug('Pinterest Tag init skipped on server');
|
|
967
|
-
return;
|
|
968
|
-
}
|
|
969
|
-
logger.info('Initializing Pinterest Tag', {
|
|
970
|
-
tagId: config.tagId
|
|
971
|
-
});
|
|
972
|
-
// Check if Pinterest Tag is already loaded
|
|
973
|
-
if (window.pintrk || window._pintrk) {
|
|
974
|
-
logger.warn('Pinterest Tag is already initialized');
|
|
975
|
-
isTagReady = true;
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
// Initialize Pinterest Tag
|
|
979
|
-
return new Promise((resolve, reject) => {
|
|
980
|
-
try {
|
|
981
|
-
// Pinterest Tag initialization script
|
|
982
|
-
// Reference: https://help.pinterest.com/en/business/article/install-the-base-code
|
|
983
|
-
(function (e) {
|
|
984
|
-
if (!window.pintrk) {
|
|
985
|
-
window.pintrk = function () {
|
|
986
|
-
window.pintrk.queue.push(Array.prototype.slice.call(arguments));
|
|
987
|
-
};
|
|
988
|
-
const n = window.pintrk;
|
|
989
|
-
n.queue = [];
|
|
990
|
-
n.version = '3.0';
|
|
991
|
-
const t = document.createElement('script');
|
|
992
|
-
t.async = true;
|
|
993
|
-
t.src = e;
|
|
994
|
-
// Add load event listener
|
|
995
|
-
t.onload = () => {
|
|
996
|
-
// base script is loaded; now (re)load tag + fire page via runtime
|
|
997
|
-
window.pintrk('load', config.tagId);
|
|
998
|
-
// initialize the tag
|
|
999
|
-
window.pintrk('page');
|
|
1000
|
-
// delay isTagReady to ensure the tag is loaded before sending events
|
|
1001
|
-
setTimeout(() => {
|
|
1002
|
-
logger.debug('Pinterest Tag initialized');
|
|
1003
|
-
isTagReady = true;
|
|
1004
|
-
resolve();
|
|
1005
|
-
}, 500);
|
|
1006
|
-
};
|
|
1007
|
-
t.onerror = () => {
|
|
1008
|
-
logger.error('Failed to load Pinterest Tag script');
|
|
1009
|
-
reject(new Error('Failed to load Pinterest Tag script'));
|
|
1010
|
-
};
|
|
1011
|
-
const r = document.getElementsByTagName('script')[0];
|
|
1012
|
-
r.parentNode.insertBefore(t, r);
|
|
1013
|
-
}
|
|
1014
|
-
})('https://s.pinimg.com/ct/core.js');
|
|
1015
|
-
} catch (error) {
|
|
1016
|
-
logger.error('Failed to initialize Pinterest Tag', error);
|
|
1017
|
-
reject(error);
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
}),
|
|
1021
|
-
track: (eventName, properties) => {
|
|
1022
|
-
if (!isTagReady || !window.pintrk) return;
|
|
1023
|
-
const pinterestEventName = getPinterestEventName(eventName, config.eventMapping);
|
|
1024
|
-
const eventData = buildEventData(properties);
|
|
1025
|
-
logger.debug('Tracking event', {
|
|
1026
|
-
eventName,
|
|
1027
|
-
pinterestEventName,
|
|
1028
|
-
eventData
|
|
1029
|
-
});
|
|
1030
|
-
// Track the event
|
|
1031
|
-
window.pintrk('track', pinterestEventName, eventData);
|
|
1032
|
-
},
|
|
1033
|
-
identify: (userId, properties, context) => {
|
|
1034
|
-
// Identify not supported with pinterest tag
|
|
1035
|
-
logger.debug('Identify not supported with pinterest tag', {
|
|
1036
|
-
userId,
|
|
1037
|
-
properties,
|
|
1038
|
-
context
|
|
1039
|
-
});
|
|
1040
|
-
window.pintrk('set', {
|
|
1041
|
-
external_id: userId
|
|
1042
|
-
});
|
|
1043
|
-
window.pintrk('set', {
|
|
1044
|
-
em: properties === null || properties === void 0 ? void 0 : properties['email']
|
|
1045
|
-
});
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
};
|
|
1049
|
-
}
|
|
1050
|
-
function getPinterestEventName(eventName, eventMapping) {
|
|
1051
|
-
// Convert default saasco Page View event by default
|
|
1052
|
-
if (eventName === 'Page View') return 'pagevisit';
|
|
1053
|
-
if (!eventMapping) return eventName.toLowerCase();
|
|
1054
|
-
return eventMapping[eventName] || eventName.toLowerCase();
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Build event data for Pinterest tracking
|
|
1058
|
-
* Reference: https://www.pinterest.com/_/_/help/business/article/event-code
|
|
1059
|
-
*/
|
|
1060
|
-
function buildEventData(properties) {
|
|
1061
|
-
if (!properties) return {};
|
|
1062
|
-
const eventData = {};
|
|
1063
|
-
// Map common properties to Pinterest event data
|
|
1064
|
-
if (properties['event_id']) eventData['event_id'] = properties['event_id'];
|
|
1065
|
-
if (properties['value']) eventData['value'] = properties['value'];
|
|
1066
|
-
if (properties['currency']) eventData['currency'] = properties['currency'];
|
|
1067
|
-
if (properties['order_quantity']) eventData['order_quantity'] = properties['order_quantity'];
|
|
1068
|
-
if (properties['order_id']) eventData['order_id'] = properties['order_id'];
|
|
1069
|
-
if (properties['promo_code']) eventData['promo_code'] = properties['promo_code'];
|
|
1070
|
-
if (properties['property']) eventData['property'] = properties['property'];
|
|
1071
|
-
if (properties['search_query']) eventData['search_query'] = properties['search_query'];
|
|
1072
|
-
if (properties['video_title']) eventData['video_title'] = properties['video_title'];
|
|
1073
|
-
if (properties['lead_type']) eventData['lead_type'] = properties['lead_type'];
|
|
1074
|
-
if (properties['product_category']) eventData['product_category'] = properties['product_category'];
|
|
1075
|
-
// Handle line items for e-commerce tracking
|
|
1076
|
-
if (properties['line_items'] && Array.isArray(properties['line_items'])) {
|
|
1077
|
-
eventData['line_items'] = properties['line_items'];
|
|
1078
|
-
}
|
|
1079
|
-
// Generate event_id if not provided
|
|
1080
|
-
if (!eventData['event_id']) {
|
|
1081
|
-
eventData['event_id'] = uuid();
|
|
1082
|
-
}
|
|
1083
|
-
return eventData;
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
/**
|
|
1087
|
-
* TikTok Pixel Integration
|
|
1088
|
-
*
|
|
1089
|
-
* This integration provides TikTok Pixel tracking capabilities including:
|
|
1090
|
-
* - Event tracking for standard and custom events
|
|
1091
|
-
* - Advanced matching with hashed user identification data
|
|
1092
|
-
* - Automatic script loading and initialization
|
|
1093
|
-
*
|
|
1094
|
-
* Key Resources:
|
|
1095
|
-
* - Advanced Matching: https://business-api.tiktok.com/portal/docs?rid=5ipocbxyw8v&id=1739585700402178
|
|
1096
|
-
* - Standard Events: https://business-api.tiktok.com/portal/docs?id=1771101186666498
|
|
1097
|
-
*/
|
|
1098
|
-
const isBrowser$1 = typeof window !== 'undefined';
|
|
1099
|
-
const isServer$1 = !isBrowser$1;
|
|
1100
|
-
/**
|
|
1101
|
-
* Standard TikTok events supported by the pixel
|
|
1102
|
-
*
|
|
1103
|
-
* Note: This list reflects TikTok's official standard events. Events like 'CompletePayment'
|
|
1104
|
-
* are not standard events and should be tracked as custom events if needed.
|
|
1105
|
-
*
|
|
1106
|
-
* Documentation: https://business-api.tiktok.com/portal/docs?id=1771101186666498
|
|
1107
|
-
*/
|
|
1108
|
-
const standardTikTokEvents = ['AddPaymentInfo', 'AddToCart', 'AddToWishlist', 'ApplicationApproval', 'CompleteRegistration', 'Contact', 'CustomizeProduct', 'Download', 'FindLocation', 'InitiateCheckout', 'Lead', 'Purchase', 'Schedule', 'Search', 'StartTrial', 'SubmitApplication', 'Subscribe', 'ViewContent', 'PageView'];
|
|
1109
|
-
/**
|
|
1110
|
-
* Create a TikTok Pixel integration instance
|
|
1111
|
-
*/
|
|
1112
|
-
function createTikTokPixelIntegration(config, debug) {
|
|
1113
|
-
let isPixelReady = false;
|
|
1114
|
-
// Create integration logger
|
|
1115
|
-
const integrationLoggerLevel = getIntegrationLoggerLevel('tiktok-pixel', debug);
|
|
1116
|
-
const logger = new AnalyticsLogger({
|
|
1117
|
-
level: integrationLoggerLevel,
|
|
1118
|
-
label: 'Saasco tiktok-pixel'
|
|
1119
|
-
});
|
|
1120
|
-
return {
|
|
1121
|
-
name: 'tiktok-pixel',
|
|
1122
|
-
environments: ['client'],
|
|
1123
|
-
init: () => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1124
|
-
if (isServer$1) {
|
|
1125
|
-
logger.debug('TikTok Pixel init skipped on server');
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
logger.info('Initializing TikTok Pixel', {
|
|
1129
|
-
pixelId: config.pixelId
|
|
1130
|
-
});
|
|
1131
|
-
// Check if TikTok Pixel is already loaded
|
|
1132
|
-
if (window.ttq) {
|
|
1133
|
-
logger.warn('TikTok Pixel is already initialized');
|
|
1134
|
-
isPixelReady = true;
|
|
1135
|
-
return;
|
|
1136
|
-
}
|
|
1137
|
-
// Initialize TikTok Pixel
|
|
1138
|
-
return new Promise((resolve, reject) => {
|
|
1139
|
-
try {
|
|
1140
|
-
(function (w, d, t) {
|
|
1141
|
-
w.TiktokAnalyticsObject = t;
|
|
1142
|
-
const ttq = w[t] = w[t] || [];
|
|
1143
|
-
ttq.methods = ['page', 'track', 'identify', 'instances', 'debug', 'on', 'off', 'once', 'ready', 'alias', 'group', 'enableCookie', 'disableCookie', 'holdConsent', 'revokeConsent', 'grantConsent'];
|
|
1144
|
-
ttq.setAndDefer = function (t, e) {
|
|
1145
|
-
t[e] = function () {
|
|
1146
|
-
t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
|
|
1147
|
-
};
|
|
1148
|
-
};
|
|
1149
|
-
for (let i = 0; i < ttq.methods.length; i++) ttq.setAndDefer(ttq, ttq.methods[i]);
|
|
1150
|
-
ttq.instance = function (t) {
|
|
1151
|
-
const e = ttq._i[t] || [];
|
|
1152
|
-
for (let n = 0; n < ttq.methods.length; n++) ttq.setAndDefer(e, ttq.methods[n]);
|
|
1153
|
-
return e;
|
|
1154
|
-
};
|
|
1155
|
-
ttq.load = function (e, n) {
|
|
1156
|
-
const r = 'https://analytics.tiktok.com/i18n/pixel/events.js';
|
|
1157
|
-
const o = n && n.partner;
|
|
1158
|
-
ttq._i = ttq._i || {};
|
|
1159
|
-
ttq._i[e] = [];
|
|
1160
|
-
ttq._i[e]._u = r;
|
|
1161
|
-
ttq._t = ttq._t || {};
|
|
1162
|
-
ttq._t[e] = +new Date();
|
|
1163
|
-
ttq._o = ttq._o || {};
|
|
1164
|
-
ttq._o[e] = n || {};
|
|
1165
|
-
const script = document.createElement('script');
|
|
1166
|
-
script.type = 'text/javascript';
|
|
1167
|
-
script.async = true;
|
|
1168
|
-
script.src = r + '?sdkid=' + e + '&lib=' + t;
|
|
1169
|
-
// Add load event listener
|
|
1170
|
-
script.onload = () => {
|
|
1171
|
-
isPixelReady = true;
|
|
1172
|
-
resolve();
|
|
1173
|
-
};
|
|
1174
|
-
script.onerror = () => {
|
|
1175
|
-
reject(new Error('Failed to load TikTok Pixel script'));
|
|
1176
|
-
};
|
|
1177
|
-
const firstScript = document.getElementsByTagName('script')[0];
|
|
1178
|
-
firstScript.parentNode.insertBefore(script, firstScript);
|
|
1179
|
-
};
|
|
1180
|
-
// Load the pixel with the provided ID
|
|
1181
|
-
ttq.load(config.pixelId, config.testMode ? {
|
|
1182
|
-
test_mode: true
|
|
1183
|
-
} : undefined);
|
|
1184
|
-
// If script loads synchronously, mark as ready
|
|
1185
|
-
if (window.ttq && typeof window.ttq === 'function') {
|
|
1186
|
-
isPixelReady = true;
|
|
1187
|
-
resolve();
|
|
1188
|
-
}
|
|
1189
|
-
})(window, document, 'ttq');
|
|
1190
|
-
} catch (error) {
|
|
1191
|
-
reject(error);
|
|
1192
|
-
}
|
|
1193
|
-
});
|
|
1194
|
-
}),
|
|
1195
|
-
track: (eventName, properties, context) => {
|
|
1196
|
-
if (!isPixelReady || !window.ttq) return;
|
|
1197
|
-
const ttEventName = getTikTokEventName(eventName, config.eventMapping);
|
|
1198
|
-
// Handle PageView separately as it uses ttq.page()
|
|
1199
|
-
if (ttEventName === 'PageView') {
|
|
1200
|
-
window.ttq.page();
|
|
1201
|
-
return;
|
|
1202
|
-
}
|
|
1203
|
-
// Map parameters based on specific event requirements
|
|
1204
|
-
const ttParams = mapTikTokEventParameters(ttEventName, properties);
|
|
1205
|
-
if (ttParams && Object.keys(ttParams).length > 0) {
|
|
1206
|
-
window.ttq.track(ttEventName, ttParams);
|
|
1207
|
-
} else {
|
|
1208
|
-
window.ttq.track(ttEventName);
|
|
1209
|
-
}
|
|
1210
|
-
},
|
|
1211
|
-
identify: (userId, properties, context) => {
|
|
1212
|
-
try {
|
|
1213
|
-
if (typeof window !== 'undefined' && window.ttq) {
|
|
1214
|
-
const identifyData = coerceTikTokIdentifyValues(Object.assign(Object.assign({}, properties || {}), userId ? {
|
|
1215
|
-
external_id: userId
|
|
1216
|
-
} : {}));
|
|
1217
|
-
if (Object.keys(identifyData).length > 0) {
|
|
1218
|
-
window.ttq.identify(identifyData);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
} catch (error) {
|
|
1222
|
-
console.error('Error identifying user in TikTok Pixel:', error);
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
};
|
|
1226
|
-
}
|
|
1227
|
-
function getTikTokEventName(eventName, eventMapping) {
|
|
1228
|
-
// Convert default saasco Page View event by default
|
|
1229
|
-
if (eventName === 'Page View') return 'PageView';
|
|
1230
|
-
if (!eventMapping) return eventName;
|
|
1231
|
-
return eventMapping[eventName] || eventName;
|
|
1232
|
-
}
|
|
1233
|
-
/**
|
|
1234
|
-
* Get identification values from properties for TikTok Pixel
|
|
1235
|
-
*
|
|
1236
|
-
* Advanced Matching Documentation:
|
|
1237
|
-
* https://business-api.tiktok.com/portal/docs?rid=5ipocbxyw8v&id=1739585700402178
|
|
1238
|
-
*
|
|
1239
|
-
* Standard Events Documentation:
|
|
1240
|
-
* https://business-api.tiktok.com/portal/docs?id=1771101186666498
|
|
1241
|
-
*
|
|
1242
|
-
* @param properties
|
|
1243
|
-
* @returns
|
|
1244
|
-
*/
|
|
1245
|
-
function coerceTikTokIdentifyValues(properties) {
|
|
1246
|
-
const coerce = [{
|
|
1247
|
-
key: 'email',
|
|
1248
|
-
coerceFrom: ['email', 'user_email', 'userEmail', 'email_address', 'emailAddress', 'e_mail', 'E_Mail', 'mail', 'Mail', 'contact_email', 'contactEmail', 'primary_email', 'primaryEmail'],
|
|
1249
|
-
transform: value => {
|
|
1250
|
-
if (typeof value !== 'string') return undefined;
|
|
1251
|
-
const normalized = value.trim().toLowerCase();
|
|
1252
|
-
// TikTok requires hashed email
|
|
1253
|
-
return normalized ? jsSha256.sha256(normalized) : undefined;
|
|
1254
|
-
}
|
|
1255
|
-
}, {
|
|
1256
|
-
key: 'phone_number',
|
|
1257
|
-
coerceFrom: ['phone', 'phone_number', 'phoneNumber', 'mobile', 'mobile_number', 'mobileNumber', 'contact_number', 'contactNumber', 'tel', 'telephone'],
|
|
1258
|
-
transform: value => {
|
|
1259
|
-
if (typeof value !== 'string' && typeof value !== 'number') return undefined;
|
|
1260
|
-
const phoneStr = String(value).replace(/\D/g, '');
|
|
1261
|
-
// TikTok requires hashed phone number
|
|
1262
|
-
return phoneStr ? jsSha256.sha256(phoneStr) : undefined;
|
|
1263
|
-
}
|
|
1264
|
-
}, {
|
|
1265
|
-
key: 'external_id',
|
|
1266
|
-
coerceFrom: ['external_id', 'externalId', 'user_id', 'userId', 'id', 'distinctId', 'distinct_id', 'customer_id', 'customerId', 'member_id', 'memberId'],
|
|
1267
|
-
transform: value => {
|
|
1268
|
-
if (value == null) return undefined;
|
|
1269
|
-
const stringValue = String(value);
|
|
1270
|
-
// TikTok requires hashed external_id
|
|
1271
|
-
return stringValue ? jsSha256.sha256(stringValue) : undefined;
|
|
1272
|
-
}
|
|
1273
|
-
}];
|
|
1274
|
-
const result = {};
|
|
1275
|
-
for (const {
|
|
1276
|
-
key,
|
|
1277
|
-
coerceFrom,
|
|
1278
|
-
transform
|
|
1279
|
-
} of coerce) {
|
|
1280
|
-
const foundValue = findFirstProperty(properties, coerceFrom);
|
|
1281
|
-
if (foundValue !== null && transform) {
|
|
1282
|
-
const transformedValue = transform(foundValue);
|
|
1283
|
-
if (transformedValue) {
|
|
1284
|
-
result[key] = transformedValue;
|
|
1285
|
-
}
|
|
1286
|
-
} else if (foundValue !== null && typeof foundValue === 'string') {
|
|
1287
|
-
result[key] = foundValue;
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
return result;
|
|
1291
|
-
}
|
|
1292
|
-
function findFirstProperty(properties, keys) {
|
|
1293
|
-
for (const key of keys) {
|
|
1294
|
-
if (properties[key] !== undefined) {
|
|
1295
|
-
return properties[key];
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
return null;
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Maps event properties to TikTok-specific parameters based on event type
|
|
1302
|
-
*
|
|
1303
|
-
* Parameter Documentation: https://ads.tiktok.com/help/article/about-parameters
|
|
1304
|
-
* Standard Events: https://business-api.tiktok.com/portal/docs?id=1771101186666498
|
|
1305
|
-
*/
|
|
1306
|
-
function mapTikTokEventParameters(eventName, properties) {
|
|
1307
|
-
if (!properties) return null;
|
|
1308
|
-
const params = {};
|
|
1309
|
-
// Helper to safely add parameters, skipping objects/null/undefined
|
|
1310
|
-
const addParam = (key, value) => {
|
|
1311
|
-
if (value != null && typeof value !== 'object') {
|
|
1312
|
-
params[key] = value;
|
|
1313
|
-
}
|
|
1314
|
-
};
|
|
1315
|
-
switch (eventName) {
|
|
1316
|
-
case 'AddToCart':
|
|
1317
|
-
case 'Purchase':
|
|
1318
|
-
case 'ViewContent':
|
|
1319
|
-
addParam('content_type', properties['content_type']);
|
|
1320
|
-
addParam('content_ids', properties['content_ids']);
|
|
1321
|
-
addParam('quantity', properties['quantity']);
|
|
1322
|
-
addParam('description', properties['description']);
|
|
1323
|
-
addParam('currency', properties['currency']);
|
|
1324
|
-
addParam('value', properties['value']);
|
|
1325
|
-
addParam('content_name', properties['content_name']);
|
|
1326
|
-
addParam('content_category', properties['content_category']);
|
|
1327
|
-
break;
|
|
1328
|
-
case 'StartTrial':
|
|
1329
|
-
addParam('content_ids', properties['content_ids']);
|
|
1330
|
-
addParam('currency', properties['currency']);
|
|
1331
|
-
addParam('value', properties['value']);
|
|
1332
|
-
break;
|
|
1333
|
-
case 'Search':
|
|
1334
|
-
const searchString = properties['search_string'] || properties['query'] || properties['search_query'];
|
|
1335
|
-
addParam('search_string', searchString);
|
|
1336
|
-
break;
|
|
1337
|
-
case 'InitiateCheckout':
|
|
1338
|
-
case 'AddPaymentInfo':
|
|
1339
|
-
addParam('value', properties['value']);
|
|
1340
|
-
addParam('currency', properties['currency']);
|
|
1341
|
-
addParam('content_ids', properties['content_ids']);
|
|
1342
|
-
addParam('quantity', properties['quantity']);
|
|
1343
|
-
break;
|
|
1344
|
-
case 'CompleteRegistration':
|
|
1345
|
-
case 'Subscribe':
|
|
1346
|
-
addParam('value', properties['value']);
|
|
1347
|
-
addParam('currency', properties['currency']);
|
|
1348
|
-
break;
|
|
1349
|
-
default:
|
|
1350
|
-
// Pass through common parameters for any other events
|
|
1351
|
-
addParam('value', properties['value']);
|
|
1352
|
-
addParam('currency', properties['currency']);
|
|
1353
|
-
break;
|
|
1354
|
-
}
|
|
1355
|
-
return Object.keys(params).length > 0 ? params : null;
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
/*
|
|
1359
|
-
Minimal analytics integration manager (v0)
|
|
1360
|
-
*/
|
|
1361
|
-
class IntegrationManager {
|
|
1362
|
-
constructor(config = {}) {
|
|
1363
|
-
var _a, _b, _c, _d;
|
|
1364
|
-
this.context = {};
|
|
1365
|
-
this.integrations = new Map();
|
|
1366
|
-
this.globalQueue = [];
|
|
1367
|
-
this.config = {
|
|
1368
|
-
loggerLevel: (_a = config.loggerLevel) !== null && _a !== void 0 ? _a : LogLevel.ERROR,
|
|
1369
|
-
maxQueueSize: (_b = config.maxQueueSize) !== null && _b !== void 0 ? _b : 200,
|
|
1370
|
-
maxIntegrationWaitTime: (_c = config.maxIntegrationWaitTime) !== null && _c !== void 0 ? _c : 10000,
|
|
1371
|
-
flushInterval: (_d = config.flushInterval) !== null && _d !== void 0 ? _d : 5000
|
|
1372
|
-
};
|
|
1373
|
-
this.logger = new AnalyticsLogger({
|
|
1374
|
-
label: 'Saasco Integrations Debug',
|
|
1375
|
-
level: this.config.loggerLevel
|
|
1376
|
-
});
|
|
1377
|
-
this.initTime = Date.now();
|
|
1378
|
-
this.currentEnvironment = typeof window !== 'undefined' ? 'client' : 'server';
|
|
1379
|
-
this.setupPeriodicFlushing();
|
|
1380
|
-
this.setupUnloadHandler();
|
|
1381
|
-
}
|
|
1382
|
-
/**
|
|
1383
|
-
* Shallow-merge context to keep it simple + predictable in v0
|
|
1384
|
-
*/
|
|
1385
|
-
setContext(next) {
|
|
1386
|
-
this.context = Object.assign(Object.assign({}, this.context), next);
|
|
1387
|
-
// Only log if we are setting something
|
|
1388
|
-
if (Object.keys(this.context).length > 0) this.logger.info('setContext', this.context);
|
|
1389
|
-
}
|
|
1390
|
-
/**
|
|
1391
|
-
* Register and init an integration. When init resolves, we mark it ready and
|
|
1392
|
-
* immediately flush any queued events in FIFO order to *all* ready integrations.
|
|
1393
|
-
*/
|
|
1394
|
-
registerIntegration(integration) {
|
|
1395
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1396
|
-
this.logger.debug('Register integrations', this.integrations);
|
|
1397
|
-
if (this.integrations.has(integration.name)) {
|
|
1398
|
-
this.logger.warn(`Integration already registered: ${integration.name}`);
|
|
1399
|
-
return;
|
|
1400
|
-
}
|
|
1401
|
-
// Check environment compatibility
|
|
1402
|
-
const isCompatible = integration.environments.includes(this.currentEnvironment);
|
|
1403
|
-
if (!isCompatible) {
|
|
1404
|
-
this.logger.warn(`Skipping ${integration.name}: requires ${integration.environments.join(' or ')} but running in ${this.currentEnvironment}`);
|
|
1405
|
-
return;
|
|
1406
|
-
}
|
|
1407
|
-
this.logger.debug(`Registering ${integration.name}`);
|
|
1408
|
-
const state = {
|
|
1409
|
-
integration,
|
|
1410
|
-
status: 'idle'
|
|
1411
|
-
};
|
|
1412
|
-
this.integrations.set(integration.name, state);
|
|
1413
|
-
if (integration.init) {
|
|
1414
|
-
try {
|
|
1415
|
-
state.status = 'loading';
|
|
1416
|
-
const startTime = Date.now();
|
|
1417
|
-
this.logger.debug(`Initializing ${integration.name}`);
|
|
1418
|
-
yield integration.init(this.context);
|
|
1419
|
-
state.status = 'ready';
|
|
1420
|
-
const endTime = Date.now();
|
|
1421
|
-
const duration = endTime - startTime;
|
|
1422
|
-
this.logger.debug(`Integration ${integration.name} ready in ${duration}ms`);
|
|
1423
|
-
this.flush();
|
|
1424
|
-
} catch (e) {
|
|
1425
|
-
state.status = 'error';
|
|
1426
|
-
this.logger.error(`Failed to init ${integration.name}`, e);
|
|
1427
|
-
}
|
|
1428
|
-
} else {
|
|
1429
|
-
state.status = 'ready';
|
|
1430
|
-
this.logger.info(`Integration ${integration.name} ready (no init)`);
|
|
1431
|
-
this.flush();
|
|
1432
|
-
}
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
|
-
identify(userId, traits) {
|
|
1436
|
-
// update context so subsequent track() carries new userId
|
|
1437
|
-
this.setContext({
|
|
1438
|
-
userId
|
|
1439
|
-
});
|
|
1440
|
-
this.send({
|
|
1441
|
-
type: 'identify',
|
|
1442
|
-
name: undefined,
|
|
1443
|
-
properties: traits
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
track(name, properties) {
|
|
1447
|
-
this.send({
|
|
1448
|
-
type: 'track',
|
|
1449
|
-
name,
|
|
1450
|
-
properties
|
|
1451
|
-
});
|
|
1452
|
-
}
|
|
1453
|
-
/**
|
|
1454
|
-
* Core send path: if at least one integration is ready -> deliver immediately
|
|
1455
|
-
* Else enqueue (bounded FIFO)
|
|
1456
|
-
*/
|
|
1457
|
-
send(partial) {
|
|
1458
|
-
const envelope = {
|
|
1459
|
-
id: uuid(),
|
|
1460
|
-
type: partial.type,
|
|
1461
|
-
timestamp: Date.now(),
|
|
1462
|
-
name: partial.name,
|
|
1463
|
-
properties: partial.properties,
|
|
1464
|
-
context: Object.assign({}, this.context)
|
|
1465
|
-
};
|
|
1466
|
-
// If we're not ready, enqueue
|
|
1467
|
-
if (!this.isReady()) {
|
|
1468
|
-
if (this.globalQueue.length >= this.config.maxQueueSize) {
|
|
1469
|
-
// drop oldest
|
|
1470
|
-
this.globalQueue.shift();
|
|
1471
|
-
this.logger.warn('Queue full → dropped oldest');
|
|
1472
|
-
}
|
|
1473
|
-
this.globalQueue.push(envelope);
|
|
1474
|
-
this.logger.debug(`Queued (${this.globalQueue.length})`, envelope);
|
|
1475
|
-
return;
|
|
1476
|
-
}
|
|
1477
|
-
this.deliver(envelope);
|
|
1478
|
-
}
|
|
1479
|
-
deliver(evt) {
|
|
1480
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1481
|
-
for (const {
|
|
1482
|
-
integration,
|
|
1483
|
-
status
|
|
1484
|
-
} of this.integrations.values()) {
|
|
1485
|
-
if (status !== 'ready') continue;
|
|
1486
|
-
try {
|
|
1487
|
-
if (evt.type === 'track' && integration.track && evt.name) {
|
|
1488
|
-
this.logger.debug(`Track (${integration.name}) ${evt.name}`, {
|
|
1489
|
-
properties: evt.properties,
|
|
1490
|
-
context: evt.context
|
|
1491
|
-
});
|
|
1492
|
-
yield integration.track(evt.name, evt.properties, evt.context);
|
|
1493
|
-
} else if (evt.type === 'identify' && integration.identify) {
|
|
1494
|
-
this.logger.debug(`Identify (${integration.name})`, {
|
|
1495
|
-
userId: evt.context.distinctId,
|
|
1496
|
-
properties: evt.properties,
|
|
1497
|
-
context: evt.context
|
|
1498
|
-
});
|
|
1499
|
-
yield integration.identify(evt.context.distinctId, evt.properties, evt.context);
|
|
1500
|
-
}
|
|
1501
|
-
} catch (e) {
|
|
1502
|
-
this.logger.error(`deliver error in ${integration.name} (${evt.type})`, e);
|
|
1503
|
-
}
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
/**
|
|
1508
|
-
* Flush queued events FIFO once at least one integration is ready.
|
|
1509
|
-
*/
|
|
1510
|
-
flush() {
|
|
1511
|
-
// nothing to flush
|
|
1512
|
-
if (this.globalQueue.length === 0) return;
|
|
1513
|
-
// If we're not ready don't flush
|
|
1514
|
-
if (!this.isReady()) return;
|
|
1515
|
-
this.logger.debug(`flushing ${this.globalQueue.length} queued events`);
|
|
1516
|
-
const toSend = this.globalQueue;
|
|
1517
|
-
this.globalQueue = [];
|
|
1518
|
-
// deliver all events
|
|
1519
|
-
toSend.forEach(evt => this.deliver(evt));
|
|
1520
|
-
}
|
|
1521
|
-
readyCount() {
|
|
1522
|
-
let n = 0;
|
|
1523
|
-
for (const s of this.integrations.values()) if (s.status === 'ready') n++;
|
|
1524
|
-
return n;
|
|
1525
|
-
}
|
|
1526
|
-
isReady() {
|
|
1527
|
-
const timeElapsed = Date.now() - this.initTime;
|
|
1528
|
-
// Return if integrations are not ready, or if we haven't waited the max integration wait time
|
|
1529
|
-
if (this.readyCount() < this.integrations.size && timeElapsed < this.config.maxIntegrationWaitTime) {
|
|
1530
|
-
return false;
|
|
1531
|
-
}
|
|
1532
|
-
if (this.readyCount() < this.integrations.size) {
|
|
1533
|
-
this.logger.info(`proceeding with ${this.readyCount()}/${this.integrations.size} integrations ready after ${timeElapsed}ms wait`);
|
|
1534
|
-
}
|
|
1535
|
-
return true;
|
|
1536
|
-
}
|
|
1537
|
-
/**
|
|
1538
|
-
* Setup periodic flushing if enabled
|
|
1539
|
-
*/
|
|
1540
|
-
setupPeriodicFlushing() {
|
|
1541
|
-
if (this.config.flushInterval <= 0) return;
|
|
1542
|
-
this.flushTimer = setInterval(() => {
|
|
1543
|
-
if (this.globalQueue.length > 0) {
|
|
1544
|
-
this.flush();
|
|
1545
|
-
}
|
|
1546
|
-
}, this.config.flushInterval);
|
|
1547
|
-
}
|
|
1548
|
-
/**
|
|
1549
|
-
* Setup page unload handler for client environment
|
|
1550
|
-
*/
|
|
1551
|
-
setupUnloadHandler() {
|
|
1552
|
-
if (this.currentEnvironment !== 'client' || typeof window === 'undefined') {
|
|
1553
|
-
return;
|
|
1554
|
-
}
|
|
1555
|
-
this.unloadHandler = () => {
|
|
1556
|
-
this.logger.info('page unloading, flushing remaining events');
|
|
1557
|
-
this.flush();
|
|
1558
|
-
};
|
|
1559
|
-
// Use both beforeunload and pagehide for better coverage
|
|
1560
|
-
window.addEventListener('beforeunload', this.unloadHandler);
|
|
1561
|
-
window.addEventListener('pagehide', this.unloadHandler);
|
|
1562
|
-
}
|
|
1563
|
-
/** Debug helpers */
|
|
1564
|
-
getStats() {
|
|
1565
|
-
return {
|
|
1566
|
-
currentEnvironment: this.currentEnvironment,
|
|
1567
|
-
integrations: [...this.integrations.values()].map(s => ({
|
|
1568
|
-
name: s.integration.name,
|
|
1569
|
-
status: s.status,
|
|
1570
|
-
environments: s.integration.environments
|
|
1571
|
-
})),
|
|
1572
|
-
queueLength: this.globalQueue.length,
|
|
1573
|
-
readyCount: this.readyCount(),
|
|
1574
|
-
periodicFlushEnabled: this.config.flushInterval > 0,
|
|
1575
|
-
flushInterval: this.config.flushInterval,
|
|
1576
|
-
context: this.context
|
|
1577
|
-
};
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
const isBrowser = typeof window !== 'undefined';
|
|
1582
|
-
const isServer = !isBrowser;
|
|
1583
|
-
const PREF = 'saasco-sdk';
|
|
1584
|
-
const SESSION_DURATION = 1000 * 60 * 30;
|
|
1585
|
-
const USER_DURATION = 1000 * 60 * 60 * 24 * 365;
|
|
1586
|
-
const data = {};
|
|
1587
|
-
function getRootDomain(url) {
|
|
1588
|
-
if (isServer) return;
|
|
1589
|
-
if (!url) return;
|
|
1590
|
-
try {
|
|
1591
|
-
const hostname = new URL(url).hostname;
|
|
1592
|
-
if (!psl.isValid(hostname)) return;
|
|
1593
|
-
const parsed = psl.parse(hostname);
|
|
1594
|
-
if ('domain' in parsed && parsed.domain) return parsed.domain;
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
// invalid url
|
|
1597
|
-
}
|
|
1598
|
-
return;
|
|
1599
|
-
}
|
|
1600
|
-
function setCookie(name, value, ttl) {
|
|
1601
|
-
if (isServer) return;
|
|
1602
|
-
const expires = new Date(Date.now() + ttl).toUTCString();
|
|
1603
|
-
const domain = getRootDomain(window.location.href);
|
|
1604
|
-
const cookieString = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/${domain ? `; domain=.${domain}` : ''}`;
|
|
1605
|
-
document.cookie = cookieString;
|
|
1606
|
-
}
|
|
1607
|
-
function getCookie(name) {
|
|
1608
|
-
if (isServer) return null;
|
|
1609
|
-
const nameEQ = name + '=';
|
|
1610
|
-
const ca = document.cookie.split(';');
|
|
1611
|
-
for (let i = 0; i < ca.length; i++) {
|
|
1612
|
-
let c = ca[i];
|
|
1613
|
-
while (c.charAt(0) === ' ') c = c.substring(1, c.length);
|
|
1614
|
-
if (c.indexOf(nameEQ) === 0) {
|
|
1615
|
-
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
1616
|
-
}
|
|
1617
|
-
}
|
|
1618
|
-
return null;
|
|
1619
|
-
}
|
|
1620
|
-
function deleteCookie(name) {
|
|
1621
|
-
if (isServer) return;
|
|
1622
|
-
const domain = getRootDomain(window.location.href);
|
|
1623
|
-
const cookieString = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/${domain ? `; domain=.${domain}` : ''}`;
|
|
1624
|
-
document.cookie = cookieString;
|
|
1625
|
-
}
|
|
1626
|
-
function migrateFromLocalStorage() {
|
|
1627
|
-
if (isServer) return;
|
|
1628
|
-
const keysToMigrate = [`${PREF}-session-id`, `${PREF}-anonymous-id`, `${PREF}-user-id`, `${PREF}-session-utm-context`, `${PREF}-super-context`];
|
|
1629
|
-
keysToMigrate.forEach(key => {
|
|
1630
|
-
try {
|
|
1631
|
-
const value = localStorage.getItem(key);
|
|
1632
|
-
if (value) {
|
|
1633
|
-
// Parse the stored data to get the original TTL
|
|
1634
|
-
const item = JSON.parse(value);
|
|
1635
|
-
const now = new Date().getTime();
|
|
1636
|
-
// Check if the data is still valid
|
|
1637
|
-
if (item.expiry > now) {
|
|
1638
|
-
// Calculate remaining TTL
|
|
1639
|
-
const remainingTtl = item.expiry - now;
|
|
1640
|
-
// Store in cookie with remaining TTL
|
|
1641
|
-
setCookie(key, value, remainingTtl);
|
|
1642
|
-
// Remove from localStorage after successful migration
|
|
1643
|
-
localStorage.removeItem(key);
|
|
1644
|
-
} else {
|
|
1645
|
-
// Data has expired, just remove it
|
|
1646
|
-
localStorage.removeItem(key);
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
} catch (error) {
|
|
1650
|
-
// If there's an error parsing the data, remove the corrupted item
|
|
1651
|
-
localStorage.removeItem(key);
|
|
1652
|
-
}
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
|
-
function storeData(key, value, ttl) {
|
|
1656
|
-
if (isServer) {
|
|
1657
|
-
const item = {
|
|
1658
|
-
value,
|
|
1659
|
-
expiry: new Date().getTime() + ttl
|
|
1660
|
-
};
|
|
1661
|
-
if (value === undefined) {
|
|
1662
|
-
delete data[key];
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
data[key] = item;
|
|
1666
|
-
return;
|
|
1667
|
-
}
|
|
1668
|
-
const fullKey = `${PREF}-${key}`;
|
|
1669
|
-
if (value === undefined) {
|
|
1670
|
-
deleteCookie(fullKey);
|
|
1671
|
-
return;
|
|
1672
|
-
}
|
|
1673
|
-
const item = {
|
|
1674
|
-
value,
|
|
1675
|
-
expiry: new Date().getTime() + ttl
|
|
1676
|
-
};
|
|
1677
|
-
setCookie(fullKey, JSON.stringify(item), ttl);
|
|
1678
|
-
}
|
|
1679
|
-
function retrieveData(key) {
|
|
1680
|
-
if (isServer) {
|
|
1681
|
-
const item = data[key];
|
|
1682
|
-
if (!item) return null;
|
|
1683
|
-
if (Date.now() > item.expiry) {
|
|
1684
|
-
delete data[key];
|
|
1685
|
-
return null;
|
|
1686
|
-
}
|
|
1687
|
-
return item.value;
|
|
1688
|
-
}
|
|
1689
|
-
const fullKey = `${PREF}-${key}`;
|
|
1690
|
-
const itemStr = getCookie(fullKey);
|
|
1691
|
-
if (!itemStr) {
|
|
1692
|
-
return null;
|
|
1693
|
-
}
|
|
1694
|
-
try {
|
|
1695
|
-
const item = JSON.parse(itemStr);
|
|
1696
|
-
if (Date.now() > item.expiry) {
|
|
1697
|
-
deleteCookie(fullKey);
|
|
1698
|
-
return null;
|
|
1699
|
-
}
|
|
1700
|
-
return item.value;
|
|
1701
|
-
} catch (error) {
|
|
1702
|
-
// If JSON parsing fails, remove the corrupted cookie
|
|
1703
|
-
deleteCookie(fullKey);
|
|
1704
|
-
return null;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
function getSessionId() {
|
|
1708
|
-
return retrieveData(`session-id`);
|
|
1709
|
-
}
|
|
1710
|
-
function setSessionId({
|
|
1711
|
-
reset
|
|
1712
|
-
} = {
|
|
1713
|
-
reset: false
|
|
1714
|
-
}) {
|
|
1715
|
-
const sessionId = reset ? uuid() : getSessionId() || uuid();
|
|
1716
|
-
storeData(`session-id`, sessionId, SESSION_DURATION);
|
|
1717
|
-
return sessionId;
|
|
1718
|
-
}
|
|
1719
|
-
function getAnonymousId() {
|
|
1720
|
-
return retrieveData(`anonymous-id`);
|
|
1721
|
-
}
|
|
1722
|
-
function setAnonymousId({
|
|
1723
|
-
reset
|
|
1724
|
-
} = {
|
|
1725
|
-
reset: false
|
|
1726
|
-
}) {
|
|
1727
|
-
const anonymousId = reset ? uuid() : getAnonymousId() || uuid();
|
|
1728
|
-
storeData(`anonymous-id`, anonymousId, USER_DURATION);
|
|
1729
|
-
return anonymousId;
|
|
1730
|
-
}
|
|
1731
|
-
function getUserId() {
|
|
1732
|
-
return retrieveData(`user-id`);
|
|
1733
|
-
}
|
|
1734
|
-
function setUserId(userId) {
|
|
1735
|
-
storeData(`user-id`, userId, USER_DURATION);
|
|
1736
|
-
}
|
|
1737
|
-
function getSuperContext() {
|
|
1738
|
-
return retrieveData('super-context') || {};
|
|
1739
|
-
}
|
|
1740
|
-
function setSuperContext(superContext) {
|
|
1741
|
-
storeData('super-context', superContext, SESSION_DURATION);
|
|
1742
|
-
}
|
|
1743
|
-
function updateSuperContext(browserContext) {
|
|
1744
|
-
const existingSuperContext = getSuperContext();
|
|
1745
|
-
const defaultSuperContextKeys = ['$utmSource', '$utmMedium', '$utmCampaign', '$utmTerm', '$utmContent', '$utmId', '$utmSourcePlatform', '$utmCampaignId', '$utmCreativeFormat', '$utmMarketingTactic', '$utmAdSource', '$utmAdId'];
|
|
1746
|
-
const superContext = defaultSuperContextKeys.reduce((acc, key) => {
|
|
1747
|
-
const value = Object.assign({}, browserContext)[key];
|
|
1748
|
-
if (value) acc[key] = value;
|
|
1749
|
-
return acc;
|
|
1750
|
-
}, existingSuperContext);
|
|
1751
|
-
// Only set referrer if it's from a different root domain
|
|
1752
|
-
// Prevents setting referrer when going between subdomains of the same root domain
|
|
1753
|
-
if (browserContext.$referrer) {
|
|
1754
|
-
const referrerRootDomain = getRootDomain(browserContext.$referrer);
|
|
1755
|
-
const currentRootDomain = getRootDomain(browserContext.$href);
|
|
1756
|
-
if (referrerRootDomain && currentRootDomain && referrerRootDomain !== currentRootDomain) {
|
|
1757
|
-
superContext['$referrer'] = browserContext.$referrer;
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
|
-
setSuperContext(superContext);
|
|
1761
|
-
return superContext;
|
|
1762
|
-
}
|
|
1763
|
-
class Saasco {
|
|
1764
|
-
/**
|
|
1765
|
-
* Creates an instance of Saasco SDK.
|
|
1766
|
-
* @param config Configuration options.
|
|
1767
|
-
* @param config.projectId The unique identifier for the project.
|
|
1768
|
-
* @param config.proxy The URL of the proxy server to use, if any.
|
|
1769
|
-
* @param config.autoPageTracking Whether to automatically track page views. Default is false.
|
|
1770
|
-
* @param config.enabled Whether analytics is enabled. Default is true. Set to false for development and staging envioronments. Will still allow debug mode to be true, just no events will be sent
|
|
1771
|
-
* @param config.debug Whether to log debug information. Default is false.
|
|
1772
|
-
* @param config.trackUrlParams Whether to track URL parameters. Default is true.
|
|
1773
|
-
* @param config.trackHashChanges Whether to track hash changes. Default is true.
|
|
1774
|
-
* @param config.integrations Configuration for third-party integrations like Facebook Pixel.
|
|
1775
|
-
*/
|
|
1776
|
-
constructor(config) {
|
|
1777
|
-
this.config = config;
|
|
1778
|
-
this.lastPageViewPath = '';
|
|
1779
|
-
this.isInitialized = false;
|
|
1780
|
-
const loggerLevel = getLoggerLevel(this.config);
|
|
1781
|
-
// Initialize logger
|
|
1782
|
-
this.logger = new AnalyticsLogger({
|
|
1783
|
-
level: loggerLevel,
|
|
1784
|
-
label: 'Saasco Debug'
|
|
1785
|
-
});
|
|
1786
|
-
this.logger.info('Saasco started');
|
|
1787
|
-
if (!config.projectId) {
|
|
1788
|
-
this.logger.error("Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
1789
|
-
return;
|
|
1790
|
-
}
|
|
1791
|
-
// default enabled to true
|
|
1792
|
-
this.config.enabled = this.config.enabled === undefined ? true : this.config.enabled;
|
|
1793
|
-
// Set default auto page tracking config
|
|
1794
|
-
this.config.autoPageTracking = Object.assign({
|
|
1795
|
-
enabled: true,
|
|
1796
|
-
trackQueryParams: true,
|
|
1797
|
-
trackHash: true
|
|
1798
|
-
}, this.config.autoPageTracking);
|
|
1799
|
-
// Initialize integration manager
|
|
1800
|
-
this.integrationManager = new IntegrationManager({
|
|
1801
|
-
loggerLevel,
|
|
1802
|
-
maxQueueSize: 1000
|
|
1803
|
-
});
|
|
1804
|
-
// Set initial context
|
|
1805
|
-
this.integrationManager.setContext({});
|
|
1806
|
-
// Initialize integrations immediately (works on both client and server)
|
|
1807
|
-
this.initIntegrations().catch(error => {
|
|
1808
|
-
this.logger.info('Error initializing integrations:', error);
|
|
1809
|
-
});
|
|
1810
|
-
}
|
|
1811
|
-
init() {
|
|
1812
|
-
if (this.isInitialized) {
|
|
1813
|
-
this.logger.info('Saasco is already initialized. Please check your code to ensure that init() is not being called multiple times.');
|
|
1814
|
-
return;
|
|
1815
|
-
}
|
|
1816
|
-
// Export Saasco to the window object for easy access and debugging
|
|
1817
|
-
if (isBrowser) {
|
|
1818
|
-
window.saasco = this;
|
|
1819
|
-
}
|
|
1820
|
-
// Migrate existing localStorage data to cookies for cross-subdomain support
|
|
1821
|
-
migrateFromLocalStorage();
|
|
1822
|
-
this.logger.info('Saasco initialized', this.config);
|
|
1823
|
-
if (this.config.debug) this.logger.info('Debug mode active. This will log all events to the console.');
|
|
1824
|
-
if (!this.config.enabled) this.logger.info(`Analytics is disabled. No requests will be sent to the server and no integrations will be initialized. ${this.config.debug ? 'Debug mode is active and will still log information.' : ''}`);
|
|
1825
|
-
this.initAutoPageTracking();
|
|
1826
|
-
this.isInitialized = true;
|
|
1827
|
-
}
|
|
1828
|
-
disableDebug() {
|
|
1829
|
-
this.logger.info('Debug mode deactivated.');
|
|
1830
|
-
this.config.debug = false;
|
|
1831
|
-
}
|
|
1832
|
-
enableDebug() {
|
|
1833
|
-
this.config.debug = true;
|
|
1834
|
-
this.logger.info('Debug mode activated.');
|
|
1835
|
-
}
|
|
1836
|
-
/**
|
|
1837
|
-
* Initialize third-party integrations
|
|
1838
|
-
*/
|
|
1839
|
-
initIntegrations() {
|
|
1840
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1841
|
-
this.logger.info('Initialize integrations');
|
|
1842
|
-
if (!this.config.integrations || this.config.integrations.length === 0) return;
|
|
1843
|
-
if (!this.config.enabled) {
|
|
1844
|
-
this.logger.info('Analytics is disabled. Skipping integration initialization.');
|
|
1845
|
-
return;
|
|
1846
|
-
}
|
|
1847
|
-
for (const integrationConfig of this.config.integrations) {
|
|
1848
|
-
try {
|
|
1849
|
-
if (integrationConfig.type === 'facebook-pixel') {
|
|
1850
|
-
this.logger.info('Registering Facebook Pixel integration with ID:', integrationConfig.config.pixelId);
|
|
1851
|
-
const fbIntegration = createFacebookPixelIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1852
|
-
yield this.integrationManager.registerIntegration(fbIntegration);
|
|
1853
|
-
} else if (integrationConfig.type === 'pinterest-tag') {
|
|
1854
|
-
this.logger.info('Registering Pinterest Tag integration with ID:', integrationConfig.config.tagId);
|
|
1855
|
-
const pinterestIntegration = createPinterestTagIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1856
|
-
yield this.integrationManager.registerIntegration(pinterestIntegration);
|
|
1857
|
-
} else if (integrationConfig.type === 'tiktok-pixel') {
|
|
1858
|
-
this.logger.info('Registering TikTok Pixel integration with ID:', integrationConfig.config.pixelId);
|
|
1859
|
-
const tiktokIntegration = createTikTokPixelIntegration(integrationConfig.config, integrationConfig.debug);
|
|
1860
|
-
yield this.integrationManager.registerIntegration(tiktokIntegration);
|
|
1861
|
-
}
|
|
1862
|
-
} catch (error) {
|
|
1863
|
-
this.logger.info(`Failed to register ${integrationConfig.type} integration:`, error);
|
|
1864
|
-
}
|
|
1865
|
-
}
|
|
1866
|
-
});
|
|
1867
|
-
}
|
|
1868
|
-
track(actionOrPayload, propertiesOrNothing, contextOrNothing) {
|
|
1869
|
-
if (!this.config.projectId) {
|
|
1870
|
-
const response = this.logger.error("Unable to track event. Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
1871
|
-
return Promise.resolve(response);
|
|
1872
|
-
}
|
|
1873
|
-
setSessionId();
|
|
1874
|
-
setAnonymousId();
|
|
1875
|
-
const hasAction = typeof actionOrPayload === 'string';
|
|
1876
|
-
const hasPayload = typeof actionOrPayload === 'object';
|
|
1877
|
-
// Must do payload on the server
|
|
1878
|
-
if (isServer && hasAction) {
|
|
1879
|
-
const response = this.logger.error('When calling track from the server you must pass a payload object with a userId in order to track events. For example: track({ event: "Song Played", userId: "user_123", properties: { song: "Song Name" } })');
|
|
1880
|
-
return Promise.resolve(response);
|
|
1881
|
-
}
|
|
1882
|
-
// On server events we require a userId or an anonymousId
|
|
1883
|
-
if (isServer && hasPayload && !actionOrPayload.userId && !actionOrPayload.anonymousId) {
|
|
1884
|
-
const response = this.logger.error('When calling track from the server you must pass a payload object with either a userId or an anonymousId in order to track events. For example:\n\ntrack({ event: "Song Played", userId: "user_123", properties: { song: "Song Name" } }). \n\nIf providing an anonymousId this must be provided from the client side otherwise we will not have a user to connect the event to.');
|
|
1885
|
-
return Promise.resolve(response);
|
|
1886
|
-
}
|
|
1887
|
-
const action = hasAction ? actionOrPayload : actionOrPayload.event;
|
|
1888
|
-
const properties = hasAction ? propertiesOrNothing : actionOrPayload.properties;
|
|
1889
|
-
const context = hasAction ? contextOrNothing : actionOrPayload.context;
|
|
1890
|
-
const sessionId = (hasAction ? undefined : actionOrPayload.sessionId) || getSessionId();
|
|
1891
|
-
const anonymousId = (hasAction ? undefined : actionOrPayload.anonymousId) || getAnonymousId();
|
|
1892
|
-
const distinctId = (hasAction ? undefined : actionOrPayload.userId) || getUserId();
|
|
1893
|
-
// Only gets browser and super context if in the browser
|
|
1894
|
-
let browserContext;
|
|
1895
|
-
let superContext;
|
|
1896
|
-
if (isBrowser) {
|
|
1897
|
-
browserContext = getBrowserContext();
|
|
1898
|
-
superContext = updateSuperContext(browserContext);
|
|
1899
|
-
}
|
|
1900
|
-
const payloadJSON = Object.assign(Object.assign(Object.assign({}, browserContext), superContext), {
|
|
1901
|
-
properties: properties || {}
|
|
1902
|
-
});
|
|
1903
|
-
const data = {
|
|
1904
|
-
id: uuid(),
|
|
1905
|
-
timestamp: new Date().toISOString(),
|
|
1906
|
-
action,
|
|
1907
|
-
version,
|
|
1908
|
-
sessionId,
|
|
1909
|
-
anonymousId,
|
|
1910
|
-
distinctId,
|
|
1911
|
-
projectId: this.config.projectId,
|
|
1912
|
-
payload: JSON.stringify(payloadJSON),
|
|
1913
|
-
source: isBrowser ? 'client' : 'server',
|
|
1914
|
-
context: JSON.stringify(context || {})
|
|
1915
|
-
};
|
|
1916
|
-
if (data.action === 'Page View') {
|
|
1917
|
-
this.logger.info('Page View', window.location.href, data);
|
|
1918
|
-
} else {
|
|
1919
|
-
this.logger.info('track', data);
|
|
1920
|
-
}
|
|
1921
|
-
if (!this.config.enabled) {
|
|
1922
|
-
return Promise.resolve({
|
|
1923
|
-
success: true,
|
|
1924
|
-
message: 'Analytics is disabled'
|
|
1925
|
-
});
|
|
1926
|
-
}
|
|
1927
|
-
// Only send track to integrations if analytics is enabled
|
|
1928
|
-
this.integrationManager.track(action, properties);
|
|
1929
|
-
// Send event to API
|
|
1930
|
-
return this.doRequest('events', data);
|
|
1931
|
-
}
|
|
1932
|
-
/**
|
|
1933
|
-
* The page method lets you record page views on your website
|
|
1934
|
-
* This records the page title and path and names the event useing the reserved property "Page Viewed"
|
|
1935
|
-
*
|
|
1936
|
-
* Before implementing this make sure you have disabled the autoPageTracking in the config or you will get duplicate page views
|
|
1937
|
-
*/
|
|
1938
|
-
page() {
|
|
1939
|
-
var _a, _b;
|
|
1940
|
-
if (isServer) {
|
|
1941
|
-
console.warn('Saasco page tracking is only available in the browser');
|
|
1942
|
-
return;
|
|
1943
|
-
}
|
|
1944
|
-
const url = new URL(window.location.href);
|
|
1945
|
-
let pathToCompare = url.pathname;
|
|
1946
|
-
// Include search params if configured
|
|
1947
|
-
if ((_a = this.config.autoPageTracking) === null || _a === void 0 ? void 0 : _a.trackQueryParams) {
|
|
1948
|
-
pathToCompare += url.search;
|
|
1949
|
-
}
|
|
1950
|
-
// Include hash if configured
|
|
1951
|
-
if ((_b = this.config.autoPageTracking) === null || _b === void 0 ? void 0 : _b.trackHash) {
|
|
1952
|
-
pathToCompare += url.hash;
|
|
1953
|
-
}
|
|
1954
|
-
// Only track if the path has changed
|
|
1955
|
-
if (pathToCompare === this.lastPageViewPath) return;
|
|
1956
|
-
this.lastPageViewPath = pathToCompare;
|
|
1957
|
-
return this.track('Page View');
|
|
1958
|
-
}
|
|
1959
|
-
identify(distinctIdOrProperties, propertiesOrContext, contextOrNothing) {
|
|
1960
|
-
if (!this.config.projectId) {
|
|
1961
|
-
const response = this.logger.error("Unable to identify user. Project ID is required but has not been provided. If you are using an env variable make sure it's set correctly.");
|
|
1962
|
-
return Promise.resolve(response);
|
|
1963
|
-
}
|
|
1964
|
-
const hasId = typeof distinctIdOrProperties === 'string' || typeof distinctIdOrProperties === 'number' || distinctIdOrProperties === null;
|
|
1965
|
-
const distinctId = hasId ? distinctIdOrProperties === null || distinctIdOrProperties === void 0 ? void 0 : distinctIdOrProperties.toString() : `soft_${uuid()}`;
|
|
1966
|
-
const properties = hasId ? propertiesOrContext : distinctIdOrProperties;
|
|
1967
|
-
const context = hasId ? contextOrNothing : propertiesOrContext;
|
|
1968
|
-
// When the user gets identified as null this will reset the users session and anonymous id
|
|
1969
|
-
// This should only be called when distinctId is null and there is an existing userId.
|
|
1970
|
-
// This means the user has logged out and we should reset the session and anonymous IDs
|
|
1971
|
-
const userIdChangedToNull = distinctId === null && !!getUserId();
|
|
1972
|
-
if (userIdChangedToNull) this.logger.info('User logged out');
|
|
1973
|
-
const reset = userIdChangedToNull;
|
|
1974
|
-
const sessionId = setSessionId({
|
|
1975
|
-
reset
|
|
1976
|
-
});
|
|
1977
|
-
const anonymousId = setAnonymousId({
|
|
1978
|
-
reset
|
|
1979
|
-
});
|
|
1980
|
-
// set the distinct Id to the userId
|
|
1981
|
-
setUserId(distinctId);
|
|
1982
|
-
// Update integration manager context and send identify (IntegrationManager will filter by environment)
|
|
1983
|
-
this.integrationManager.setContext(Object.assign({
|
|
1984
|
-
distinctId,
|
|
1985
|
-
anonymousId,
|
|
1986
|
-
sessionId
|
|
1987
|
-
}, properties));
|
|
1988
|
-
// No distinctId provided so we don't track the user
|
|
1989
|
-
if (!distinctId) return Promise.resolve({
|
|
1990
|
-
success: true,
|
|
1991
|
-
message: 'No distinctId provided'
|
|
1992
|
-
});
|
|
1993
|
-
// Send identify to API
|
|
1994
|
-
const data = {
|
|
1995
|
-
id: uuid(),
|
|
1996
|
-
timestamp: new Date().toISOString(),
|
|
1997
|
-
projectId: this.config.projectId,
|
|
1998
|
-
distinctId,
|
|
1999
|
-
anonymousId,
|
|
2000
|
-
version,
|
|
2001
|
-
payload: properties || {},
|
|
2002
|
-
context: context || {}
|
|
2003
|
-
};
|
|
2004
|
-
this.logger.info('identify', data);
|
|
2005
|
-
// Only send identify to integrations if analytics is enabled
|
|
2006
|
-
if (!this.config.enabled) {
|
|
2007
|
-
return Promise.resolve({
|
|
2008
|
-
success: true,
|
|
2009
|
-
message: 'Analytics is disabled'
|
|
2010
|
-
});
|
|
2011
|
-
}
|
|
2012
|
-
this.integrationManager.identify(distinctId, properties);
|
|
2013
|
-
return this.doRequest('identify', data);
|
|
2014
|
-
}
|
|
2015
|
-
/**
|
|
2016
|
-
* This should only be called when the user logs out
|
|
2017
|
-
* It will reset the session, anonymous id, and user id
|
|
2018
|
-
*
|
|
2019
|
-
* All events after calling reset will be tracked as a new user
|
|
2020
|
-
*
|
|
2021
|
-
*/
|
|
2022
|
-
logout() {
|
|
2023
|
-
// Clear super context data on logout
|
|
2024
|
-
setSuperContext(null);
|
|
2025
|
-
this.identify(null);
|
|
2026
|
-
}
|
|
2027
|
-
/**
|
|
2028
|
-
* Handles sending data to the Saasco API
|
|
2029
|
-
* If you have a proxy set up, it will send the data to the proxy and you can handle forawrding the data to the Saasco events API
|
|
2030
|
-
* @param path
|
|
2031
|
-
* @param data
|
|
2032
|
-
*/
|
|
2033
|
-
doRequest(path, data) {
|
|
2034
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
2035
|
-
const base = this.config.proxy || 'https://www.saasco.com/api/';
|
|
2036
|
-
const url = `${base}${path}`;
|
|
2037
|
-
// If analytics is disabled, don't send the request
|
|
2038
|
-
if (this.config.enabled === false) return {
|
|
2039
|
-
success: false,
|
|
2040
|
-
message: 'Analytics is disabled'
|
|
2041
|
-
};
|
|
2042
|
-
try {
|
|
2043
|
-
const response = yield fetch(url, {
|
|
2044
|
-
method: 'POST',
|
|
2045
|
-
headers: {
|
|
2046
|
-
'Content-Type': 'application/json'
|
|
2047
|
-
},
|
|
2048
|
-
body: JSON.stringify(data)
|
|
2049
|
-
});
|
|
2050
|
-
const responseBody = yield response.json();
|
|
2051
|
-
if (!response.ok) {
|
|
2052
|
-
throw new Error(`${responseBody.message}: ${responseBody.errors ? `[${responseBody.errors.join(', ')}]` : ''}` || JSON.stringify(responseBody));
|
|
2053
|
-
}
|
|
2054
|
-
return {
|
|
2055
|
-
success: true,
|
|
2056
|
-
message: responseBody.message
|
|
2057
|
-
};
|
|
2058
|
-
} catch (error) {
|
|
2059
|
-
this.logger.error(`\nError Message: "${error.message}"`, `\nPath: "/${path}"`, `\nRequest Data: ${JSON.stringify(data, null, 2)}`);
|
|
2060
|
-
return {
|
|
2061
|
-
success: false,
|
|
2062
|
-
message: error.message
|
|
2063
|
-
};
|
|
2064
|
-
}
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
|
-
/**
|
|
2068
|
-
* If autoPageTracking is enabled, this will automatically track page views
|
|
2069
|
-
* It listens to url changes to track new pages every time the url changes
|
|
2070
|
-
* @returns
|
|
2071
|
-
*/
|
|
2072
|
-
initAutoPageTracking() {
|
|
2073
|
-
var _a, _b;
|
|
2074
|
-
// Disable auto page tracking if the config is set to false
|
|
2075
|
-
if (!((_a = this.config.autoPageTracking) === null || _a === void 0 ? void 0 : _a.enabled)) return;
|
|
2076
|
-
// Prevent running on the server
|
|
2077
|
-
if (isServer) return console.warn('Saasco auto page tracking is only available in the browser');
|
|
2078
|
-
// Prevent intitializing auto page tracking more than once
|
|
2079
|
-
if (window.saascoAutoPageTrackingActive) {
|
|
2080
|
-
this.logger.info('Auto Page Tracking already enabled');
|
|
2081
|
-
return;
|
|
2082
|
-
}
|
|
2083
|
-
window.saascoAutoPageTrackingActive = true;
|
|
2084
|
-
this.logger.info('Auto Page Tracking enabled');
|
|
2085
|
-
// Track initial page load
|
|
2086
|
-
this.page();
|
|
2087
|
-
// Listen for hash changes if hash tracking is enabled
|
|
2088
|
-
if ((_b = this.config.autoPageTracking) === null || _b === void 0 ? void 0 : _b.trackHash) {
|
|
2089
|
-
window.addEventListener('hashchange', () => this.page());
|
|
2090
|
-
}
|
|
2091
|
-
// Listen to popstate for back and forward navigation
|
|
2092
|
-
window.addEventListener('popstate', () => this.page());
|
|
2093
|
-
// Wrap history push state to listen for URL changes
|
|
2094
|
-
const historyPushState = history.pushState;
|
|
2095
|
-
history.pushState = (...args) => {
|
|
2096
|
-
const returnValue = historyPushState.apply(history, args);
|
|
2097
|
-
this.page();
|
|
2098
|
-
return returnValue;
|
|
2099
|
-
};
|
|
2100
|
-
// Wrap history replace state to listen for URL changes
|
|
2101
|
-
const historyReplaceState = history.replaceState;
|
|
2102
|
-
history.replaceState = (...args) => {
|
|
2103
|
-
const returnValue = historyReplaceState.apply(history, args);
|
|
2104
|
-
this.page();
|
|
2105
|
-
return returnValue;
|
|
2106
|
-
};
|
|
2107
|
-
}
|
|
2108
|
-
getIntegrationsStats() {
|
|
2109
|
-
return this.integrationManager.getStats();
|
|
2110
|
-
}
|
|
2111
|
-
}
|
|
2112
|
-
function getLoggerLevel(config) {
|
|
2113
|
-
// Check URL parameters for debug overrides (browser only)
|
|
2114
|
-
let urlDebug = false;
|
|
2115
|
-
let urlDebugVerbose = false;
|
|
2116
|
-
if (!isServer) {
|
|
2117
|
-
try {
|
|
2118
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
2119
|
-
urlDebug = urlParams.get('saasco-debug') === 'true';
|
|
2120
|
-
urlDebugVerbose = urlParams.get('saasco-debug-verbose') === 'true';
|
|
2121
|
-
} catch (error) {
|
|
2122
|
-
// Ignore URL parsing errors
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
// URL parameters override config
|
|
2126
|
-
const effectiveDebug = urlDebug || config.debug;
|
|
2127
|
-
const effectiveDebugVerbose = urlDebugVerbose || config.debugVerbose;
|
|
2128
|
-
return !!effectiveDebugVerbose ? LogLevel.DEBUG : !!effectiveDebug ? LogLevel.INFO : LogLevel.ERROR;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
const browserContextSchema = zod.z.object({
|
|
2132
|
-
$locale: zod.z.string(),
|
|
2133
|
-
$location: zod.z.string(),
|
|
2134
|
-
$href: zod.z.string(),
|
|
2135
|
-
$pathname: zod.z.string(),
|
|
2136
|
-
$referrer: zod.z.string(),
|
|
2137
|
-
$screenDPI: zod.z.number(),
|
|
2138
|
-
$screenHeight: zod.z.number(),
|
|
2139
|
-
$screenWidth: zod.z.number(),
|
|
2140
|
-
$title: zod.z.string(),
|
|
2141
|
-
$userAgent: zod.z.string(),
|
|
2142
|
-
$utmSource: zod.z.string().nullable(),
|
|
2143
|
-
$utmMedium: zod.z.string().nullable(),
|
|
2144
|
-
$utmCampaign: zod.z.string().nullable(),
|
|
2145
|
-
$utmTerm: zod.z.string().nullable(),
|
|
2146
|
-
$utmContent: zod.z.string().nullable(),
|
|
2147
|
-
$utmId: zod.z.string().nullable(),
|
|
2148
|
-
$utmSourcePlatform: zod.z.string().nullable(),
|
|
2149
|
-
$utmCampaignId: zod.z.string().nullable(),
|
|
2150
|
-
$utmCreativeFormat: zod.z.string().nullable(),
|
|
2151
|
-
$utmMarketingTactic: zod.z.string().nullable(),
|
|
2152
|
-
$utmAdSource: zod.z.string().nullable(),
|
|
2153
|
-
$utmAdId: zod.z.string().nullable()
|
|
2154
|
-
});
|
|
2155
|
-
|
|
2156
|
-
exports.IntegrationManager = IntegrationManager;
|
|
2157
|
-
exports.Saasco = Saasco;
|
|
2158
|
-
exports.browserContextSchema = browserContextSchema;
|
|
2159
|
-
exports.createFacebookPixelIntegration = createFacebookPixelIntegration;
|
|
2160
|
-
exports.createPinterestTagIntegration = createPinterestTagIntegration;
|
|
2161
|
-
exports.createTikTokPixelIntegration = createTikTokPixelIntegration;
|
|
2162
|
-
exports.getBrowserContext = getBrowserContext;
|
|
2163
|
-
exports.standardFacebookEvents = standardFacebookEvents;
|
|
2164
|
-
exports.standardPinterestEvents = standardPinterestEvents;
|
|
2165
|
-
exports.standardTikTokEvents = standardTikTokEvents;
|
|
2166
|
-
exports.timezones = timezones;
|