@miso.ai/client-sdk-core 1.5.1-beta.0 → 1.6.0-beta.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/dist/cjs/api/base.js +1 -15
- package/dist/cjs/api/helpers.js +2 -31
- package/dist/cjs/api/index.js +0 -9
- package/dist/cjs/api/interactions.js +7 -24
- package/dist/cjs/api/recommendation.js +0 -11
- package/dist/cjs/api/search.js +0 -8
- package/dist/cjs/classes.js +0 -9
- package/dist/cjs/client.js +0 -21
- package/dist/cjs/cmd.js +4 -8
- package/dist/cjs/context.js +0 -4
- package/dist/cjs/index.js +4 -8
- package/dist/cjs/plugin/anonymous.js +0 -4
- package/dist/cjs/plugin/auto-events.js +0 -28
- package/dist/cjs/plugin/index.js +0 -12
- package/dist/cjs/plugin/page-info.js +0 -12
- package/dist/cjs/plugin/plugin-root.js +7 -55
- package/dist/cjs/plugin/user.js +0 -26
- package/dist/cjs/root.js +4 -13
- package/dist/cjs/utils.js +7 -37
- package/dist/cjs/version.js +1 -1
- package/dist/es/api/base.js +1 -12
- package/dist/es/api/helpers.js +2 -27
- package/dist/es/api/index.js +0 -1
- package/dist/es/api/interactions.js +7 -20
- package/dist/es/api/recommendation.js +0 -7
- package/dist/es/api/search.js +0 -4
- package/dist/es/client.js +0 -13
- package/dist/es/cmd.js +4 -7
- package/dist/es/context.js +0 -1
- package/dist/es/index.js +4 -4
- package/dist/es/plugin/anonymous.js +0 -1
- package/dist/es/plugin/auto-events.js +0 -24
- package/dist/es/plugin/index.js +1 -2
- package/dist/es/plugin/page-info.js +0 -8
- package/dist/es/plugin/plugin-root.js +7 -48
- package/dist/es/plugin/user.js +0 -22
- package/dist/es/root.js +5 -7
- package/dist/es/utils.js +1 -34
- package/dist/es/version.js +1 -1
- package/package.json +2 -2
- package/dist/cjs/plugin/units/constants.js +0 -10
- package/dist/cjs/plugin/units/index.js +0 -41
- package/dist/cjs/plugin/units/tracker.js +0 -365
- package/dist/cjs/plugin/units/unit.js +0 -63
- package/dist/cjs/plugin/units/units.js +0 -70
- package/dist/es/plugin/units/constants.js +0 -2
- package/dist/es/plugin/units/index.js +0 -27
- package/dist/es/plugin/units/tracker.js +0 -353
- package/dist/es/plugin/units/unit.js +0 -50
- package/dist/es/plugin/units/units.js +0 -56
|
@@ -1,353 +0,0 @@
|
|
|
1
|
-
const _excluded = ["impression", "viewable", "click"];
|
|
2
|
-
|
|
3
|
-
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
4
|
-
|
|
5
|
-
import { isElement, trimObj, asArray } from '@miso.ai/commons';
|
|
6
|
-
import { ATTR_PRODUCT_ID } from './constants';
|
|
7
|
-
import { viewable as whenViewable } from '../../utils';
|
|
8
|
-
|
|
9
|
-
function mergeOptions(def, opt) {
|
|
10
|
-
// if opt is falsy then return false, merge otherwise
|
|
11
|
-
return !!opt && Object.assign({}, def, opt);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const DEFAULT_TRACKING_OPTIONS = {
|
|
15
|
-
watchElements: false,
|
|
16
|
-
viewable: {
|
|
17
|
-
area: 0.5,
|
|
18
|
-
duration: 1000
|
|
19
|
-
},
|
|
20
|
-
click: {
|
|
21
|
-
lenient: false
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
const STATE = {
|
|
25
|
-
INITIAL: 0,
|
|
26
|
-
TRACKING: 1,
|
|
27
|
-
TRIGGERED: 2
|
|
28
|
-
};
|
|
29
|
-
export default class Tracker {
|
|
30
|
-
constructor(unit) {
|
|
31
|
-
this._unit = unit;
|
|
32
|
-
this._eventStates = new Map();
|
|
33
|
-
this._sentEvents = {
|
|
34
|
-
impression: new Set(),
|
|
35
|
-
viewable: new Set(),
|
|
36
|
-
click: new Set()
|
|
37
|
-
};
|
|
38
|
-
this._handleClick = this._handleClick.bind(this);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
start(options) {
|
|
42
|
-
const root = this._unit.element;
|
|
43
|
-
|
|
44
|
-
if (!root) {
|
|
45
|
-
throw new Error(`The unit has no bound element. Call unit.bind(element) to associated the unit to an element.`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (this._options) {
|
|
49
|
-
// throw, for the options could be different and this won't be idempotent
|
|
50
|
-
throw new Error(`Already tracking events.`);
|
|
51
|
-
} // settle tracking options
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this._options = this._normalizeOptions(options);
|
|
55
|
-
|
|
56
|
-
this._watchElements();
|
|
57
|
-
|
|
58
|
-
this._trackClicks();
|
|
59
|
-
|
|
60
|
-
this.sync();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
sync() {
|
|
64
|
-
const {
|
|
65
|
-
impression,
|
|
66
|
-
viewable
|
|
67
|
-
} = this._options;
|
|
68
|
-
|
|
69
|
-
if (!impression && !viewable) {
|
|
70
|
-
return;
|
|
71
|
-
} // scan all descendants for product item elements
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const elements = this._unit.element.querySelectorAll(`[${ATTR_PRODUCT_ID}]`);
|
|
75
|
-
|
|
76
|
-
this._trackImpressions(elements);
|
|
77
|
-
|
|
78
|
-
this._trackViewables(elements);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
interceptEventPayload(callback) {
|
|
82
|
-
this._eventPayloadPass = callback;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
impression(productIds) {
|
|
86
|
-
this._sendEvent('impression', productIds, true);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
viewable(productIds) {
|
|
90
|
-
this._sendEvent('viewable', productIds, true);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
click(productIds) {
|
|
94
|
-
this._sendEvent('click', productIds, true);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
_normalizeOptions() {
|
|
98
|
-
let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
99
|
-
|
|
100
|
-
let {
|
|
101
|
-
impression = {},
|
|
102
|
-
viewable = {},
|
|
103
|
-
click = {}
|
|
104
|
-
} = _ref,
|
|
105
|
-
options = _objectWithoutPropertiesLoose(_ref, _excluded);
|
|
106
|
-
|
|
107
|
-
return trimObj(Object.assign({}, DEFAULT_TRACKING_OPTIONS, options, {
|
|
108
|
-
impression: mergeOptions(DEFAULT_TRACKING_OPTIONS.impression, impression),
|
|
109
|
-
viewable: mergeOptions(DEFAULT_TRACKING_OPTIONS.viewable, viewable),
|
|
110
|
-
click: mergeOptions(DEFAULT_TRACKING_OPTIONS.click, click)
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
_watchElements() {
|
|
115
|
-
const {
|
|
116
|
-
watchElements,
|
|
117
|
-
impression,
|
|
118
|
-
viewable
|
|
119
|
-
} = this._options;
|
|
120
|
-
|
|
121
|
-
if (!watchElements || !impression && !viewable) {
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const observer = this._mutationObserver = new MutationObserver(mutations => {
|
|
126
|
-
let hasAddedElements = false;
|
|
127
|
-
let removedElements = [];
|
|
128
|
-
|
|
129
|
-
for (const {
|
|
130
|
-
addedNodes,
|
|
131
|
-
removedNodes
|
|
132
|
-
} of mutations) {
|
|
133
|
-
hasAddedElements = hasAddedElements || addedNodes.length > 0;
|
|
134
|
-
addedElements = [...addedElements, ...addedNodes];
|
|
135
|
-
removedElements = [...removedElements, ...removedNodes];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (hasAddedElements) {
|
|
139
|
-
this.sync();
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this._untrackViewables(removedElements);
|
|
143
|
-
});
|
|
144
|
-
observer.observe(this._element, {
|
|
145
|
-
childList: true,
|
|
146
|
-
subtree: true
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
_unwatchElements() {
|
|
151
|
-
this._mutationObserver && this._mutationObserver.disconnect();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
_trackImpressions(elements) {
|
|
155
|
-
const options = this._options.impression;
|
|
156
|
-
|
|
157
|
-
if (!options) {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const productIds = [];
|
|
162
|
-
|
|
163
|
-
for (const element of elements) {
|
|
164
|
-
const productId = element.getAttribute(ATTR_PRODUCT_ID);
|
|
165
|
-
|
|
166
|
-
if (!productId) {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const state = this._getEventState(productId);
|
|
171
|
-
|
|
172
|
-
if (state.impression !== STATE.TRIGGERED) {
|
|
173
|
-
state.impression = STATE.TRIGGERED;
|
|
174
|
-
productIds.push(productId);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
this._sendEvent('impression', productIds);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
_trackViewables(elements) {
|
|
182
|
-
const options = this._options.viewable;
|
|
183
|
-
|
|
184
|
-
if (!options) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
for (const element of elements) {
|
|
189
|
-
this._trackViewableOnElement(element);
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async _trackViewableOnElement(element) {
|
|
194
|
-
if (!isElement(element)) {
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const productId = element.getAttribute(ATTR_PRODUCT_ID);
|
|
199
|
-
|
|
200
|
-
if (!productId) {
|
|
201
|
-
return;
|
|
202
|
-
} // TODO: when element is replaced without proper unload...
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const state = this._getEventState(productId);
|
|
206
|
-
|
|
207
|
-
if (state.viewable !== STATE.INITIAL) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
state.viewable = STATE.TRACKING;
|
|
212
|
-
const {
|
|
213
|
-
area,
|
|
214
|
-
duration
|
|
215
|
-
} = this._options.viewable;
|
|
216
|
-
await whenViewable(element, {
|
|
217
|
-
area,
|
|
218
|
-
duration
|
|
219
|
-
});
|
|
220
|
-
state.viewable = STATE.TRIGGERED;
|
|
221
|
-
|
|
222
|
-
this._sendEvent('viewable', [productId]);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
_untrackAllViewables() {// TODO
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
_untrackViewables(elements) {// we need to scan through all descendants to scavenge detached product items
|
|
229
|
-
// TODO
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
_trackClicks() {
|
|
233
|
-
this._unit.element.addEventListener('click', this._handleClick);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
_untrackClicks() {
|
|
237
|
-
this._unit.element.removeEventListener('click', this._handleClick);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
_destroy() {
|
|
241
|
-
this._unwatchElements();
|
|
242
|
-
|
|
243
|
-
this._untrackClicks();
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
_getEventState(productId) {
|
|
247
|
-
let state = this._eventStates.get(productId);
|
|
248
|
-
|
|
249
|
-
if (!state) {
|
|
250
|
-
this._eventStates.set(productId, state = {
|
|
251
|
-
impression: STATE.INITIAL,
|
|
252
|
-
viewable: STATE.INITIAL,
|
|
253
|
-
click: STATE.INITIAL
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return state;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
_handleClick(event) {
|
|
261
|
-
const options = this._options && this._options.click;
|
|
262
|
-
|
|
263
|
-
if (!options) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
let productId;
|
|
268
|
-
|
|
269
|
-
for (let element = event.target; !productId && element && element !== document; element = element.parentElement) {
|
|
270
|
-
productId = element.getAttribute(ATTR_PRODUCT_ID);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (!productId) {
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const state = this._getEventState(productId);
|
|
278
|
-
|
|
279
|
-
if (state.click === STATE.TRIGGERED) {
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (!options.lenient) {// only left click counts
|
|
284
|
-
// event isPreventDefault
|
|
285
|
-
// go through a real link
|
|
286
|
-
// TODO
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
state.click = STATE.TRIGGERED;
|
|
290
|
-
|
|
291
|
-
this._sendEvent('click', [productId]);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
_sendEvent(type, productIds, manual) {
|
|
295
|
-
if (type !== 'impression' && type !== 'viewable' && type !== 'click') {
|
|
296
|
-
throw new Error(`Unrecognized event type: ${type}`);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
productIds = asArray(productIds); // dedupe
|
|
300
|
-
|
|
301
|
-
const sentEvents = this._sentEvents[type];
|
|
302
|
-
productIds = productIds.filter(id => {
|
|
303
|
-
const sent = sentEvents.has(id);
|
|
304
|
-
|
|
305
|
-
if (!sent) {
|
|
306
|
-
sentEvents.add(id);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return !sent;
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
if (productIds.length === 0) {
|
|
313
|
-
return;
|
|
314
|
-
} // TODO: emit
|
|
315
|
-
// we want to queue the event for a while so we can merge them and save requests
|
|
316
|
-
// TODO
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const client = this._unit._context._client;
|
|
320
|
-
|
|
321
|
-
const payloads = this._buildPayloads(type, productIds, manual);
|
|
322
|
-
|
|
323
|
-
if (payloads.length > 0) {
|
|
324
|
-
client.api.interactions.upload(payloads);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
_buildPayloads(type, productIds, manual) {
|
|
329
|
-
const {
|
|
330
|
-
id,
|
|
331
|
-
uuid
|
|
332
|
-
} = this._unit;
|
|
333
|
-
let payload = {
|
|
334
|
-
type: type === 'viewable' ? 'viewable_impression' : type,
|
|
335
|
-
product_ids: productIds,
|
|
336
|
-
context: {
|
|
337
|
-
custom_context: {
|
|
338
|
-
unit_id: id,
|
|
339
|
-
unit_instance_uuid: uuid,
|
|
340
|
-
trigger: manual ? 'manual' : 'auto'
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
if (this._eventPayloadPass) {
|
|
346
|
-
// TODO: handle error
|
|
347
|
-
payload = this._eventPayloadPass(payload);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return asArray(payload);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { uuidv4 } from '@miso.ai/commons';
|
|
2
|
-
import Tracker from './tracker';
|
|
3
|
-
export default class Unit {
|
|
4
|
-
constructor(context, id) {
|
|
5
|
-
this._context = context;
|
|
6
|
-
Object.defineProperties(this, {
|
|
7
|
-
uuid: {
|
|
8
|
-
value: uuidv4()
|
|
9
|
-
},
|
|
10
|
-
id: {
|
|
11
|
-
value: id
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
context._units.set(id, this);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get element() {
|
|
19
|
-
return this._element;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get tracker() {
|
|
23
|
-
return this._tracker || (this._tracker = new Tracker(this));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
bind(element) {
|
|
27
|
-
if (this._element === element) {
|
|
28
|
-
return;
|
|
29
|
-
} else if (this._element) {
|
|
30
|
-
throw new Error(`Element has already been set.`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
this._element = element;
|
|
34
|
-
|
|
35
|
-
this._context._e2u.set(element, this);
|
|
36
|
-
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
destroy() {
|
|
41
|
-
this._tracker && this._tracker._destroy();
|
|
42
|
-
|
|
43
|
-
this._context._e2u.delete(this._element);
|
|
44
|
-
|
|
45
|
-
this._context._units.delete(this.id);
|
|
46
|
-
|
|
47
|
-
this._element = undefined;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { isElement, delegateGetters } from '@miso.ai/commons';
|
|
2
|
-
import { ATTR_UNIT_ID } from './constants';
|
|
3
|
-
import Unit from './unit';
|
|
4
|
-
export default class UnitsContext {
|
|
5
|
-
constructor(plugin, client) {
|
|
6
|
-
this._plugin = plugin;
|
|
7
|
-
this._client = client;
|
|
8
|
-
this._units = new Map();
|
|
9
|
-
this._e2u = new WeakMap();
|
|
10
|
-
this.interface = new Units(this);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
list() {
|
|
14
|
-
return [...this._units.values()];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
create() {
|
|
18
|
-
let id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'default';
|
|
19
|
-
|
|
20
|
-
if (this._units.has(id)) {
|
|
21
|
-
throw new Error(`Unit already exists: ${id}`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return new Unit(this, id);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
has() {
|
|
28
|
-
let ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'default';
|
|
29
|
-
return typeof ref === 'string' ? this._units.has(ref) : isElement(ref) && this._e2u.has(ref);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get() {
|
|
33
|
-
let ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'default';
|
|
34
|
-
|
|
35
|
-
if (typeof ref === 'string') {
|
|
36
|
-
return this._units.get(ref) || new Unit(this, ref);
|
|
37
|
-
} else if (isElement(ref)) {
|
|
38
|
-
if (this._e2u.has(ref)) {
|
|
39
|
-
return this._e2u.get(ref);
|
|
40
|
-
} else {
|
|
41
|
-
const id = ref.getAttribute(ATTR_UNIT_ID) || 'default';
|
|
42
|
-
return new Unit(this, id).bind(ref);
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
throw new Error(`Required input to be either a string unit ID or an element: ${ref}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
class Units {
|
|
52
|
-
constructor(context) {
|
|
53
|
-
delegateGetters(this, context, ['list', 'has', 'create', 'get']);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|