@openreplay/tracker 11.0.6 → 12.0.0

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/cjs/app/index.d.ts +84 -6
  3. package/cjs/app/index.js +427 -58
  4. package/cjs/app/logger.d.ts +7 -17
  5. package/cjs/app/logger.js +11 -19
  6. package/cjs/app/messages.gen.d.ts +2 -0
  7. package/cjs/app/messages.gen.js +20 -1
  8. package/cjs/app/observer/iframe_observer.js +4 -1
  9. package/cjs/app/observer/shadow_root_observer.js +4 -1
  10. package/cjs/app/observer/top_observer.js +7 -4
  11. package/cjs/common/interaction.d.ts +5 -2
  12. package/cjs/common/messages.gen.d.ts +17 -2
  13. package/cjs/index.d.ts +45 -2
  14. package/cjs/index.js +237 -106
  15. package/cjs/modules/Network/beaconProxy.js +4 -1
  16. package/cjs/modules/Network/fetchProxy.js +24 -1
  17. package/cjs/modules/Network/index.js +6 -3
  18. package/cjs/modules/Network/xhrProxy.js +24 -1
  19. package/cjs/modules/conditionsManager.d.ts +84 -0
  20. package/cjs/modules/conditionsManager.js +343 -0
  21. package/cjs/modules/exception.js +4 -1
  22. package/cjs/modules/featureFlags.d.ts +1 -1
  23. package/cjs/modules/featureFlags.js +36 -46
  24. package/cjs/modules/network.js +5 -2
  25. package/cjs/modules/tagWatcher.d.ts +21 -0
  26. package/cjs/modules/tagWatcher.js +77 -0
  27. package/cjs/modules/userTesting/index.js +30 -4
  28. package/cjs/modules/userTesting/recorder.js +71 -88
  29. package/coverage/clover.xml +577 -544
  30. package/coverage/coverage-final.json +8 -8
  31. package/coverage/lcov-report/index.html +28 -28
  32. package/coverage/lcov-report/main/app/canvas.ts.html +97 -46
  33. package/coverage/lcov-report/main/app/guards.ts.html +1 -1
  34. package/coverage/lcov-report/main/app/index.html +19 -19
  35. package/coverage/lcov-report/main/app/index.ts.html +62 -35
  36. package/coverage/lcov-report/main/app/logger.ts.html +1 -1
  37. package/coverage/lcov-report/main/app/messages.gen.ts.html +32 -5
  38. package/coverage/lcov-report/main/app/nodes.ts.html +17 -5
  39. package/coverage/lcov-report/main/app/observer/iframe_observer.ts.html +1 -1
  40. package/coverage/lcov-report/main/app/observer/iframe_offsets.ts.html +1 -1
  41. package/coverage/lcov-report/main/app/observer/index.html +1 -1
  42. package/coverage/lcov-report/main/app/observer/shadow_root_observer.ts.html +1 -1
  43. package/coverage/lcov-report/main/app/observer/top_observer.ts.html +1 -1
  44. package/coverage/lcov-report/main/app/sanitizer.ts.html +1 -1
  45. package/coverage/lcov-report/main/app/session.ts.html +1 -1
  46. package/coverage/lcov-report/main/app/ticker.ts.html +1 -1
  47. package/coverage/lcov-report/main/index.html +9 -9
  48. package/coverage/lcov-report/main/index.ts.html +27 -6
  49. package/coverage/lcov-report/main/modules/Network/beaconProxy.ts.html +1 -1
  50. package/coverage/lcov-report/main/modules/Network/fetchProxy.ts.html +1 -1
  51. package/coverage/lcov-report/main/modules/Network/index.html +1 -1
  52. package/coverage/lcov-report/main/modules/Network/index.ts.html +1 -1
  53. package/coverage/lcov-report/main/modules/Network/networkMessage.ts.html +1 -1
  54. package/coverage/lcov-report/main/modules/Network/utils.ts.html +1 -1
  55. package/coverage/lcov-report/main/modules/Network/xhrProxy.ts.html +1 -1
  56. package/coverage/lcov-report/main/modules/attributeSender.ts.html +1 -1
  57. package/coverage/lcov-report/main/modules/axiosSpy.ts.html +1 -1
  58. package/coverage/lcov-report/main/modules/conditionsManager.ts.html +92 -38
  59. package/coverage/lcov-report/main/modules/connection.ts.html +1 -1
  60. package/coverage/lcov-report/main/modules/console.ts.html +1 -1
  61. package/coverage/lcov-report/main/modules/constructedStyleSheets.ts.html +1 -1
  62. package/coverage/lcov-report/main/modules/cssrules.ts.html +1 -1
  63. package/coverage/lcov-report/main/modules/exception.ts.html +1 -1
  64. package/coverage/lcov-report/main/modules/featureFlags.ts.html +1 -1
  65. package/coverage/lcov-report/main/modules/focus.ts.html +1 -1
  66. package/coverage/lcov-report/main/modules/fonts.ts.html +1 -1
  67. package/coverage/lcov-report/main/modules/img.ts.html +1 -1
  68. package/coverage/lcov-report/main/modules/index.html +21 -21
  69. package/coverage/lcov-report/main/modules/input.ts.html +1 -1
  70. package/coverage/lcov-report/main/modules/mouse.ts.html +1 -1
  71. package/coverage/lcov-report/main/modules/network.ts.html +1 -1
  72. package/coverage/lcov-report/main/modules/performance.ts.html +1 -1
  73. package/coverage/lcov-report/main/modules/scroll.ts.html +1 -1
  74. package/coverage/lcov-report/main/modules/selection.ts.html +1 -1
  75. package/coverage/lcov-report/main/modules/tabs.ts.html +1 -1
  76. package/coverage/lcov-report/main/modules/tagWatcher.ts.html +54 -27
  77. package/coverage/lcov-report/main/modules/timing.ts.html +1 -1
  78. package/coverage/lcov-report/main/modules/userTesting/SignalManager.ts.html +1 -1
  79. package/coverage/lcov-report/main/modules/userTesting/dnd.ts.html +1 -1
  80. package/coverage/lcov-report/main/modules/userTesting/index.html +1 -1
  81. package/coverage/lcov-report/main/modules/userTesting/index.ts.html +1 -1
  82. package/coverage/lcov-report/main/modules/userTesting/recorder.ts.html +1 -1
  83. package/coverage/lcov-report/main/modules/userTesting/styles.ts.html +1 -1
  84. package/coverage/lcov-report/main/modules/userTesting/utils.ts.html +1 -1
  85. package/coverage/lcov-report/main/modules/viewport.ts.html +1 -1
  86. package/coverage/lcov-report/main/utils.ts.html +1 -1
  87. package/coverage/lcov-report/webworker/BatchWriter.ts.html +1 -1
  88. package/coverage/lcov-report/webworker/MessageEncoder.gen.ts.html +17 -5
  89. package/coverage/lcov-report/webworker/PrimitiveEncoder.ts.html +1 -1
  90. package/coverage/lcov-report/webworker/QueueSender.ts.html +1 -1
  91. package/coverage/lcov-report/webworker/index.html +7 -7
  92. package/coverage/lcov-report/webworker/index.ts.html +1 -1
  93. package/coverage/lcov.info +1100 -1033
  94. package/lib/app/index.d.ts +84 -6
  95. package/lib/app/index.js +387 -44
  96. package/lib/app/logger.d.ts +7 -17
  97. package/lib/app/logger.js +11 -19
  98. package/lib/app/messages.gen.d.ts +2 -0
  99. package/lib/app/messages.gen.js +17 -0
  100. package/lib/common/interaction.d.ts +5 -2
  101. package/lib/common/messages.gen.d.ts +17 -2
  102. package/lib/common/tsconfig.tsbuildinfo +1 -1
  103. package/lib/index.d.ts +45 -2
  104. package/lib/index.js +191 -86
  105. package/lib/modules/conditionsManager.d.ts +84 -0
  106. package/lib/modules/conditionsManager.js +340 -0
  107. package/lib/modules/featureFlags.d.ts +1 -1
  108. package/lib/modules/featureFlags.js +36 -46
  109. package/lib/modules/tagWatcher.d.ts +21 -0
  110. package/lib/modules/tagWatcher.js +74 -0
  111. package/lib/modules/userTesting/recorder.js +71 -88
  112. package/package.json +1 -1
  113. package/tsconfig-base.json +3 -2
