@snowplow/signals-browser-plugin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,951 @@
1
+ /*!
2
+ * Snowplow Signals Interventions SDK v0.0.1 (https://github.com/snowplow-incubator/signals-browser-plugin)
3
+ * Copyright 2025 Snowplow Analytics Ltd
4
+ * Licensed under BSD-3-Clause
5
+ */
6
+
7
+ (function (global, factory) {
8
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
9
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
10
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.snowplowSignals = {}));
11
+ })(this, (function (exports) { 'use strict';
12
+
13
+ /******************************************************************************
14
+ Copyright (c) Microsoft Corporation.
15
+
16
+ Permission to use, copy, modify, and/or distribute this software for any
17
+ purpose with or without fee is hereby granted.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
20
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
22
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
24
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25
+ PERFORMANCE OF THIS SOFTWARE.
26
+ ***************************************************************************** */
27
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
28
+
29
+
30
+ function __spreadArray(to, from, pack) {
31
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
32
+ if (ar || !(i in from)) {
33
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
34
+ ar[i] = from[i];
35
+ }
36
+ }
37
+ return to.concat(ar || Array.prototype.slice.call(from));
38
+ }
39
+
40
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
41
+ var e = new Error(message);
42
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
43
+ };
44
+
45
+ /*!
46
+ * Core functionality for Snowplow JavaScript trackers v4.6.5 (http://bit.ly/sp-js)
47
+ * Copyright 2022 Snowplow Analytics Ltd, 2010 Anthon Pang
48
+ * Licensed under BSD-3-Clause
49
+ */
50
+
51
+
52
+ /*
53
+ * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang
54
+ * All rights reserved.
55
+ *
56
+ * Redistribution and use in source and binary forms, with or without
57
+ * modification, are permitted provided that the following conditions are met:
58
+ *
59
+ * 1. Redistributions of source code must retain the above copyright notice, this
60
+ * list of conditions and the following disclaimer.
61
+ *
62
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
63
+ * this list of conditions and the following disclaimer in the documentation
64
+ * and/or other materials provided with the distribution.
65
+ *
66
+ * 3. Neither the name of the copyright holder nor the names of its
67
+ * contributors may be used to endorse or promote products derived from
68
+ * this software without specific prior written permission.
69
+ *
70
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
71
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
73
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
74
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
76
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
77
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
78
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
79
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
80
+ */
81
+ function payloadBuilder() {
82
+ var dict = {}, allJson = [], jsonForProcessing = [], contextEntitiesForProcessing = [];
83
+ var processor;
84
+ var add = function (key, value) {
85
+ if (value != null && value !== '') {
86
+ // null also checks undefined
87
+ dict[key] = value;
88
+ }
89
+ };
90
+ var addDict = function (dict) {
91
+ for (var key in dict) {
92
+ if (Object.prototype.hasOwnProperty.call(dict, key)) {
93
+ add(key, dict[key]);
94
+ }
95
+ }
96
+ };
97
+ var addJson = function (keyIfEncoded, keyIfNotEncoded, json) {
98
+ if (json && isNonEmptyJson(json)) {
99
+ var jsonWithKeys = { keyIfEncoded: keyIfEncoded, keyIfNotEncoded: keyIfNotEncoded, json: json };
100
+ jsonForProcessing.push(jsonWithKeys);
101
+ allJson.push(jsonWithKeys);
102
+ }
103
+ };
104
+ var addContextEntity = function (entity) {
105
+ contextEntitiesForProcessing.push(entity);
106
+ };
107
+ return {
108
+ add: add,
109
+ addDict: addDict,
110
+ addJson: addJson,
111
+ addContextEntity: addContextEntity,
112
+ getPayload: function () { return dict; },
113
+ getJson: function () { return allJson; },
114
+ withJsonProcessor: function (jsonProcessor) {
115
+ processor = jsonProcessor;
116
+ },
117
+ build: function () {
118
+ processor === null || processor === void 0 ? void 0 : processor(this, jsonForProcessing, contextEntitiesForProcessing);
119
+ return dict;
120
+ },
121
+ };
122
+ }
123
+ /**
124
+ * Is property a non-empty JSON?
125
+ * @param property - Checks if object is non-empty json
126
+ */
127
+ function isNonEmptyJson(property) {
128
+ if (!isJson(property)) {
129
+ return false;
130
+ }
131
+ for (var key in property) {
132
+ if (Object.prototype.hasOwnProperty.call(property, key)) {
133
+ return true;
134
+ }
135
+ }
136
+ return false;
137
+ }
138
+ /**
139
+ * Is property a JSON?
140
+ * @param property - Checks if object is json
141
+ */
142
+ function isJson(property) {
143
+ return (typeof property !== 'undefined' &&
144
+ property !== null &&
145
+ (property.constructor === {}.constructor || property.constructor === [].constructor));
146
+ }
147
+
148
+ /*
149
+ * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang
150
+ * All rights reserved.
151
+ *
152
+ * Redistribution and use in source and binary forms, with or without
153
+ * modification, are permitted provided that the following conditions are met:
154
+ *
155
+ * 1. Redistributions of source code must retain the above copyright notice, this
156
+ * list of conditions and the following disclaimer.
157
+ *
158
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
159
+ * this list of conditions and the following disclaimer in the documentation
160
+ * and/or other materials provided with the distribution.
161
+ *
162
+ * 3. Neither the name of the copyright holder nor the names of its
163
+ * contributors may be used to endorse or promote products derived from
164
+ * this software without specific prior written permission.
165
+ *
166
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
167
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
168
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
169
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
170
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
171
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
172
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
173
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
174
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
175
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
176
+ */
177
+ var label = 'Snowplow: ';
178
+ var LOG_LEVEL;
179
+ (function (LOG_LEVEL) {
180
+ LOG_LEVEL[LOG_LEVEL["none"] = 0] = "none";
181
+ LOG_LEVEL[LOG_LEVEL["error"] = 1] = "error";
182
+ LOG_LEVEL[LOG_LEVEL["warn"] = 2] = "warn";
183
+ LOG_LEVEL[LOG_LEVEL["debug"] = 3] = "debug";
184
+ LOG_LEVEL[LOG_LEVEL["info"] = 4] = "info";
185
+ })(LOG_LEVEL || (LOG_LEVEL = {}));
186
+ logger$1();
187
+ function logger$1(logLevel) {
188
+ if (logLevel === void 0) { logLevel = LOG_LEVEL.warn; }
189
+ function setLogLevel(level) {
190
+ if (LOG_LEVEL[level]) {
191
+ logLevel = level;
192
+ }
193
+ else {
194
+ logLevel = LOG_LEVEL.warn;
195
+ }
196
+ }
197
+ /**
198
+ * Log errors, with or without error object
199
+ */
200
+ function error(message, error) {
201
+ var extraParams = [];
202
+ for (var _i = 2; _i < arguments.length; _i++) {
203
+ extraParams[_i - 2] = arguments[_i];
204
+ }
205
+ if (logLevel >= LOG_LEVEL.error && typeof console !== 'undefined') {
206
+ var logMsg = label + message + '\n';
207
+ if (error) {
208
+ console.error.apply(console, __spreadArray([logMsg + '\n', error], extraParams, false));
209
+ }
210
+ else {
211
+ console.error.apply(console, __spreadArray([logMsg], extraParams, false));
212
+ }
213
+ }
214
+ }
215
+ /**
216
+ * Log warnings, with or without error object
217
+ */
218
+ function warn(message, error) {
219
+ var extraParams = [];
220
+ for (var _i = 2; _i < arguments.length; _i++) {
221
+ extraParams[_i - 2] = arguments[_i];
222
+ }
223
+ if (logLevel >= LOG_LEVEL.warn && typeof console !== 'undefined') {
224
+ var logMsg = label + message;
225
+ if (error) {
226
+ console.warn.apply(console, __spreadArray([logMsg + '\n', error], extraParams, false));
227
+ }
228
+ else {
229
+ console.warn.apply(console, __spreadArray([logMsg], extraParams, false));
230
+ }
231
+ }
232
+ }
233
+ /**
234
+ * Log debug messages
235
+ */
236
+ function debug(message) {
237
+ var extraParams = [];
238
+ for (var _i = 1; _i < arguments.length; _i++) {
239
+ extraParams[_i - 1] = arguments[_i];
240
+ }
241
+ if (logLevel >= LOG_LEVEL.debug && typeof console !== 'undefined') {
242
+ console.debug.apply(console, __spreadArray([label + message], extraParams, false));
243
+ }
244
+ }
245
+ /**
246
+ * Log info messages
247
+ */
248
+ function info(message) {
249
+ var extraParams = [];
250
+ for (var _i = 1; _i < arguments.length; _i++) {
251
+ extraParams[_i - 1] = arguments[_i];
252
+ }
253
+ if (logLevel >= LOG_LEVEL.info && typeof console !== 'undefined') {
254
+ console.info.apply(console, __spreadArray([label + message], extraParams, false));
255
+ }
256
+ }
257
+ return { setLogLevel: setLogLevel, warn: warn, error: error, debug: debug, info: info };
258
+ }
259
+ /**
260
+ * Find dynamic context generating functions and return their results to be merged into the static contexts
261
+ * Combine an array of unchanging contexts with the result of a context-creating function
262
+ *
263
+ * @param dynamicOrStaticContexts - Array of custom context Objects or custom context generating functions
264
+ * @param Parameters - to pass to dynamic context callbacks
265
+ * @returns An array of Self Describing JSON context
266
+ */
267
+ function resolveDynamicContext(dynamicOrStaticContexts) {
268
+ var _a;
269
+ var extraParams = [];
270
+ for (var _i = 1; _i < arguments.length; _i++) {
271
+ extraParams[_i - 1] = arguments[_i];
272
+ }
273
+ return ((_a = dynamicOrStaticContexts === null || dynamicOrStaticContexts === void 0 ? void 0 : dynamicOrStaticContexts.map(function (context) {
274
+ if (typeof context === 'function') {
275
+ try {
276
+ return context.apply(void 0, extraParams);
277
+ }
278
+ catch (e) {
279
+ //TODO: provide warning
280
+ return undefined;
281
+ }
282
+ }
283
+ else {
284
+ return context;
285
+ }
286
+ }).filter(Boolean)) !== null && _a !== void 0 ? _a : []);
287
+ }
288
+ /**
289
+ * Build a self-describing event
290
+ * A custom event type, allowing for an event to be tracked using your own custom schema
291
+ * and a data object which conforms to the supplied schema
292
+ *
293
+ * @param event - Contains the properties and schema location for the event
294
+ * @returns PayloadBuilder to be sent to {@link @snowplow/tracker-core#TrackerCore.track}
295
+ */
296
+ function buildSelfDescribingEvent(event) {
297
+ var _a = event.event, schema = _a.schema, data = _a.data, pb = payloadBuilder();
298
+ var ueJson = {
299
+ schema: 'iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0',
300
+ data: { schema: schema, data: data },
301
+ };
302
+ pb.add('e', 'ue');
303
+ pb.addJson('ue_px', 'ue_pr', ueJson);
304
+ return pb;
305
+ }
306
+
307
+ var LogLevel;
308
+ (function (LogLevel) {
309
+ LogLevel["DEBUG"] = "debug";
310
+ LogLevel["ERROR"] = "error";
311
+ LogLevel["INFO"] = "info";
312
+ LogLevel["WARN"] = "warn";
313
+ })(LogLevel || (LogLevel = {}));
314
+ var name;
315
+ var LOG;
316
+ /**
317
+ * Wrap the passed logger so that it's clear which plugin/tracker instance log messages refer to
318
+ * @param level Log level for the logged message
319
+ * @param trackerId Tracker ID logging the event
320
+ * @param args Log message and other arguments
321
+ */
322
+ var logger = function (level, trackerId) {
323
+ var args = [];
324
+ for (var _i = 2; _i < arguments.length; _i++) {
325
+ args[_i - 2] = arguments[_i];
326
+ }
327
+ if (!name || !LOG) {
328
+ return;
329
+ }
330
+ var prefix = "[".concat(name, ":").concat(trackerId, "] ");
331
+ if (typeof args[0] === 'string') {
332
+ var msg = prefix + args.shift();
333
+ LOG[level].apply(LOG, __spreadArray([msg], args, false));
334
+ }
335
+ else {
336
+ LOG[level].apply(LOG, __spreadArray([prefix], args, false));
337
+ }
338
+ };
339
+ var setLogger = function (plugin, log) {
340
+ name = plugin;
341
+ LOG = log;
342
+ };
343
+
344
+ var ENTITY_ALIASES = ['co', 'cx', 'context', 'entity', 'entities'];
345
+ var EVENT_ALIASES = ['ue', 'ue_pr', 'ue_px', 'unstruct', 'unstruct_event', 'self_describing', 'sde'];
346
+ var EVENT_SCHEMAS = {
347
+ se: 'iglu:com.google.analytics/event/jsonschema/1-0-0',
348
+ ev: 'iglu:com.google.analytics/event/jsonschema/1-0-0',
349
+ ad: 'iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0',
350
+ tr: 'iglu:com.snowplowanalytics.snowplow/transaction/jsonschema/1-0-0',
351
+ ti: 'iglu:com.snowplowanalytics.snowplow/transaction_item/1-0-0',
352
+ pv: 'iglu:com.snowplowanalytics.snowplow/page_view/jsonschema/1-0-0',
353
+ pp: 'iglu:com.snowplowanalytics.snowplow/page_ping/jsonschema/1-0-0',
354
+ ue: '', // here to pass an `in` check below; needs to be extracted from SDJ payload
355
+ };
356
+ var objWithKey = function (obj, key) {
357
+ return typeof obj === 'object' && obj != null && key in obj;
358
+ };
359
+ var extractUrlFrom = function (part, field) {
360
+ return function (event) {
361
+ try {
362
+ return new URL(event[field])[part].toString() || undefined;
363
+ }
364
+ catch (_) {
365
+ return;
366
+ }
367
+ };
368
+ };
369
+ var extractUrlParamFrom = function (param, field) {
370
+ return function (event) {
371
+ var _a;
372
+ try {
373
+ return (_a = new URL(event[field]).searchParams.get(param)) !== null && _a !== void 0 ? _a : undefined;
374
+ }
375
+ catch (_) {
376
+ return;
377
+ }
378
+ };
379
+ };
380
+ var extractParamPartFrom = function (param, field, index, sep) {
381
+ if (sep === void 0) { sep = '.'; }
382
+ return function (event) {
383
+ var _a;
384
+ try {
385
+ var full = (_a = new URL(event[field]).searchParams.get(param)) !== null && _a !== void 0 ? _a : undefined;
386
+ return full ? full.split(sep)[index] : undefined;
387
+ }
388
+ catch (_) {
389
+ return;
390
+ }
391
+ };
392
+ };
393
+ var extractDimensionValueFrom = function (dim, field) {
394
+ return function (event) {
395
+ try {
396
+ return event[field].split('x')[dim];
397
+ }
398
+ catch (_) {
399
+ return;
400
+ }
401
+ };
402
+ };
403
+ /**
404
+ * `Payload` instances access data via their tracker protocol field names, rather than the more human-friendly ones users are familiar with from enriched data.
405
+ * This maps the enriched fields back to their TP equivalents; allowing for field extraction where necessary.
406
+ */
407
+ var selectorMap = {
408
+ app_id: 'aid',
409
+ platform: 'p',
410
+ dvce_created_tstamp: 'dtm',
411
+ event: function (_a) {
412
+ var _b;
413
+ var e = _a.e;
414
+ return ((_b = {
415
+ se: 'struct',
416
+ ev: 'struct',
417
+ ue: 'unstruct',
418
+ ad: 'ad_impression',
419
+ tr: 'transaction',
420
+ ti: 'transaction_item',
421
+ pv: 'page_view',
422
+ pp: 'page_ping',
423
+ }[e]) !== null && _b !== void 0 ? _b : e);
424
+ },
425
+ event_id: 'eid',
426
+ txn_id: 'tid',
427
+ name_tracker: 'tna',
428
+ v_tracker: 'tv',
429
+ user_id: 'uid',
430
+ user_ipaddress: 'ip',
431
+ user_fingerprint: 'fp',
432
+ domain_userid: 'duid',
433
+ domain_sessionidx: 'vid',
434
+ network_userid: 'nuid',
435
+ page_url: 'url',
436
+ page_title: 'page',
437
+ page_referrer: 'refr',
438
+ page_urlscheme: extractUrlFrom('protocol', 'url'),
439
+ page_urlhost: extractUrlFrom('hostname', 'url'),
440
+ page_urlport: extractUrlFrom('port', 'url'),
441
+ page_urlpath: extractUrlFrom('pathname', 'url'),
442
+ page_urlquery: extractUrlFrom('search', 'url'),
443
+ page_urlfragment: extractUrlFrom('hash', 'url'),
444
+ refr_urlscheme: extractUrlFrom('protocol', 'refr'),
445
+ refr_urlhost: extractUrlFrom('hostname', 'refr'),
446
+ refr_urlport: extractUrlFrom('port', 'refr'),
447
+ refr_urlpath: extractUrlFrom('pathname', 'refr'),
448
+ refr_urlquery: extractUrlFrom('search', 'refr'),
449
+ refr_urlfragment: extractUrlFrom('hash', 'refr'),
450
+ mkt_medium: extractUrlParamFrom('utm_medium', 'url'),
451
+ mkt_source: extractUrlParamFrom('utm_source', 'url'),
452
+ mkt_term: extractUrlParamFrom('utm_term', 'url'),
453
+ mkt_content: extractUrlParamFrom('utm_content', 'url'),
454
+ mkt_campaign: extractUrlParamFrom('utm_campaign', 'url'),
455
+ se_category: 'se_ca',
456
+ se_action: 'se_ac',
457
+ se_label: 'se_la',
458
+ se_property: 'se_pr',
459
+ se_value: 'se_va',
460
+ tr_orderid: 'tr_id',
461
+ tr_affiliation: 'tr_af',
462
+ tr_total: 'tr_tt',
463
+ tr_tax: 'tr_tx',
464
+ tr_shipping: 'tr_sh',
465
+ tr_city: 'tr_ci',
466
+ tr_state: 'tr_st',
467
+ tr_country: 'tr_co',
468
+ ti_orderid: 'ti_id',
469
+ ti_sku: 'ti_sk',
470
+ ti_name: 'ti_na',
471
+ ti_category: 'ti_ca',
472
+ ti_price: 'ti_pr',
473
+ ti_quantity: 'ti_qu',
474
+ pp_xoffset_min: 'pp_mix',
475
+ pp_xoffset_max: 'pp_max',
476
+ pp_yoffset_min: 'pp_miy',
477
+ pp_yoffset_max: 'pp_may',
478
+ useragent: function (_a) {
479
+ var ua = _a.ua;
480
+ return ua !== null && ua !== void 0 ? ua : navigator.userAgent;
481
+ }, // often elided and extracted from HTTP header
482
+ br_lang: 'lang',
483
+ br_features_pdf: 'f_pdf',
484
+ br_features_flash: 'f_fla',
485
+ br_features_java: 'f_java',
486
+ br_features_director: 'f_dir',
487
+ br_features_quicktime: 'f_qt',
488
+ br_features_realplayer: 'f_realp',
489
+ br_features_windowsmedia: 'f_wma',
490
+ br_features_gears: 'f_gears',
491
+ br_features_silverlight: 'f_ag',
492
+ br_cookies: 'cookie',
493
+ br_colordepth: 'cd',
494
+ br_viewwidth: extractDimensionValueFrom(0, 'vp'),
495
+ br_viewheight: extractDimensionValueFrom(1, 'vp'),
496
+ os_timezone: 'tz',
497
+ dvce_screenwidth: extractDimensionValueFrom(0, 'res'),
498
+ dvce_screenheight: extractDimensionValueFrom(1, 'res'),
499
+ doc_charset: 'cs',
500
+ doc_width: extractDimensionValueFrom(0, 'ds'),
501
+ doc_height: extractDimensionValueFrom(1, 'ds'),
502
+ tr_currency: 'tr_cu',
503
+ ti_currency: 'ti_cu',
504
+ mkt_clickid: extractUrlParamFrom('gclid', 'url'),
505
+ dvce_sent_tstamp: 'stm',
506
+ refr_domain_userid: extractParamPartFrom('_sp', 'url', 0),
507
+ refr_device_tstamp: extractParamPartFrom('_sp', 'url', 1),
508
+ domain_sessionid: 'sid',
509
+ event_vendor: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.vendor; },
510
+ event_name: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.name; },
511
+ event_format: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.format; },
512
+ event_version: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.version; },
513
+ true_tstamp: 'ttm',
514
+ };
515
+ var parseJsonPointer = function (pointer) {
516
+ if (!pointer) {
517
+ return;
518
+ }
519
+ if (pointer[0] !== '/') {
520
+ pointer = "/".concat(pointer); // for atomic-level fields, this will allow regular names since JSON Pointer isn't super intuitive
521
+ }
522
+ return pointer
523
+ .split('/')
524
+ .map(function (segment) { return segment.replace(/~1/g, '/').replace(/~0/g, '~'); })
525
+ .slice(1);
526
+ };
527
+ var parseEncodedFields = function (evt, fields) {
528
+ return fields.map(function (field) {
529
+ if (objWithKey(evt, field)) {
530
+ var val = evt[field];
531
+ if (typeof val === 'string') {
532
+ try {
533
+ return JSON.parse(val);
534
+ }
535
+ catch (_) {
536
+ try {
537
+ return JSON.parse(atob(val.replace(/[_-]/g, function (m) { return (m == '-' ? '+' : '/'); })));
538
+ }
539
+ catch (_) { }
540
+ }
541
+ }
542
+ }
543
+ });
544
+ };
545
+ /**
546
+ * Build a lookup map of `vendor: name[]` for entities to lookup more easily
547
+ */
548
+ var summarizeEntities = function (evt) {
549
+ var fields = parseEncodedFields(evt, ['co', 'cx']);
550
+ return fields.reduce(function (acc, ctx) {
551
+ if (objWithKey(ctx, 'data') && Array.isArray(ctx.data)) {
552
+ ctx.data.forEach(function (entity) {
553
+ var _a, _b;
554
+ if (objWithKey(entity, 'schema') && objWithKey(entity, 'data')) {
555
+ if (typeof entity.schema === 'string' && entity.schema.indexOf('iglu:') === 0) {
556
+ var parts = entity.schema.slice(5).split('/');
557
+ if (parts.length !== 4) {
558
+ return;
559
+ }
560
+ var vendor = (acc[parts[0]] = (_a = acc[parts[0]]) !== null && _a !== void 0 ? _a : {});
561
+ vendor[parts[1]] = (_b = vendor[parts[1]]) !== null && _b !== void 0 ? _b : [];
562
+ vendor[parts[1]].push(entity.data);
563
+ }
564
+ }
565
+ });
566
+ }
567
+ return acc;
568
+ }, {});
569
+ };
570
+ /**
571
+ * Build a lookup map of `vendor: name` for SDE payloads to lookup more easily
572
+ */
573
+ var summarizeEvent = function (evt) {
574
+ var fields = parseEncodedFields(evt, ['ue_pr', 'ue_px']);
575
+ return fields.reduce(function (acc, ctx) {
576
+ var _a;
577
+ if (objWithKey(ctx, 'data')) {
578
+ if (objWithKey(ctx.data, 'schema') && objWithKey(ctx.data, 'data')) {
579
+ var event_1 = ctx.data;
580
+ if (typeof event_1.schema === 'string' && event_1.schema.indexOf('iglu:') === 0) {
581
+ var parts = event_1.schema.slice(5).split('/');
582
+ if (parts.length !== 4) {
583
+ return;
584
+ }
585
+ var vendor = (acc[parts[0]] = (_a = acc[parts[0]]) !== null && _a !== void 0 ? _a : {});
586
+ vendor[parts[1]] = event_1.data;
587
+ }
588
+ }
589
+ }
590
+ return acc;
591
+ }, {});
592
+ };
593
+ var getEventSelf = function (evt) {
594
+ var _a;
595
+ var schema = undefined;
596
+ if (!objWithKey(evt, 'e') || typeof evt.e !== 'string' || !(evt.e in EVENT_SCHEMAS)) {
597
+ return;
598
+ }
599
+ if (evt.e === 'ue') {
600
+ var fields = parseEncodedFields(evt, ['ue_pr', 'ue_px']);
601
+ var ue = (_a = fields[0]) !== null && _a !== void 0 ? _a : fields[1];
602
+ if (objWithKey(ue, 'data') && objWithKey(ue.data, 'schema') && typeof ue.data.schema === 'string') {
603
+ schema = ue.data.schema;
604
+ }
605
+ }
606
+ else {
607
+ schema = EVENT_SCHEMAS[evt.e];
608
+ }
609
+ if (typeof schema === 'string' && schema.indexOf('iglu:') === 0) {
610
+ var _b = schema.slice(5).split('/'), vendor = _b[0], name_1 = _b[1], format = _b[2], version = _b[3];
611
+ return { vendor: vendor, name: name_1, format: format, version: version };
612
+ }
613
+ };
614
+ var derefJsonPointer = function (pointerString, obj) {
615
+ var _a, _b;
616
+ var pointer = parseJsonPointer(pointerString);
617
+ if (pointer == null) {
618
+ return obj; // return whole document
619
+ }
620
+ if (typeof obj !== 'object' || obj === null) {
621
+ return; // no segments will work
622
+ }
623
+ // map enriched fieldname <> TP field
624
+ if (pointer[0] in selectorMap) {
625
+ var mappedSelector = selectorMap[pointer[0]];
626
+ if (typeof mappedSelector === 'string' && objWithKey(obj, mappedSelector)) {
627
+ return obj[mappedSelector];
628
+ }
629
+ else if (typeof mappedSelector === 'function') {
630
+ return mappedSelector(obj);
631
+ }
632
+ }
633
+ // descend into nested SDJ payloads
634
+ var cursor = obj;
635
+ if (ENTITY_ALIASES.indexOf(pointer[0]) !== -1) {
636
+ cursor = (_a = {}, _a[pointer[0]] = summarizeEntities(cursor), _a);
637
+ }
638
+ if (EVENT_ALIASES.indexOf(pointer[0]) !== -1) {
639
+ cursor = (_b = {}, _b[pointer[0]] = summarizeEvent(cursor), _b);
640
+ }
641
+ for (var _i = 0, pointer_1 = pointer; _i < pointer_1.length; _i++) {
642
+ var segment = pointer_1[_i];
643
+ if (objWithKey(cursor, segment)) {
644
+ cursor = cursor[segment];
645
+ }
646
+ else if (Array.isArray(cursor) && cursor.length === 1 && objWithKey(cursor[0], segment)) {
647
+ cursor = cursor[0][segment];
648
+ }
649
+ else
650
+ return;
651
+ }
652
+ return cursor;
653
+ };
654
+ /**
655
+ * Given a list of rules to extract entity IDs and a Snowplow event payload, return any extracted ID results.
656
+ * @param targets Map of entity names to rule definitions for how to find ID values
657
+ * @param pb Snowplow event
658
+ * @returns Resulting IDs extracted from the event
659
+ */
660
+ function extractEntityValues(targets, pb) {
661
+ var extracted = {};
662
+ Object.entries(targets).forEach(function (_a) {
663
+ var entityName = _a[0], pointers = _a[1];
664
+ pointers = Array.isArray(pointers) ? pointers : [pointers];
665
+ for (var _i = 0, pointers_1 = pointers; _i < pointers_1.length; _i++) {
666
+ var pointer = pointers_1[_i];
667
+ if (pointer === '') {
668
+ continue;
669
+ }
670
+ var candidateId = derefJsonPointer(pointer, pb);
671
+ if (candidateId != null) {
672
+ extracted[entityName] = String(candidateId);
673
+ return;
674
+ }
675
+ }
676
+ });
677
+ return extracted;
678
+ }
679
+
680
+ var DEFAULT_PULL_API_PATH = '/api/v1/interventions';
681
+ var DEFAULT_ENTITY_TARGETS = {
682
+ domain_userid: '/domain_userid',
683
+ domain_sessionid: '/domain_sessionid',
684
+ //pageview_id: '/co/com.snowplowanalytics.snowplow/web_page/id', // not a default entity seed
685
+ //tab_id: '/co/com.snowplowanalytics.snowplow/browser_context/tabId', // not a default entity seed
686
+ };
687
+ var DEFAULT_CONNECTION_TIMEOUT_MS = 2500;
688
+ /**
689
+ * Default `Fetcher` implementation; uses SSEs, auto updates based on observed entity IDs defined with JSON Pointers
690
+ */
691
+ var InterventionFetcher = /** @class */ (function () {
692
+ function InterventionFetcher(tracker, dispatch) {
693
+ this.tracker = tracker;
694
+ this.dispatch = dispatch;
695
+ this.entityValues = {};
696
+ this.aborter = new AbortController();
697
+ this.timeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
698
+ this.newEndpoint = false;
699
+ this.currentParams = '';
700
+ this.entitySelectors = DEFAULT_ENTITY_TARGETS;
701
+ // reasonable defaults even without any events observed
702
+ var info = tracker.getDomainUserInfo();
703
+ this.update({
704
+ duid: info[1] || undefined,
705
+ sid: info[6] || undefined,
706
+ });
707
+ }
708
+ InterventionFetcher.prototype.configure = function (_a) {
709
+ var endpoint = _a.endpoint, _b = _a.apiPath, apiPath = _b === void 0 ? DEFAULT_PULL_API_PATH : _b, _c = _a.entityTargets, entityTargets = _c === void 0 ? DEFAULT_ENTITY_TARGETS : _c, _d = _a.entityIds, entityIds = _d === void 0 ? {} : _d, _e = _a.connectionTimeoutMs, connectionTimeoutMs = _e === void 0 ? DEFAULT_CONNECTION_TIMEOUT_MS : _e;
710
+ this.entitySelectors = Object.assign({}, this.entitySelectors, entityTargets);
711
+ this.timeoutMs = connectionTimeoutMs;
712
+ var prevEndpoint = this.endpoint;
713
+ this.endpoint = "".concat(/\/\//.test(endpoint) ? endpoint : 'https://' + endpoint).concat(apiPath);
714
+ this.newEndpoint = prevEndpoint != this.endpoint;
715
+ this.update(undefined, entityIds);
716
+ };
717
+ InterventionFetcher.prototype.update = function (payload, explicitEntities) {
718
+ if (explicitEntities === void 0) { explicitEntities = {}; }
719
+ if (payload) {
720
+ Object.assign(this.entityValues, extractEntityValues(this.entitySelectors, payload));
721
+ }
722
+ Object.assign(this.entityValues, explicitEntities);
723
+ var newParams = new URLSearchParams(this.entityValues).toString();
724
+ if (this.endpoint && (newParams != this.currentParams || this.newEndpoint)) {
725
+ logger(LogLevel.DEBUG, this.tracker.id, 'Entity IDs updated', this.entityValues);
726
+ this.currentParams = newParams;
727
+ this.requestInterventions();
728
+ }
729
+ };
730
+ /**
731
+ * Set up a new SSE connection and start dispatching any received events
732
+ */
733
+ InterventionFetcher.prototype.requestInterventions = function () {
734
+ var _this = this;
735
+ this.aborter.abort(); // abort any previous connection
736
+ var aborter = (this.aborter = new AbortController()); // new controller to reset aborted state
737
+ if (!this.endpoint) {
738
+ logger(LogLevel.ERROR, this.tracker.id, 'Requested interventions from undefined endpoint');
739
+ return;
740
+ }
741
+ var url = "".concat(this.endpoint, "?").concat(this.currentParams);
742
+ var stream = new EventSource(url);
743
+ aborter.signal.addEventListener('abort', stream.close.bind(stream), {
744
+ once: true,
745
+ });
746
+ var timeout = setTimeout(aborter.abort.bind(aborter), this.timeoutMs);
747
+ stream.addEventListener('open', function () { return clearTimeout(timeout); }, {
748
+ once: true,
749
+ });
750
+ stream.addEventListener('error', function (ev) {
751
+ return logger(LogLevel.ERROR, _this.tracker.id, 'Error fetching interventions:', ev);
752
+ });
753
+ stream.addEventListener('message', function (ev) {
754
+ _this.dispatch(JSON.parse(ev.data), _this.tracker);
755
+ });
756
+ };
757
+ /**
758
+ * Factory to match signature of public interface and create the default fetcher instance
759
+ * @param tracker Tracker activating the plugin
760
+ * @param dispatch Callback to pass interventions to
761
+ * @returns `InterventionFetcher` instance
762
+ */
763
+ InterventionFetcher.create = function (tracker, dispatch) { return new InterventionFetcher(tracker, dispatch); };
764
+ return InterventionFetcher;
765
+ }());
766
+
767
+ var Events = {
768
+ INTERVENTION_RECEIVED: 'iglu:com.snowplowanalytics.signals/intervention_receive/jsonschema/1-0-0',
769
+ INTERVENTION_HANDLE: 'iglu:com.snowplowanalytics.signals/intervention_handle/jsonschema/1-0-0',
770
+ INTERVENTION_HANDLE_ERROR: 'iglu:com.snowplowanalytics.signals/intervention_handle_error/jsonschema/1-0-0',
771
+ };
772
+ var Entities = {
773
+ INTERVENTION: 'iglu:com.snowplowanalytics.signals/intervention_instance/jsonschema/1-0-0',
774
+ };
775
+ var MEASUREMENT_EVENTS = {
776
+ delivery: Events.INTERVENTION_RECEIVED,
777
+ dispatch_accept: Events.INTERVENTION_HANDLE,
778
+ dispatch_error: Events.INTERVENTION_HANDLE_ERROR,
779
+ };
780
+
781
+ var DEFAULT_MEASUREMENT_SETTINGS = {
782
+ delivery: true,
783
+ dispatch_accept: true,
784
+ dispatch_error: true,
785
+ };
786
+ /* Per-tracker state */
787
+ var instances = {}; // entities will likely vary by tracker, so each set will need its own fetcher
788
+ var handlerRegistry = {}; // handlers can be added/removed for individual trackers
789
+ var measurementSettings = {}; // currently global and only specified per-plugin instance but may be required in future
790
+ /**
791
+ * Create an instance of the Signals Interventions plugin.
792
+ * @param configuration Configuration for the plugin
793
+ * @returns Configured plugin instance
794
+ */
795
+ function SignalsInterventionsPlugin(_a) {
796
+ var _b = _a === void 0 ? {
797
+ measurement: DEFAULT_MEASUREMENT_SETTINGS,
798
+ handlers: {},
799
+ } : _a, fetcher = _b.fetcher, _c = _b.measurement, measurement = _c === void 0 ? DEFAULT_MEASUREMENT_SETTINGS : _c, _d = _b.handlers, handlers = _d === void 0 ? {} : _d;
800
+ return {
801
+ activateBrowserPlugin: function (tracker) {
802
+ var _a;
803
+ logger(LogLevel.INFO, tracker.id, 'Activating plugin for tracker');
804
+ instances[tracker.id] = fetcher ? fetcher(tracker, dispatch) : InterventionFetcher.create(tracker, dispatch);
805
+ measurementSettings[tracker.id] = Object.assign({}, DEFAULT_MEASUREMENT_SETTINGS, measurement);
806
+ handlerRegistry[tracker.id] = Object.assign((_a = handlerRegistry[tracker.id]) !== null && _a !== void 0 ? _a : {}, handlers);
807
+ },
808
+ afterTrack: function (payload) {
809
+ var trackerName = payload['tna'];
810
+ if (typeof trackerName === 'string' && trackerName in instances) {
811
+ instances[trackerName].update(payload);
812
+ }
813
+ },
814
+ logger: function (LOG) {
815
+ setLogger(SignalsInterventionsPlugin.name, LOG);
816
+ },
817
+ };
818
+ }
819
+ /**
820
+ * Tracks an event upon receipt/handling of an intervention unless configured not to.
821
+ * @param settings Measurement settings defining whether an event should fire or not, and configuring custom context
822
+ * @param tracker The tracker to track the event with
823
+ * @param measurement The type of event to track
824
+ * @param intervention The intervention payload in question
825
+ * @param payload An event-specific payload, if required according to `measurement`
826
+ */
827
+ var measure = function (settings, tracker, measurement, intervention, payload) {
828
+ var _a;
829
+ var filter = settings[measurement];
830
+ if (filter && (typeof filter !== 'function' || filter(intervention))) {
831
+ var entities = [
832
+ {
833
+ schema: Entities.INTERVENTION,
834
+ data: {
835
+ intervention_id: intervention.intervention_id,
836
+ name: intervention.name,
837
+ version: intervention.version,
838
+ entity: intervention.target_entity,
839
+ attributes: intervention.attributes,
840
+ },
841
+ },
842
+ ];
843
+ tracker.core.track(buildSelfDescribingEvent({
844
+ event: {
845
+ schema: MEASUREMENT_EVENTS[measurement],
846
+ data: payload,
847
+ },
848
+ }), resolveDynamicContext(__spreadArray(__spreadArray([], entities, true), ((_a = settings.context) !== null && _a !== void 0 ? _a : []), true), measurement, intervention, payload));
849
+ }
850
+ };
851
+ /**
852
+ * Called by the `Fetcher` to track receipt of an Intervention and distribute to any handlers
853
+ * @param intervention The intervention payload that was fetched
854
+ * @param tracker The tracker associated with the plugin
855
+ */
856
+ var dispatch = function (intervention, tracker) {
857
+ var _a, _b;
858
+ var measurement = (_a = measurementSettings[tracker.id]) !== null && _a !== void 0 ? _a : DEFAULT_MEASUREMENT_SETTINGS;
859
+ var handlers = (_b = handlerRegistry[tracker.id]) !== null && _b !== void 0 ? _b : {};
860
+ var handlerIds = Object.keys(handlers);
861
+ if (!handlerIds.length) {
862
+ return logger(LogLevel.WARN, tracker.id, 'No handlers registered for intervention', intervention);
863
+ }
864
+ logger(LogLevel.INFO, tracker.id, 'Attempting dispatch for intervention', intervention, handlerIds);
865
+ measure(measurement, tracker, 'delivery', intervention, {
866
+ handlers: handlerIds,
867
+ });
868
+ for (var _i = 0, _c = Object.entries(handlers); _i < _c.length; _i++) {
869
+ var _d = _c[_i], handlerId = _d[0], handler = _d[1];
870
+ setTimeout(function (handlerId, handler, intervention, tracker) {
871
+ var success = function () {
872
+ logger(LogLevel.INFO, tracker.id, 'Intervention handled', handlerId, intervention);
873
+ measure(measurement, tracker, 'dispatch_accept', intervention, {
874
+ handler: handlerId,
875
+ });
876
+ };
877
+ var failure = function (err) {
878
+ logger(LogLevel.ERROR, tracker.id, 'Handler failed processing intervention', err, handlerId, intervention);
879
+ measure(measurement, tracker, 'dispatch_error', intervention, {
880
+ handler: handlerId,
881
+ error: err ? String(err) : undefined,
882
+ });
883
+ };
884
+ try {
885
+ var result = handler(intervention, tracker);
886
+ if (result instanceof Promise) {
887
+ result.then(success, failure);
888
+ }
889
+ else if (objWithKey(result, 'then') && typeof result.then === 'function') {
890
+ result.then(success, failure);
891
+ }
892
+ else
893
+ success();
894
+ }
895
+ catch (e) {
896
+ failure(e);
897
+ }
898
+ }, 0, handlerId, handler, intervention, tracker);
899
+ }
900
+ };
901
+ /**
902
+ * Configure the endpoint information for the plugin's `Fetcher` to subscribe to events from
903
+ * @param config Configuration about the endpoint and entity IDs/definitions to configure
904
+ * @param trackers List of tracker IDs that have activated the plugin to configure a `Fetcher` for
905
+ */
906
+ function subscribeToInterventions(config, trackers) {
907
+ if (trackers === void 0) { trackers = Object.keys(instances); }
908
+ for (var _i = 0, trackers_1 = trackers; _i < trackers_1.length; _i++) {
909
+ var trackerId = trackers_1[_i];
910
+ if (trackerId in instances) {
911
+ instances[trackerId].configure(config);
912
+ }
913
+ }
914
+ }
915
+ /**
916
+ * Start calling the given handlers when new interventions are received
917
+ * @param handlers Map of handler IDs to handler functions to call with new interventions
918
+ * @param trackers List of tracker IDs that have activated the plugin to add these handlers to
919
+ */
920
+ function addInterventionHandlers(handlers, trackers) {
921
+ var _a;
922
+ if (trackers === void 0) { trackers = Object.keys(instances); }
923
+ for (var _i = 0, trackers_2 = trackers; _i < trackers_2.length; _i++) {
924
+ var trackerId = trackers_2[_i];
925
+ handlerRegistry[trackerId] = Object.assign((_a = handlerRegistry[trackerId]) !== null && _a !== void 0 ? _a : {}, handlers);
926
+ }
927
+ }
928
+ /**
929
+ * Stop calling handlers with the given IDs with new interventions
930
+ * @param handlerIds One or more handler IDs to remove
931
+ * @param trackers List of trackers to remove the handlers for; defaults to all trackers that have activated the plugin.
932
+ */
933
+ function removeInterventionHandlers(handlerIds, trackers) {
934
+ if (trackers === void 0) { trackers = Object.keys(instances); }
935
+ var toRemove = Array.isArray(handlerIds) ? handlerIds : [handlerIds];
936
+ for (var _i = 0, toRemove_1 = toRemove; _i < toRemove_1.length; _i++) {
937
+ var handlerId = toRemove_1[_i];
938
+ for (var _a = 0, trackers_3 = trackers; _a < trackers_3.length; _a++) {
939
+ var trackerId = trackers_3[_a];
940
+ delete handlerRegistry[trackerId][handlerId];
941
+ }
942
+ }
943
+ }
944
+
945
+ exports.SignalsInterventionsPlugin = SignalsInterventionsPlugin;
946
+ exports.addInterventionHandlers = addInterventionHandlers;
947
+ exports.removeInterventionHandlers = removeInterventionHandlers;
948
+ exports.subscribeToInterventions = subscribeToInterventions;
949
+
950
+ }));
951
+ //# sourceMappingURL=index.umd.js.map