@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.
- package/LICENSE +29 -0
- package/README.md +131 -0
- package/dist/index.module.d.ts +187 -0
- package/dist/index.module.js +649 -0
- package/dist/index.module.js.map +1 -0
- package/dist/index.umd.js +951 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/index.umd.min.js +13 -0
- package/dist/index.umd.min.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,649 @@
|
|
|
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
|
+
import { __spreadArray } from 'tslib';
|
|
8
|
+
import { buildSelfDescribingEvent, resolveDynamicContext } from '@snowplow/tracker-core';
|
|
9
|
+
|
|
10
|
+
var LogLevel;
|
|
11
|
+
(function (LogLevel) {
|
|
12
|
+
LogLevel["DEBUG"] = "debug";
|
|
13
|
+
LogLevel["ERROR"] = "error";
|
|
14
|
+
LogLevel["INFO"] = "info";
|
|
15
|
+
LogLevel["WARN"] = "warn";
|
|
16
|
+
})(LogLevel || (LogLevel = {}));
|
|
17
|
+
var name;
|
|
18
|
+
var LOG;
|
|
19
|
+
/**
|
|
20
|
+
* Wrap the passed logger so that it's clear which plugin/tracker instance log messages refer to
|
|
21
|
+
* @param level Log level for the logged message
|
|
22
|
+
* @param trackerId Tracker ID logging the event
|
|
23
|
+
* @param args Log message and other arguments
|
|
24
|
+
*/
|
|
25
|
+
var logger = function (level, trackerId) {
|
|
26
|
+
var args = [];
|
|
27
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
28
|
+
args[_i - 2] = arguments[_i];
|
|
29
|
+
}
|
|
30
|
+
if (!name || !LOG) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
var prefix = "[".concat(name, ":").concat(trackerId, "] ");
|
|
34
|
+
if (typeof args[0] === 'string') {
|
|
35
|
+
var msg = prefix + args.shift();
|
|
36
|
+
LOG[level].apply(LOG, __spreadArray([msg], args, false));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
LOG[level].apply(LOG, __spreadArray([prefix], args, false));
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var setLogger = function (plugin, log) {
|
|
43
|
+
name = plugin;
|
|
44
|
+
LOG = log;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
var ENTITY_ALIASES = ['co', 'cx', 'context', 'entity', 'entities'];
|
|
48
|
+
var EVENT_ALIASES = ['ue', 'ue_pr', 'ue_px', 'unstruct', 'unstruct_event', 'self_describing', 'sde'];
|
|
49
|
+
var EVENT_SCHEMAS = {
|
|
50
|
+
se: 'iglu:com.google.analytics/event/jsonschema/1-0-0',
|
|
51
|
+
ev: 'iglu:com.google.analytics/event/jsonschema/1-0-0',
|
|
52
|
+
ad: 'iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0',
|
|
53
|
+
tr: 'iglu:com.snowplowanalytics.snowplow/transaction/jsonschema/1-0-0',
|
|
54
|
+
ti: 'iglu:com.snowplowanalytics.snowplow/transaction_item/1-0-0',
|
|
55
|
+
pv: 'iglu:com.snowplowanalytics.snowplow/page_view/jsonschema/1-0-0',
|
|
56
|
+
pp: 'iglu:com.snowplowanalytics.snowplow/page_ping/jsonschema/1-0-0',
|
|
57
|
+
ue: '', // here to pass an `in` check below; needs to be extracted from SDJ payload
|
|
58
|
+
};
|
|
59
|
+
var objWithKey = function (obj, key) {
|
|
60
|
+
return typeof obj === 'object' && obj != null && key in obj;
|
|
61
|
+
};
|
|
62
|
+
var extractUrlFrom = function (part, field) {
|
|
63
|
+
return function (event) {
|
|
64
|
+
try {
|
|
65
|
+
return new URL(event[field])[part].toString() || undefined;
|
|
66
|
+
}
|
|
67
|
+
catch (_) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
var extractUrlParamFrom = function (param, field) {
|
|
73
|
+
return function (event) {
|
|
74
|
+
var _a;
|
|
75
|
+
try {
|
|
76
|
+
return (_a = new URL(event[field]).searchParams.get(param)) !== null && _a !== void 0 ? _a : undefined;
|
|
77
|
+
}
|
|
78
|
+
catch (_) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
var extractParamPartFrom = function (param, field, index, sep) {
|
|
84
|
+
if (sep === void 0) { sep = '.'; }
|
|
85
|
+
return function (event) {
|
|
86
|
+
var _a;
|
|
87
|
+
try {
|
|
88
|
+
var full = (_a = new URL(event[field]).searchParams.get(param)) !== null && _a !== void 0 ? _a : undefined;
|
|
89
|
+
return full ? full.split(sep)[index] : undefined;
|
|
90
|
+
}
|
|
91
|
+
catch (_) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
var extractDimensionValueFrom = function (dim, field) {
|
|
97
|
+
return function (event) {
|
|
98
|
+
try {
|
|
99
|
+
return event[field].split('x')[dim];
|
|
100
|
+
}
|
|
101
|
+
catch (_) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* `Payload` instances access data via their tracker protocol field names, rather than the more human-friendly ones users are familiar with from enriched data.
|
|
108
|
+
* This maps the enriched fields back to their TP equivalents; allowing for field extraction where necessary.
|
|
109
|
+
*/
|
|
110
|
+
var selectorMap = {
|
|
111
|
+
app_id: 'aid',
|
|
112
|
+
platform: 'p',
|
|
113
|
+
dvce_created_tstamp: 'dtm',
|
|
114
|
+
event: function (_a) {
|
|
115
|
+
var _b;
|
|
116
|
+
var e = _a.e;
|
|
117
|
+
return ((_b = {
|
|
118
|
+
se: 'struct',
|
|
119
|
+
ev: 'struct',
|
|
120
|
+
ue: 'unstruct',
|
|
121
|
+
ad: 'ad_impression',
|
|
122
|
+
tr: 'transaction',
|
|
123
|
+
ti: 'transaction_item',
|
|
124
|
+
pv: 'page_view',
|
|
125
|
+
pp: 'page_ping',
|
|
126
|
+
}[e]) !== null && _b !== void 0 ? _b : e);
|
|
127
|
+
},
|
|
128
|
+
event_id: 'eid',
|
|
129
|
+
txn_id: 'tid',
|
|
130
|
+
name_tracker: 'tna',
|
|
131
|
+
v_tracker: 'tv',
|
|
132
|
+
user_id: 'uid',
|
|
133
|
+
user_ipaddress: 'ip',
|
|
134
|
+
user_fingerprint: 'fp',
|
|
135
|
+
domain_userid: 'duid',
|
|
136
|
+
domain_sessionidx: 'vid',
|
|
137
|
+
network_userid: 'nuid',
|
|
138
|
+
page_url: 'url',
|
|
139
|
+
page_title: 'page',
|
|
140
|
+
page_referrer: 'refr',
|
|
141
|
+
page_urlscheme: extractUrlFrom('protocol', 'url'),
|
|
142
|
+
page_urlhost: extractUrlFrom('hostname', 'url'),
|
|
143
|
+
page_urlport: extractUrlFrom('port', 'url'),
|
|
144
|
+
page_urlpath: extractUrlFrom('pathname', 'url'),
|
|
145
|
+
page_urlquery: extractUrlFrom('search', 'url'),
|
|
146
|
+
page_urlfragment: extractUrlFrom('hash', 'url'),
|
|
147
|
+
refr_urlscheme: extractUrlFrom('protocol', 'refr'),
|
|
148
|
+
refr_urlhost: extractUrlFrom('hostname', 'refr'),
|
|
149
|
+
refr_urlport: extractUrlFrom('port', 'refr'),
|
|
150
|
+
refr_urlpath: extractUrlFrom('pathname', 'refr'),
|
|
151
|
+
refr_urlquery: extractUrlFrom('search', 'refr'),
|
|
152
|
+
refr_urlfragment: extractUrlFrom('hash', 'refr'),
|
|
153
|
+
mkt_medium: extractUrlParamFrom('utm_medium', 'url'),
|
|
154
|
+
mkt_source: extractUrlParamFrom('utm_source', 'url'),
|
|
155
|
+
mkt_term: extractUrlParamFrom('utm_term', 'url'),
|
|
156
|
+
mkt_content: extractUrlParamFrom('utm_content', 'url'),
|
|
157
|
+
mkt_campaign: extractUrlParamFrom('utm_campaign', 'url'),
|
|
158
|
+
se_category: 'se_ca',
|
|
159
|
+
se_action: 'se_ac',
|
|
160
|
+
se_label: 'se_la',
|
|
161
|
+
se_property: 'se_pr',
|
|
162
|
+
se_value: 'se_va',
|
|
163
|
+
tr_orderid: 'tr_id',
|
|
164
|
+
tr_affiliation: 'tr_af',
|
|
165
|
+
tr_total: 'tr_tt',
|
|
166
|
+
tr_tax: 'tr_tx',
|
|
167
|
+
tr_shipping: 'tr_sh',
|
|
168
|
+
tr_city: 'tr_ci',
|
|
169
|
+
tr_state: 'tr_st',
|
|
170
|
+
tr_country: 'tr_co',
|
|
171
|
+
ti_orderid: 'ti_id',
|
|
172
|
+
ti_sku: 'ti_sk',
|
|
173
|
+
ti_name: 'ti_na',
|
|
174
|
+
ti_category: 'ti_ca',
|
|
175
|
+
ti_price: 'ti_pr',
|
|
176
|
+
ti_quantity: 'ti_qu',
|
|
177
|
+
pp_xoffset_min: 'pp_mix',
|
|
178
|
+
pp_xoffset_max: 'pp_max',
|
|
179
|
+
pp_yoffset_min: 'pp_miy',
|
|
180
|
+
pp_yoffset_max: 'pp_may',
|
|
181
|
+
useragent: function (_a) {
|
|
182
|
+
var ua = _a.ua;
|
|
183
|
+
return ua !== null && ua !== void 0 ? ua : navigator.userAgent;
|
|
184
|
+
}, // often elided and extracted from HTTP header
|
|
185
|
+
br_lang: 'lang',
|
|
186
|
+
br_features_pdf: 'f_pdf',
|
|
187
|
+
br_features_flash: 'f_fla',
|
|
188
|
+
br_features_java: 'f_java',
|
|
189
|
+
br_features_director: 'f_dir',
|
|
190
|
+
br_features_quicktime: 'f_qt',
|
|
191
|
+
br_features_realplayer: 'f_realp',
|
|
192
|
+
br_features_windowsmedia: 'f_wma',
|
|
193
|
+
br_features_gears: 'f_gears',
|
|
194
|
+
br_features_silverlight: 'f_ag',
|
|
195
|
+
br_cookies: 'cookie',
|
|
196
|
+
br_colordepth: 'cd',
|
|
197
|
+
br_viewwidth: extractDimensionValueFrom(0, 'vp'),
|
|
198
|
+
br_viewheight: extractDimensionValueFrom(1, 'vp'),
|
|
199
|
+
os_timezone: 'tz',
|
|
200
|
+
dvce_screenwidth: extractDimensionValueFrom(0, 'res'),
|
|
201
|
+
dvce_screenheight: extractDimensionValueFrom(1, 'res'),
|
|
202
|
+
doc_charset: 'cs',
|
|
203
|
+
doc_width: extractDimensionValueFrom(0, 'ds'),
|
|
204
|
+
doc_height: extractDimensionValueFrom(1, 'ds'),
|
|
205
|
+
tr_currency: 'tr_cu',
|
|
206
|
+
ti_currency: 'ti_cu',
|
|
207
|
+
mkt_clickid: extractUrlParamFrom('gclid', 'url'),
|
|
208
|
+
dvce_sent_tstamp: 'stm',
|
|
209
|
+
refr_domain_userid: extractParamPartFrom('_sp', 'url', 0),
|
|
210
|
+
refr_device_tstamp: extractParamPartFrom('_sp', 'url', 1),
|
|
211
|
+
domain_sessionid: 'sid',
|
|
212
|
+
event_vendor: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.vendor; },
|
|
213
|
+
event_name: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.name; },
|
|
214
|
+
event_format: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.format; },
|
|
215
|
+
event_version: function (evt) { var _a; return (_a = getEventSelf(evt)) === null || _a === void 0 ? void 0 : _a.version; },
|
|
216
|
+
true_tstamp: 'ttm',
|
|
217
|
+
};
|
|
218
|
+
var parseJsonPointer = function (pointer) {
|
|
219
|
+
if (!pointer) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (pointer[0] !== '/') {
|
|
223
|
+
pointer = "/".concat(pointer); // for atomic-level fields, this will allow regular names since JSON Pointer isn't super intuitive
|
|
224
|
+
}
|
|
225
|
+
return pointer
|
|
226
|
+
.split('/')
|
|
227
|
+
.map(function (segment) { return segment.replace(/~1/g, '/').replace(/~0/g, '~'); })
|
|
228
|
+
.slice(1);
|
|
229
|
+
};
|
|
230
|
+
var parseEncodedFields = function (evt, fields) {
|
|
231
|
+
return fields.map(function (field) {
|
|
232
|
+
if (objWithKey(evt, field)) {
|
|
233
|
+
var val = evt[field];
|
|
234
|
+
if (typeof val === 'string') {
|
|
235
|
+
try {
|
|
236
|
+
return JSON.parse(val);
|
|
237
|
+
}
|
|
238
|
+
catch (_) {
|
|
239
|
+
try {
|
|
240
|
+
return JSON.parse(atob(val.replace(/[_-]/g, function (m) { return (m == '-' ? '+' : '/'); })));
|
|
241
|
+
}
|
|
242
|
+
catch (_) { }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
/**
|
|
249
|
+
* Build a lookup map of `vendor: name[]` for entities to lookup more easily
|
|
250
|
+
*/
|
|
251
|
+
var summarizeEntities = function (evt) {
|
|
252
|
+
var fields = parseEncodedFields(evt, ['co', 'cx']);
|
|
253
|
+
return fields.reduce(function (acc, ctx) {
|
|
254
|
+
if (objWithKey(ctx, 'data') && Array.isArray(ctx.data)) {
|
|
255
|
+
ctx.data.forEach(function (entity) {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
if (objWithKey(entity, 'schema') && objWithKey(entity, 'data')) {
|
|
258
|
+
if (typeof entity.schema === 'string' && entity.schema.indexOf('iglu:') === 0) {
|
|
259
|
+
var parts = entity.schema.slice(5).split('/');
|
|
260
|
+
if (parts.length !== 4) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
var vendor = (acc[parts[0]] = (_a = acc[parts[0]]) !== null && _a !== void 0 ? _a : {});
|
|
264
|
+
vendor[parts[1]] = (_b = vendor[parts[1]]) !== null && _b !== void 0 ? _b : [];
|
|
265
|
+
vendor[parts[1]].push(entity.data);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
return acc;
|
|
271
|
+
}, {});
|
|
272
|
+
};
|
|
273
|
+
/**
|
|
274
|
+
* Build a lookup map of `vendor: name` for SDE payloads to lookup more easily
|
|
275
|
+
*/
|
|
276
|
+
var summarizeEvent = function (evt) {
|
|
277
|
+
var fields = parseEncodedFields(evt, ['ue_pr', 'ue_px']);
|
|
278
|
+
return fields.reduce(function (acc, ctx) {
|
|
279
|
+
var _a;
|
|
280
|
+
if (objWithKey(ctx, 'data')) {
|
|
281
|
+
if (objWithKey(ctx.data, 'schema') && objWithKey(ctx.data, 'data')) {
|
|
282
|
+
var event_1 = ctx.data;
|
|
283
|
+
if (typeof event_1.schema === 'string' && event_1.schema.indexOf('iglu:') === 0) {
|
|
284
|
+
var parts = event_1.schema.slice(5).split('/');
|
|
285
|
+
if (parts.length !== 4) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
var vendor = (acc[parts[0]] = (_a = acc[parts[0]]) !== null && _a !== void 0 ? _a : {});
|
|
289
|
+
vendor[parts[1]] = event_1.data;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return acc;
|
|
294
|
+
}, {});
|
|
295
|
+
};
|
|
296
|
+
var getEventSelf = function (evt) {
|
|
297
|
+
var _a;
|
|
298
|
+
var schema = undefined;
|
|
299
|
+
if (!objWithKey(evt, 'e') || typeof evt.e !== 'string' || !(evt.e in EVENT_SCHEMAS)) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (evt.e === 'ue') {
|
|
303
|
+
var fields = parseEncodedFields(evt, ['ue_pr', 'ue_px']);
|
|
304
|
+
var ue = (_a = fields[0]) !== null && _a !== void 0 ? _a : fields[1];
|
|
305
|
+
if (objWithKey(ue, 'data') && objWithKey(ue.data, 'schema') && typeof ue.data.schema === 'string') {
|
|
306
|
+
schema = ue.data.schema;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
schema = EVENT_SCHEMAS[evt.e];
|
|
311
|
+
}
|
|
312
|
+
if (typeof schema === 'string' && schema.indexOf('iglu:') === 0) {
|
|
313
|
+
var _b = schema.slice(5).split('/'), vendor = _b[0], name_1 = _b[1], format = _b[2], version = _b[3];
|
|
314
|
+
return { vendor: vendor, name: name_1, format: format, version: version };
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
var derefJsonPointer = function (pointerString, obj) {
|
|
318
|
+
var _a, _b;
|
|
319
|
+
var pointer = parseJsonPointer(pointerString);
|
|
320
|
+
if (pointer == null) {
|
|
321
|
+
return obj; // return whole document
|
|
322
|
+
}
|
|
323
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
324
|
+
return; // no segments will work
|
|
325
|
+
}
|
|
326
|
+
// map enriched fieldname <> TP field
|
|
327
|
+
if (pointer[0] in selectorMap) {
|
|
328
|
+
var mappedSelector = selectorMap[pointer[0]];
|
|
329
|
+
if (typeof mappedSelector === 'string' && objWithKey(obj, mappedSelector)) {
|
|
330
|
+
return obj[mappedSelector];
|
|
331
|
+
}
|
|
332
|
+
else if (typeof mappedSelector === 'function') {
|
|
333
|
+
return mappedSelector(obj);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// descend into nested SDJ payloads
|
|
337
|
+
var cursor = obj;
|
|
338
|
+
if (ENTITY_ALIASES.indexOf(pointer[0]) !== -1) {
|
|
339
|
+
cursor = (_a = {}, _a[pointer[0]] = summarizeEntities(cursor), _a);
|
|
340
|
+
}
|
|
341
|
+
if (EVENT_ALIASES.indexOf(pointer[0]) !== -1) {
|
|
342
|
+
cursor = (_b = {}, _b[pointer[0]] = summarizeEvent(cursor), _b);
|
|
343
|
+
}
|
|
344
|
+
for (var _i = 0, pointer_1 = pointer; _i < pointer_1.length; _i++) {
|
|
345
|
+
var segment = pointer_1[_i];
|
|
346
|
+
if (objWithKey(cursor, segment)) {
|
|
347
|
+
cursor = cursor[segment];
|
|
348
|
+
}
|
|
349
|
+
else if (Array.isArray(cursor) && cursor.length === 1 && objWithKey(cursor[0], segment)) {
|
|
350
|
+
cursor = cursor[0][segment];
|
|
351
|
+
}
|
|
352
|
+
else
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
return cursor;
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Given a list of rules to extract entity IDs and a Snowplow event payload, return any extracted ID results.
|
|
359
|
+
* @param targets Map of entity names to rule definitions for how to find ID values
|
|
360
|
+
* @param pb Snowplow event
|
|
361
|
+
* @returns Resulting IDs extracted from the event
|
|
362
|
+
*/
|
|
363
|
+
function extractEntityValues(targets, pb) {
|
|
364
|
+
var extracted = {};
|
|
365
|
+
Object.entries(targets).forEach(function (_a) {
|
|
366
|
+
var entityName = _a[0], pointers = _a[1];
|
|
367
|
+
pointers = Array.isArray(pointers) ? pointers : [pointers];
|
|
368
|
+
for (var _i = 0, pointers_1 = pointers; _i < pointers_1.length; _i++) {
|
|
369
|
+
var pointer = pointers_1[_i];
|
|
370
|
+
if (pointer === '') {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
var candidateId = derefJsonPointer(pointer, pb);
|
|
374
|
+
if (candidateId != null) {
|
|
375
|
+
extracted[entityName] = String(candidateId);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
return extracted;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
var DEFAULT_PULL_API_PATH = '/api/v1/interventions';
|
|
384
|
+
var DEFAULT_ENTITY_TARGETS = {
|
|
385
|
+
domain_userid: '/domain_userid',
|
|
386
|
+
domain_sessionid: '/domain_sessionid',
|
|
387
|
+
//pageview_id: '/co/com.snowplowanalytics.snowplow/web_page/id', // not a default entity seed
|
|
388
|
+
//tab_id: '/co/com.snowplowanalytics.snowplow/browser_context/tabId', // not a default entity seed
|
|
389
|
+
};
|
|
390
|
+
var DEFAULT_CONNECTION_TIMEOUT_MS = 2500;
|
|
391
|
+
/**
|
|
392
|
+
* Default `Fetcher` implementation; uses SSEs, auto updates based on observed entity IDs defined with JSON Pointers
|
|
393
|
+
*/
|
|
394
|
+
var InterventionFetcher = /** @class */ (function () {
|
|
395
|
+
function InterventionFetcher(tracker, dispatch) {
|
|
396
|
+
this.tracker = tracker;
|
|
397
|
+
this.dispatch = dispatch;
|
|
398
|
+
this.entityValues = {};
|
|
399
|
+
this.aborter = new AbortController();
|
|
400
|
+
this.timeoutMs = DEFAULT_CONNECTION_TIMEOUT_MS;
|
|
401
|
+
this.newEndpoint = false;
|
|
402
|
+
this.currentParams = '';
|
|
403
|
+
this.entitySelectors = DEFAULT_ENTITY_TARGETS;
|
|
404
|
+
// reasonable defaults even without any events observed
|
|
405
|
+
var info = tracker.getDomainUserInfo();
|
|
406
|
+
this.update({
|
|
407
|
+
duid: info[1] || undefined,
|
|
408
|
+
sid: info[6] || undefined,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
InterventionFetcher.prototype.configure = function (_a) {
|
|
412
|
+
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;
|
|
413
|
+
this.entitySelectors = Object.assign({}, this.entitySelectors, entityTargets);
|
|
414
|
+
this.timeoutMs = connectionTimeoutMs;
|
|
415
|
+
var prevEndpoint = this.endpoint;
|
|
416
|
+
this.endpoint = "".concat(/\/\//.test(endpoint) ? endpoint : 'https://' + endpoint).concat(apiPath);
|
|
417
|
+
this.newEndpoint = prevEndpoint != this.endpoint;
|
|
418
|
+
this.update(undefined, entityIds);
|
|
419
|
+
};
|
|
420
|
+
InterventionFetcher.prototype.update = function (payload, explicitEntities) {
|
|
421
|
+
if (explicitEntities === void 0) { explicitEntities = {}; }
|
|
422
|
+
if (payload) {
|
|
423
|
+
Object.assign(this.entityValues, extractEntityValues(this.entitySelectors, payload));
|
|
424
|
+
}
|
|
425
|
+
Object.assign(this.entityValues, explicitEntities);
|
|
426
|
+
var newParams = new URLSearchParams(this.entityValues).toString();
|
|
427
|
+
if (this.endpoint && (newParams != this.currentParams || this.newEndpoint)) {
|
|
428
|
+
logger(LogLevel.DEBUG, this.tracker.id, 'Entity IDs updated', this.entityValues);
|
|
429
|
+
this.currentParams = newParams;
|
|
430
|
+
this.requestInterventions();
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
/**
|
|
434
|
+
* Set up a new SSE connection and start dispatching any received events
|
|
435
|
+
*/
|
|
436
|
+
InterventionFetcher.prototype.requestInterventions = function () {
|
|
437
|
+
var _this = this;
|
|
438
|
+
this.aborter.abort(); // abort any previous connection
|
|
439
|
+
var aborter = (this.aborter = new AbortController()); // new controller to reset aborted state
|
|
440
|
+
if (!this.endpoint) {
|
|
441
|
+
logger(LogLevel.ERROR, this.tracker.id, 'Requested interventions from undefined endpoint');
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
var url = "".concat(this.endpoint, "?").concat(this.currentParams);
|
|
445
|
+
var stream = new EventSource(url);
|
|
446
|
+
aborter.signal.addEventListener('abort', stream.close.bind(stream), {
|
|
447
|
+
once: true,
|
|
448
|
+
});
|
|
449
|
+
var timeout = setTimeout(aborter.abort.bind(aborter), this.timeoutMs);
|
|
450
|
+
stream.addEventListener('open', function () { return clearTimeout(timeout); }, {
|
|
451
|
+
once: true,
|
|
452
|
+
});
|
|
453
|
+
stream.addEventListener('error', function (ev) {
|
|
454
|
+
return logger(LogLevel.ERROR, _this.tracker.id, 'Error fetching interventions:', ev);
|
|
455
|
+
});
|
|
456
|
+
stream.addEventListener('message', function (ev) {
|
|
457
|
+
_this.dispatch(JSON.parse(ev.data), _this.tracker);
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
/**
|
|
461
|
+
* Factory to match signature of public interface and create the default fetcher instance
|
|
462
|
+
* @param tracker Tracker activating the plugin
|
|
463
|
+
* @param dispatch Callback to pass interventions to
|
|
464
|
+
* @returns `InterventionFetcher` instance
|
|
465
|
+
*/
|
|
466
|
+
InterventionFetcher.create = function (tracker, dispatch) { return new InterventionFetcher(tracker, dispatch); };
|
|
467
|
+
return InterventionFetcher;
|
|
468
|
+
}());
|
|
469
|
+
|
|
470
|
+
var Events = {
|
|
471
|
+
INTERVENTION_RECEIVED: 'iglu:com.snowplowanalytics.signals/intervention_receive/jsonschema/1-0-0',
|
|
472
|
+
INTERVENTION_HANDLE: 'iglu:com.snowplowanalytics.signals/intervention_handle/jsonschema/1-0-0',
|
|
473
|
+
INTERVENTION_HANDLE_ERROR: 'iglu:com.snowplowanalytics.signals/intervention_handle_error/jsonschema/1-0-0',
|
|
474
|
+
};
|
|
475
|
+
var Entities = {
|
|
476
|
+
INTERVENTION: 'iglu:com.snowplowanalytics.signals/intervention_instance/jsonschema/1-0-0',
|
|
477
|
+
};
|
|
478
|
+
var MEASUREMENT_EVENTS = {
|
|
479
|
+
delivery: Events.INTERVENTION_RECEIVED,
|
|
480
|
+
dispatch_accept: Events.INTERVENTION_HANDLE,
|
|
481
|
+
dispatch_error: Events.INTERVENTION_HANDLE_ERROR,
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
var DEFAULT_MEASUREMENT_SETTINGS = {
|
|
485
|
+
delivery: true,
|
|
486
|
+
dispatch_accept: true,
|
|
487
|
+
dispatch_error: true,
|
|
488
|
+
};
|
|
489
|
+
/* Per-tracker state */
|
|
490
|
+
var instances = {}; // entities will likely vary by tracker, so each set will need its own fetcher
|
|
491
|
+
var handlerRegistry = {}; // handlers can be added/removed for individual trackers
|
|
492
|
+
var measurementSettings = {}; // currently global and only specified per-plugin instance but may be required in future
|
|
493
|
+
/**
|
|
494
|
+
* Create an instance of the Signals Interventions plugin.
|
|
495
|
+
* @param configuration Configuration for the plugin
|
|
496
|
+
* @returns Configured plugin instance
|
|
497
|
+
*/
|
|
498
|
+
function SignalsInterventionsPlugin(_a) {
|
|
499
|
+
var _b = _a === void 0 ? {
|
|
500
|
+
measurement: DEFAULT_MEASUREMENT_SETTINGS,
|
|
501
|
+
handlers: {},
|
|
502
|
+
} : _a, fetcher = _b.fetcher, _c = _b.measurement, measurement = _c === void 0 ? DEFAULT_MEASUREMENT_SETTINGS : _c, _d = _b.handlers, handlers = _d === void 0 ? {} : _d;
|
|
503
|
+
return {
|
|
504
|
+
activateBrowserPlugin: function (tracker) {
|
|
505
|
+
var _a;
|
|
506
|
+
logger(LogLevel.INFO, tracker.id, 'Activating plugin for tracker');
|
|
507
|
+
instances[tracker.id] = fetcher ? fetcher(tracker, dispatch) : InterventionFetcher.create(tracker, dispatch);
|
|
508
|
+
measurementSettings[tracker.id] = Object.assign({}, DEFAULT_MEASUREMENT_SETTINGS, measurement);
|
|
509
|
+
handlerRegistry[tracker.id] = Object.assign((_a = handlerRegistry[tracker.id]) !== null && _a !== void 0 ? _a : {}, handlers);
|
|
510
|
+
},
|
|
511
|
+
afterTrack: function (payload) {
|
|
512
|
+
var trackerName = payload['tna'];
|
|
513
|
+
if (typeof trackerName === 'string' && trackerName in instances) {
|
|
514
|
+
instances[trackerName].update(payload);
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
logger: function (LOG) {
|
|
518
|
+
setLogger(SignalsInterventionsPlugin.name, LOG);
|
|
519
|
+
},
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Tracks an event upon receipt/handling of an intervention unless configured not to.
|
|
524
|
+
* @param settings Measurement settings defining whether an event should fire or not, and configuring custom context
|
|
525
|
+
* @param tracker The tracker to track the event with
|
|
526
|
+
* @param measurement The type of event to track
|
|
527
|
+
* @param intervention The intervention payload in question
|
|
528
|
+
* @param payload An event-specific payload, if required according to `measurement`
|
|
529
|
+
*/
|
|
530
|
+
var measure = function (settings, tracker, measurement, intervention, payload) {
|
|
531
|
+
var _a;
|
|
532
|
+
var filter = settings[measurement];
|
|
533
|
+
if (filter && (typeof filter !== 'function' || filter(intervention))) {
|
|
534
|
+
var entities = [
|
|
535
|
+
{
|
|
536
|
+
schema: Entities.INTERVENTION,
|
|
537
|
+
data: {
|
|
538
|
+
intervention_id: intervention.intervention_id,
|
|
539
|
+
name: intervention.name,
|
|
540
|
+
version: intervention.version,
|
|
541
|
+
entity: intervention.target_entity,
|
|
542
|
+
attributes: intervention.attributes,
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
];
|
|
546
|
+
tracker.core.track(buildSelfDescribingEvent({
|
|
547
|
+
event: {
|
|
548
|
+
schema: MEASUREMENT_EVENTS[measurement],
|
|
549
|
+
data: payload,
|
|
550
|
+
},
|
|
551
|
+
}), resolveDynamicContext(__spreadArray(__spreadArray([], entities, true), ((_a = settings.context) !== null && _a !== void 0 ? _a : []), true), measurement, intervention, payload));
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
/**
|
|
555
|
+
* Called by the `Fetcher` to track receipt of an Intervention and distribute to any handlers
|
|
556
|
+
* @param intervention The intervention payload that was fetched
|
|
557
|
+
* @param tracker The tracker associated with the plugin
|
|
558
|
+
*/
|
|
559
|
+
var dispatch = function (intervention, tracker) {
|
|
560
|
+
var _a, _b;
|
|
561
|
+
var measurement = (_a = measurementSettings[tracker.id]) !== null && _a !== void 0 ? _a : DEFAULT_MEASUREMENT_SETTINGS;
|
|
562
|
+
var handlers = (_b = handlerRegistry[tracker.id]) !== null && _b !== void 0 ? _b : {};
|
|
563
|
+
var handlerIds = Object.keys(handlers);
|
|
564
|
+
if (!handlerIds.length) {
|
|
565
|
+
return logger(LogLevel.WARN, tracker.id, 'No handlers registered for intervention', intervention);
|
|
566
|
+
}
|
|
567
|
+
logger(LogLevel.INFO, tracker.id, 'Attempting dispatch for intervention', intervention, handlerIds);
|
|
568
|
+
measure(measurement, tracker, 'delivery', intervention, {
|
|
569
|
+
handlers: handlerIds,
|
|
570
|
+
});
|
|
571
|
+
for (var _i = 0, _c = Object.entries(handlers); _i < _c.length; _i++) {
|
|
572
|
+
var _d = _c[_i], handlerId = _d[0], handler = _d[1];
|
|
573
|
+
setTimeout(function (handlerId, handler, intervention, tracker) {
|
|
574
|
+
var success = function () {
|
|
575
|
+
logger(LogLevel.INFO, tracker.id, 'Intervention handled', handlerId, intervention);
|
|
576
|
+
measure(measurement, tracker, 'dispatch_accept', intervention, {
|
|
577
|
+
handler: handlerId,
|
|
578
|
+
});
|
|
579
|
+
};
|
|
580
|
+
var failure = function (err) {
|
|
581
|
+
logger(LogLevel.ERROR, tracker.id, 'Handler failed processing intervention', err, handlerId, intervention);
|
|
582
|
+
measure(measurement, tracker, 'dispatch_error', intervention, {
|
|
583
|
+
handler: handlerId,
|
|
584
|
+
error: err ? String(err) : undefined,
|
|
585
|
+
});
|
|
586
|
+
};
|
|
587
|
+
try {
|
|
588
|
+
var result = handler(intervention, tracker);
|
|
589
|
+
if (result instanceof Promise) {
|
|
590
|
+
result.then(success, failure);
|
|
591
|
+
}
|
|
592
|
+
else if (objWithKey(result, 'then') && typeof result.then === 'function') {
|
|
593
|
+
result.then(success, failure);
|
|
594
|
+
}
|
|
595
|
+
else
|
|
596
|
+
success();
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
failure(e);
|
|
600
|
+
}
|
|
601
|
+
}, 0, handlerId, handler, intervention, tracker);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* Configure the endpoint information for the plugin's `Fetcher` to subscribe to events from
|
|
606
|
+
* @param config Configuration about the endpoint and entity IDs/definitions to configure
|
|
607
|
+
* @param trackers List of tracker IDs that have activated the plugin to configure a `Fetcher` for
|
|
608
|
+
*/
|
|
609
|
+
function subscribeToInterventions(config, trackers) {
|
|
610
|
+
if (trackers === void 0) { trackers = Object.keys(instances); }
|
|
611
|
+
for (var _i = 0, trackers_1 = trackers; _i < trackers_1.length; _i++) {
|
|
612
|
+
var trackerId = trackers_1[_i];
|
|
613
|
+
if (trackerId in instances) {
|
|
614
|
+
instances[trackerId].configure(config);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Start calling the given handlers when new interventions are received
|
|
620
|
+
* @param handlers Map of handler IDs to handler functions to call with new interventions
|
|
621
|
+
* @param trackers List of tracker IDs that have activated the plugin to add these handlers to
|
|
622
|
+
*/
|
|
623
|
+
function addInterventionHandlers(handlers, trackers) {
|
|
624
|
+
var _a;
|
|
625
|
+
if (trackers === void 0) { trackers = Object.keys(instances); }
|
|
626
|
+
for (var _i = 0, trackers_2 = trackers; _i < trackers_2.length; _i++) {
|
|
627
|
+
var trackerId = trackers_2[_i];
|
|
628
|
+
handlerRegistry[trackerId] = Object.assign((_a = handlerRegistry[trackerId]) !== null && _a !== void 0 ? _a : {}, handlers);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Stop calling handlers with the given IDs with new interventions
|
|
633
|
+
* @param handlerIds One or more handler IDs to remove
|
|
634
|
+
* @param trackers List of trackers to remove the handlers for; defaults to all trackers that have activated the plugin.
|
|
635
|
+
*/
|
|
636
|
+
function removeInterventionHandlers(handlerIds, trackers) {
|
|
637
|
+
if (trackers === void 0) { trackers = Object.keys(instances); }
|
|
638
|
+
var toRemove = Array.isArray(handlerIds) ? handlerIds : [handlerIds];
|
|
639
|
+
for (var _i = 0, toRemove_1 = toRemove; _i < toRemove_1.length; _i++) {
|
|
640
|
+
var handlerId = toRemove_1[_i];
|
|
641
|
+
for (var _a = 0, trackers_3 = trackers; _a < trackers_3.length; _a++) {
|
|
642
|
+
var trackerId = trackers_3[_a];
|
|
643
|
+
delete handlerRegistry[trackerId][handlerId];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
export { SignalsInterventionsPlugin, addInterventionHandlers, removeInterventionHandlers, subscribeToInterventions };
|
|
649
|
+
//# sourceMappingURL=index.module.js.map
|