@@ -0,0 +1,340 @@
1
+ export default class ConditionsManager {
2
+ constructor(app, startParams) {
3
+ this.app = app;
4
+ this.startParams = startParams;
5
+ this.conditions = [];
6
+ this.hasStarted = false;
7
+ this.createConditionFromFilter = (filter) => {
8
+ if (filter.value.length) {
9
+ const resultCondition = mapCondition(filter);
10
+ if (resultCondition.type) {
11
+ return resultCondition;
12
+ }
13
+ }
14
+ return undefined;
15
+ };
16
+ this.durationInt = null;
17
+ }
18
+ setConditions(conditions) {
19
+ this.conditions = conditions;
20
+ }
21
+ async fetchConditions(projectId, token) {
22
+ try {
23
+ const r = await fetch(`${this.app.options.ingestPoint}/v1/web/conditions/${projectId}`, {
24
+ method: 'GET',
25
+ headers: {
26
+ Authorization: `Bearer ${token}`,
27
+ },
28
+ });
29
+ const { conditions } = (await r.json());
30
+ const mappedConditions = [];
31
+ conditions.forEach((c) => {
32
+ const filters = c.filters;
33
+ filters.forEach((filter) => {
34
+ let cond;
35
+ if (filter.type === 'fetch') {
36
+ cond = {
37
+ type: 'network_request',
38
+ subConditions: [],
39
+ name: c.name,
40
+ };
41
+ filter.filters.forEach((f) => {
42
+ const subCond = this.createConditionFromFilter(f);
43
+ if (subCond) {
44
+ ;
45
+ cond.subConditions.push(subCond);
46
+ }
47
+ });
48
+ }
49
+ else {
50
+ cond = this.createConditionFromFilter(filter);
51
+ }
52
+ if (cond) {
53
+ if (cond.type === 'session_duration') {
54
+ this.processDuration(cond.value[0], c.name);
55
+ }
56
+ mappedConditions.push(Object.assign(Object.assign({}, cond), { name: c.name }));
57
+ }
58
+ });
59
+ });
60
+ this.conditions = mappedConditions;
61
+ }
62
+ catch (e) {
63
+ this.app.debug.error('Critical: cannot fetch start conditions');
64
+ }
65
+ }
66
+ trigger(conditionName) {
67
+ if (this.hasStarted)
68
+ return;
69
+ try {
70
+ this.hasStarted = true;
71
+ void this.app.start(this.startParams, undefined, conditionName);
72
+ }
73
+ catch (e) {
74
+ this.app.debug.error(e);
75
+ }
76
+ }
77
+ processMessage(message) {
78
+ if (this.hasStarted)
79
+ return;
80
+ switch (message[0]) {
81
+ case 78 /* Type.JSException */:
82
+ this.jsExceptionEvent(message);
83
+ break;
84
+ case 27 /* Type.CustomEvent */:
85
+ this.customEvent(message);
86
+ break;
87
+ case 69 /* Type.MouseClick */:
88
+ this.clickEvent(message);
89
+ break;
90
+ case 4 /* Type.SetPageLocation */:
91
+ this.pageLocationEvent(message);
92
+ break;
93
+ case 83 /* Type.NetworkRequest */:
94
+ this.networkRequest(message);
95
+ break;
96
+ default:
97
+ break;
98
+ }
99
+ }
100
+ processFlags(flag) {
101
+ const flagConds = this.conditions.filter((c) => c.type === 'feature_flag');
102
+ if (flagConds.length) {
103
+ flagConds.forEach((flagCond) => {
104
+ const operator = operators[flagCond.operator];
105
+ if (operator && flag.find((f) => operator(f.key, flagCond.value))) {
106
+ this.trigger(flagCond.name);
107
+ }
108
+ });
109
+ }
110
+ }
111
+ processDuration(durationMs, condName) {
112
+ this.durationInt = setInterval(() => {
113
+ const sessionLength = performance.now();
114
+ if (sessionLength > durationMs) {
115
+ this.trigger(condName);
116
+ }
117
+ }, 1000);
118
+ this.app.attachStopCallback(() => {
119
+ if (this.durationInt) {
120
+ clearInterval(this.durationInt);
121
+ }
122
+ });
123
+ }
124
+ networkRequest(message) {
125
+ // method - 2, url - 3, status - 6, duration - 8
126
+ const reqConds = this.conditions.filter((c) => c.type === 'network_request');
127
+ if (!reqConds.length)
128
+ return;
129
+ reqConds.forEach((reqCond) => {
130
+ const validSubConditions = reqCond.subConditions.filter((c) => c.operator !== 'isAny');
131
+ if (validSubConditions.length) {
132
+ const allPass = validSubConditions.every((subCond) => {
133
+ let value;
134
+ switch (subCond.key) {
135
+ case 'url':
136
+ value = message[3];
137
+ break;
138
+ case 'status':
139
+ value = message[6];
140
+ break;
141
+ case 'method':
142
+ value = message[2];
143
+ break;
144
+ case 'duration':
145
+ value = message[8];
146
+ break;
147
+ default:
148
+ break;
149
+ }
150
+ const operator = operators[subCond.operator];
151
+ // @ts-ignore
152
+ if (operator && operator(value, subCond.value)) {
153
+ return true;
154
+ }
155
+ });
156
+ if (allPass) {
157
+ this.trigger(reqCond.name);
158
+ }
159
+ }
160
+ else if (validSubConditions.length === 0 && reqCond.subConditions.length) {
161
+ this.trigger(reqCond.name);
162
+ }
163
+ });
164
+ }
165
+ customEvent(message) {
166
+ // name - 1, payload - 2
167
+ const evConds = this.conditions.filter((c) => c.type === 'custom_event');
168
+ if (evConds.length) {
169
+ evConds.forEach((evCond) => {
170
+ const operator = operators[evCond.operator];
171
+ if (operator &&
172
+ (operator(message[1], evCond.value) || operator(message[2], evCond.value))) {
173
+ this.trigger(evCond.name);
174
+ }
175
+ });
176
+ }
177
+ }
178
+ clickEvent(message) {
179
+ // label - 3, selector - 4
180
+ const clickCond = this.conditions.filter((c) => c.type === 'click');
181
+ if (clickCond.length) {
182
+ clickCond.forEach((click) => {
183
+ const operator = operators[click.operator];
184
+ if (operator && (operator(message[3], click.value) || operator(message[4], click.value))) {
185
+ this.trigger(click.name);
186
+ }
187
+ });
188
+ }
189
+ }
190
+ pageLocationEvent(message) {
191
+ // url - 1
192
+ const urlConds = this.conditions.filter((c) => c.type === 'visited_url');
193
+ if (urlConds) {
194
+ urlConds.forEach((urlCond) => {
195
+ const operator = operators[urlCond.operator];
196
+ if (operator && operator(message[1], urlCond.value)) {
197
+ this.trigger(urlCond.name);
198
+ }
199
+ });
200
+ }
201
+ }
202
+ jsExceptionEvent(message) {
203
+ // name - 1, message - 2, payload - 3
204
+ const testedValues = [message[1], message[2], message[3]];
205
+ const exceptionConds = this.conditions.filter((c) => c.type === 'exception');
206
+ if (exceptionConds) {
207
+ exceptionConds.forEach((exceptionCond) => {
208
+ const operator = operators[exceptionCond.operator];
209
+ if (operator && testedValues.some((val) => operator(val, exceptionCond.value))) {
210
+ this.trigger(exceptionCond.name);
211
+ }
212
+ });
213
+ }
214
+ }
215
+ }
216
+ const operators = {
217
+ is: (val, target) => target.some((t) => val.includes(t)),
218
+ isAny: () => true,
219
+ isNot: (val, target) => !target.some((t) => val.includes(t)),
220
+ contains: (val, target) => target.some((t) => val.includes(t)),
221
+ notContains: (val, target) => !target.some((t) => val.includes(t)),
222
+ startsWith: (val, target) => target.some((t) => val.startsWith(t)),
223
+ endsWith: (val, target) => target.some((t) => val.endsWith(t)),
224
+ greaterThan: (val, target) => val > target,
225
+ greaterOrEqual: (val, target) => val >= target,
226
+ lessOrEqual: (val, target) => val <= target,
227
+ lessThan: (val, target) => val < target,
228
+ };
229
+ const mapCondition = (condition) => {
230
+ const opMap = {
231
+ on: 'is',
232
+ notOn: 'isNot',
233
+ '\u003e': 'greaterThan',
234
+ '\u003c': 'lessThan',
235
+ '\u003d': 'is',
236
+ '\u003c=': 'lessOrEqual',
237
+ '\u003e=': 'greaterOrEqual',
238
+ };
239
+ const mapOperator = (operator) => {
240
+ const keys = Object.keys(opMap);
241
+ // @ts-ignore
242
+ if (keys.includes(operator))
243
+ return opMap[operator];
244
+ };
245
+ let con = {
246
+ type: '',
247
+ operator: '',
248
+ value: condition.value,
249
+ key: '',
250
+ };
251
+ switch (condition.type) {
252
+ case 'click':
253
+ con = {
254
+ type: 'click',
255
+ operator: mapOperator(condition.operator),
256
+ value: condition.value,
257
+ key: '',
258
+ };
259
+ break;
260
+ case 'location':
261
+ con = {
262
+ type: 'visited_url',
263
+ // @ts-ignore
264
+ operator: condition.operator,
265
+ value: condition.value,
266
+ key: '',
267
+ };
268
+ break;
269
+ case 'custom':
270
+ con = {
271
+ type: 'custom_event',
272
+ // @ts-ignore
273
+ operator: condition.operator,
274
+ value: condition.value,
275
+ key: '',
276
+ };
277
+ break;
278
+ case 'metadata':
279
+ con = {
280
+ // @ts-ignore
281
+ type: condition.source === 'featureFlag' ? 'feature_flag' : condition.type,
282
+ // @ts-ignore
283
+ operator: condition.operator,
284
+ value: condition.value,
285
+ key: '',
286
+ };
287
+ break;
288
+ case 'error':
289
+ con = {
290
+ type: 'exception',
291
+ // @ts-ignore
292
+ operator: condition.operator,
293
+ value: condition.value,
294
+ key: '',
295
+ };
296
+ break;
297
+ case 'duration':
298
+ con = {
299
+ type: 'session_duration',
300
+ // @ts-ignore
301
+ value: condition.value[0],
302
+ key: '',
303
+ };
304
+ break;
305
+ case 'fetchUrl':
306
+ con = {
307
+ type: 'network_request',
308
+ key: 'url',
309
+ operator: condition.operator,
310
+ value: condition.value,
311
+ };
312
+ break;
313
+ case 'fetchStatusCode':
314
+ con = {
315
+ type: 'network_request',
316
+ key: 'status',
317
+ operator: mapOperator(condition.operator),
318
+ value: condition.value,
319
+ };
320
+ break;
321
+ case 'fetchMethod':
322
+ con = {
323
+ type: 'network_request',
324
+ key: 'method',
325
+ operator: mapOperator(condition.operator),
326
+ value: condition.value,
327
+ };
328
+ break;
329
+ case 'fetchDuration':
330
+ con = {
331
+ type: 'network_request',
332
+ key: 'duration',
333
+ operator: mapOperator(condition.operator),
334
+ value: condition.value,
335
+ };
336
+ break;
337
+ }
338
+ // @ts-ignore
339
+ return con;
340
+ };
@@ -18,7 +18,7 @@ export default class FeatureFlags {
18
18
  getFeatureFlag(flagName: string): IFeatureFlag | undefined;
19
19
  isFlagEnabled(flagName: string): boolean;
20
20
  onFlagsLoad(cb: (flags: IFeatureFlag[]) => void): void;
21
- reloadFlags(): Promise<void>;
21
+ reloadFlags(token?: string): Promise<void>;
22
22
  handleFlags(flags: IFeatureFlag[]): void;
23
23
  clearPersistFlags(): void;
24
24
  diffPersist(flags: IFeatureFlag[]): IFeatureFlag[];
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  export default class FeatureFlags {
11
2
  constructor(app) {
12
3
  this.app = app;
@@ -27,45 +18,44 @@ export default class FeatureFlags {
27
18
  onFlagsLoad(cb) {
28
19
  this.onFlagsCb = cb;
29
20
  }
30
- reloadFlags() {
31
- return __awaiter(this, void 0, void 0, function* () {
32
- const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
33
- const persistFlags = {};
34
- if (persistFlagsStr) {
35
- const persistArray = persistFlagsStr.split(';').filter(Boolean);
36
- persistArray.forEach((flag) => {
37
- const flagObj = JSON.parse(flag);
38
- persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
39
- });
40
- }
41
- const sessionInfo = this.app.session.getInfo();
42
- const userInfo = this.app.session.userInfo;
43
- const requestObject = {
44
- projectID: sessionInfo.projectID,
45
- userID: sessionInfo.userID,
46
- metadata: sessionInfo.metadata,
47
- referrer: document.referrer,
48
- os: userInfo.userOS,
49
- device: userInfo.userDevice,
50
- country: userInfo.userCountry,
51
- state: userInfo.userState,
52
- city: userInfo.userCity,
53
- browser: userInfo.userBrowser,
54
- persistFlags: persistFlags,
55
- };
56
- const resp = yield fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
57
- method: 'POST',
58
- headers: {
59
- 'Content-Type': 'application/json',
60
- Authorization: `Bearer ${this.app.session.getSessionToken()}`,
61
- },
62
- body: JSON.stringify(requestObject),
21
+ async reloadFlags(token) {
22
+ const persistFlagsStr = this.app.sessionStorage.getItem(this.storageKey);
23
+ const persistFlags = {};
24
+ if (persistFlagsStr) {
25
+ const persistArray = persistFlagsStr.split(';').filter(Boolean);
26
+ persistArray.forEach((flag) => {
27
+ const flagObj = JSON.parse(flag);
28
+ persistFlags[flagObj.key] = { key: flagObj.key, value: flagObj.value };
63
29
  });
64
- if (resp.status === 200) {
65
- const data = yield resp.json();
66
- return this.handleFlags(data.flags);
67
- }
30
+ }
31
+ const sessionInfo = this.app.session.getInfo();
32
+ const userInfo = this.app.session.userInfo;
33
+ const requestObject = {
34
+ projectID: sessionInfo.projectID,
35
+ userID: sessionInfo.userID,
36
+ metadata: sessionInfo.metadata,
37
+ referrer: document.referrer,
38
+ os: userInfo.userOS,
39
+ device: userInfo.userDevice,
40
+ country: userInfo.userCountry,
41
+ state: userInfo.userState,
42
+ city: userInfo.userCity,
43
+ browser: userInfo.userBrowser,
44
+ persistFlags: persistFlags,
45
+ };
46
+ const authToken = token !== null && token !== void 0 ? token : this.app.session.getSessionToken();
47
+ const resp = await fetch(this.app.options.ingestPoint + '/v1/web/feature-flags', {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ Authorization: `Bearer ${authToken}`,
52
+ },
53
+ body: JSON.stringify(requestObject),
68
54
  });
55
+ if (resp.status === 200) {
56
+ const data = await resp.json();
57
+ return this.handleFlags(data.flags);
58
+ }
69
59
  }
70
60
  handleFlags(flags) {
71
61
  var _a;
@@ -0,0 +1,21 @@
1
+ export declare const WATCHED_TAGS_KEY = "__or__watched_tags__";
2
+ declare class TagWatcher {
3
+ private readonly sessionStorage;
4
+ private readonly errLog;
5
+ private readonly onTag;
6
+ intervals: Record<string, ReturnType<typeof setInterval>>;
7
+ tags: {
8
+ id: number;
9
+ selector: string;
10
+ }[];
11
+ observer: IntersectionObserver;
12
+ constructor(sessionStorage: Storage, errLog: (args: any[]) => void, onTag: (tag: number) => void);
13
+ fetchTags(ingest: string, token: string): Promise<void>;
14
+ setTags(tags: {
15
+ id: number;
16
+ selector: string;
17
+ }[]): void;
18
+ onTagRendered(tagId: number): void;
19
+ clear(): void;
20
+ }
21
+ export default TagWatcher;
@@ -0,0 +1,74 @@
1
+ export const WATCHED_TAGS_KEY = '__or__watched_tags__';
2
+ class TagWatcher {
3
+ constructor(sessionStorage, errLog, onTag) {
4
+ var _a;
5
+ this.sessionStorage = sessionStorage;
6
+ this.errLog = errLog;
7
+ this.onTag = onTag;
8
+ this.intervals = {};
9
+ this.tags = [];
10
+ const tags = JSON.parse((_a = sessionStorage.getItem(WATCHED_TAGS_KEY)) !== null && _a !== void 0 ? _a : '[]');
11
+ this.setTags(tags);
12
+ this.observer = new IntersectionObserver((entries) => {
13
+ entries.forEach((entry) => {
14
+ if (entry.isIntersecting) {
15
+ if (entry.target) {
16
+ // @ts-ignore
17
+ const tag = entry.target.__or_watcher_tagname;
18
+ if (tag) {
19
+ this.onTagRendered(tag);
20
+ }
21
+ this.observer.unobserve(entry.target);
22
+ }
23
+ }
24
+ });
25
+ });
26
+ }
27
+ async fetchTags(ingest, token) {
28
+ return fetch(`${ingest}/v1/web/tags`, {
29
+ method: 'GET',
30
+ headers: {
31
+ Authorization: `Bearer ${token}`,
32
+ },
33
+ })
34
+ .then((r) => r.json())
35
+ .then(({ tags }) => {
36
+ if (tags && tags.length) {
37
+ this.setTags(tags);
38
+ const tagString = JSON.stringify(tags);
39
+ this.sessionStorage.setItem(WATCHED_TAGS_KEY, tagString || '');
40
+ }
41
+ })
42
+ .catch((e) => this.errLog(e));
43
+ }
44
+ setTags(tags) {
45
+ this.tags = tags;
46
+ this.intervals = {};
47
+ tags.forEach((tag) => {
48
+ this.intervals[tag.id] = setInterval(() => {
49
+ const possibleEls = document.querySelectorAll(tag.selector);
50
+ if (possibleEls.length > 0) {
51
+ const el = possibleEls[0];
52
+ // @ts-ignore
53
+ el.__or_watcher_tagname = tag.id;
54
+ this.observer.observe(el);
55
+ }
56
+ }, 500);
57
+ });
58
+ }
59
+ onTagRendered(tagId) {
60
+ if (this.intervals[tagId]) {
61
+ clearInterval(this.intervals[tagId]);
62
+ }
63
+ this.onTag(tagId);
64
+ }
65
+ clear() {
66
+ this.tags.forEach((tag) => {
67
+ clearInterval(this.intervals[tag.id]);
68
+ });
69
+ this.tags = [];
70
+ this.intervals = {};
71
+ this.observer.disconnect();
72
+ }
73
+ }
74
+ export default TagWatcher;