@newrelic/video-core 5.0.1 → 5.1.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 (79) hide show
  1. package/CHANGELOG.md +20 -2
  2. package/README.md +13 -0
  3. package/__mock__.js +8 -4
  4. package/dist/cjs/browser/index.js +1 -1
  5. package/dist/cjs/browser/index.js.LICENSE.txt +1 -1
  6. package/dist/cjs/browser/index.js.map +1 -1
  7. package/dist/cjs/index.js +1 -1
  8. package/dist/cjs/index.js.LICENSE.txt +1 -1
  9. package/dist/cjs/index.js.map +1 -1
  10. package/dist/cjs/vega/index.js +1 -1
  11. package/dist/cjs/vega/index.js.LICENSE.txt +1 -1
  12. package/dist/cjs/vega/index.js.map +1 -1
  13. package/dist/esm/browser/index.js +1 -1
  14. package/dist/esm/browser/index.js.LICENSE.txt +1 -1
  15. package/dist/esm/browser/index.js.map +1 -1
  16. package/dist/esm/index.js +1 -1
  17. package/dist/esm/index.js.LICENSE.txt +1 -1
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/vega/index.js +1 -1
  20. package/dist/esm/vega/index.js.LICENSE.txt +1 -1
  21. package/dist/esm/vega/index.js.map +1 -1
  22. package/dist/types/browser/agent.d.ts +46 -0
  23. package/dist/types/browser/harvestScheduler.d.ts +125 -0
  24. package/dist/types/browser/index.d.ts +34 -0
  25. package/dist/types/chrono.d.ts +52 -0
  26. package/dist/types/connectedDevice/connectedDeviceAgent.d.ts +63 -0
  27. package/dist/types/connectedDevice/connectedDeviceConstants.d.ts +51 -0
  28. package/dist/types/connectedDevice/connectedDeviceHarvester.d.ts +198 -0
  29. package/dist/types/connectedDevice/index.d.ts +30 -0
  30. package/dist/types/constants.d.ts +30 -0
  31. package/dist/types/core.d.ts +54 -0
  32. package/dist/types/emitter.d.ts +42 -0
  33. package/dist/types/eventAggregator.d.ts +97 -0
  34. package/dist/types/index.d.ts +33 -0
  35. package/dist/types/log.d.ts +61 -0
  36. package/dist/types/obfuscate.d.ts +13 -0
  37. package/dist/types/optimizedHttpClient.d.ts +83 -0
  38. package/dist/types/recordEvent.d.ts +30 -0
  39. package/dist/types/retryQueueHandler.d.ts +46 -0
  40. package/dist/types/tracker.d.ts +176 -0
  41. package/dist/types/utils/eventBuilder.d.ts +69 -0
  42. package/dist/types/utils/harvestTimer.d.ts +45 -0
  43. package/dist/types/utils/index.d.ts +38 -0
  44. package/dist/types/utils/qoeFilters.d.ts +88 -0
  45. package/dist/types/videoConfiguration.d.ts +90 -0
  46. package/dist/types/videotracker.d.ts +366 -0
  47. package/dist/types/videotrackerstate.d.ts +228 -0
  48. package/dist/umd/nrvideo.min.js +1 -1
  49. package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
  50. package/dist/umd/nrvideo.min.js.map +1 -1
  51. package/package.json +15 -2
  52. package/src/browser/{agent.js → agent.ts} +21 -16
  53. package/src/browser/{harvestScheduler.js → harvestScheduler.ts} +51 -24
  54. package/src/{chrono.js → chrono.ts} +29 -24
  55. package/src/connectedDevice/{connectedDeviceAgent.js → connectedDeviceAgent.ts} +14 -10
  56. package/src/connectedDevice/{connectedDeviceConstants.js → connectedDeviceConstants.ts} +5 -3
  57. package/src/connectedDevice/{connectedDeviceHarvester.js → connectedDeviceHarvester.ts} +71 -34
  58. package/src/constants.ts +62 -0
  59. package/src/{core.js → core.ts} +31 -18
  60. package/src/{emitter.js → emitter.ts} +16 -5
  61. package/src/{eventAggregator.js → eventAggregator.ts} +33 -19
  62. package/src/global.d.ts +25 -0
  63. package/src/{log.js → log.ts} +23 -17
  64. package/src/{obfuscate.js → obfuscate.ts} +6 -1
  65. package/src/{optimizedHttpClient.js → optimizedHttpClient.ts} +37 -13
  66. package/src/{recordEvent.js → recordEvent.ts} +11 -9
  67. package/src/{retryQueueHandler.js → retryQueueHandler.ts} +17 -12
  68. package/src/{tracker.js → tracker.ts} +56 -23
  69. package/src/utils/{eventBuilder.js → eventBuilder.ts} +31 -14
  70. package/src/utils/{harvestTimer.js → harvestTimer.ts} +21 -4
  71. package/src/utils/{index.js → index.ts} +14 -14
  72. package/src/utils/{qoeFilters.js → qoeFilters.ts} +19 -8
  73. package/src/{videoConfiguration.js → videoConfiguration.ts} +44 -10
  74. package/src/{videotracker.js → videotracker.ts} +119 -78
  75. package/src/{videotrackerstate.js → videotrackerstate.ts} +113 -49
  76. package/src/constants.js +0 -63
  77. /package/src/browser/{index.js → index.ts} +0 -0
  78. /package/src/connectedDevice/{index.js → index.ts} +0 -0
  79. /package/src/{index.js → index.ts} +0 -0
@@ -10,6 +10,7 @@ import {
10
10
  CD_CONNECT_RETRY_DELAY_MS,
11
11
  CD_CONNECT_TIMEOUT_MS,
12
12
  CD_DATA_TIMEOUT_MS,
13
+ VegaEndpoint,
13
14
  } from "./connectedDeviceConstants";
14
15
  import {
15
16
  bufferEventWithQoeDedup,
@@ -17,9 +18,27 @@ import {
17
18
  partitionByQoeCycle,
18
19
  applyQoeDirtyFilter,
19
20
  } from "../utils/qoeFilters";
20
- import { createHarvestTimer } from "../utils/harvestTimer";
21
+ import { createHarvestTimer, HarvestTimer } from "../utils/harvestTimer";
21
22
  import { applyObfuscationRules } from "../obfuscate";
22
23
  import Log from "../log";
24
+ import { EventAttributes } from "../utils/eventBuilder";
25
+
26
+ export interface ConnectedDeviceHarvesterOptions {
27
+ accountId?: string;
28
+ applicationToken: string;
29
+ endpoint: string;
30
+ deviceInfo?: Record<string, any>;
31
+ }
32
+
33
+ interface DeviceInfo {
34
+ uuid: any;
35
+ osVersion: any;
36
+ deviceModel: any;
37
+ deviceManufacturer: any;
38
+ osBuild: any;
39
+ appBuild: any;
40
+ architecture: any;
41
+ }
23
42
 
24
43
  /**
25
44
  * Generic NR mobile-collector harvester for the connected-device pipeline
@@ -32,11 +51,29 @@ import Log from "../log";
32
51
  *
33
52
  */
34
53
  export default class ConnectedDeviceHarvester {
54
+ accountId?: string;
55
+ applicationToken: string;
56
+ endpoint: VegaEndpoint;
57
+ harvestInterval: number;
58
+ maxBufferSize: number;
59
+ deviceInfo: DeviceInfo;
60
+ eventBuffer: NrVideoEventAggregator;
61
+ dataToken: string | null;
62
+ isHarvesting: boolean;
63
+ isDisposed: boolean;
64
+ _isFetchingToken: boolean;
65
+ timer: HarvestTimer;
66
+ qoeCycleCount: number;
67
+ forceQoeNextCycle: boolean;
68
+ beforeDrainCallback: (() => void) | null;
69
+ _lastSentQoeKpis: Record<string, any>;
70
+ _connectAttempt: number;
71
+
35
72
  /**
36
73
  * @param {object} opts
37
74
  * @param {string} [opts.accountId] Captured for parity with CAF; not transmitted.
38
- * @param {string} opts.applicationToken Sent as `X-App-License-Key` header.
39
- * @param {string} opts.endpoint One of `US`, `EU`, `staging`.
75
+ * @param {string} opts.applicationToken Sent as `X-App-License-Key` header.
76
+ * @param {string} opts.endpoint One of `US`, `EU`, `staging`.
40
77
  * @param {object} [opts.deviceInfo] Customer-collected device identity. Any of:
41
78
  * uuid, osVersion, deviceModel, deviceManufacturer, osBuild, appBuild, architecture.
42
79
  * Each field optional — missing values fall back to placeholders from
@@ -52,18 +89,18 @@ export default class ConnectedDeviceHarvester {
52
89
  applicationToken,
53
90
  endpoint,
54
91
  deviceInfo,
55
- } = {}) {
92
+ }: Partial<ConnectedDeviceHarvesterOptions> = {}) {
56
93
  if (!applicationToken) {
57
- throw new Error("applicationToken is required");
94
+ throw new Error("applicationToken is required");
58
95
  }
59
96
  const normalizedEndpoint = endpoint?.toLowerCase();
60
- if (!(normalizedEndpoint in ENDPOINT_URL)) {
97
+ if (!((normalizedEndpoint as string) in ENDPOINT_URL)) {
61
98
  throw new Error("Invalid endpoint");
62
99
  }
63
100
 
64
101
  this.accountId = accountId;
65
102
  this.applicationToken = applicationToken;
66
- this.endpoint = normalizedEndpoint;
103
+ this.endpoint = normalizedEndpoint as VegaEndpoint;
67
104
  this.harvestInterval = DEFAULT_HARVEST_TIME;
68
105
  this.maxBufferSize = DEFAULT_BUFFER_SIZE;
69
106
 
@@ -90,16 +127,16 @@ export default class ConnectedDeviceHarvester {
90
127
  architecture: architecture || CD_METADATA.architecture,
91
128
  };
92
129
 
93
- this.eventBuffer = new NrVideoEventAggregator();
130
+ this.eventBuffer = new NrVideoEventAggregator();
94
131
 
95
132
  // Smart-harvest wiring: buffer triggers an early drain at 60% (smart) and
96
133
  // 90% (overflow) capacity, before makeRoom()'s drop-oldest FIFO eviction
97
- // would silently lose events.
134
+ // would silently lose events.
98
135
  this.eventBuffer.setSmartHarvestCallback((type, threshold) =>
99
136
  this.triggerSmartHarvest(type, threshold)
100
137
  );
101
138
 
102
- this.dataToken = null;
139
+ this.dataToken = null;
103
140
  this.isHarvesting = false;
104
141
  this.isDisposed = false;
105
142
  this._isFetchingToken = false; // guard: prevents parallel /v5/connect sequences
@@ -112,13 +149,13 @@ export default class ConnectedDeviceHarvester {
112
149
  errorLabel: "ConnectedDeviceHarvester",
113
150
  });
114
151
 
115
- // QoE state
152
+ // QoE state
116
153
  this.qoeCycleCount = 1;
117
154
  this.forceQoeNextCycle = false;
118
155
  this.beforeDrainCallback = null;
119
156
  this._lastSentQoeKpis = {};
120
157
 
121
- // Connect retry state
158
+ // Connect retry state
122
159
  this._connectAttempt = 0;
123
160
 
124
161
  // fire-and-forget initialise.
@@ -132,15 +169,15 @@ export default class ConnectedDeviceHarvester {
132
169
  *
133
170
  * @returns {string}
134
171
  */
135
- getEndpointBaseUrl() {
172
+ getEndpointBaseUrl(): string {
136
173
  return ENDPOINT_URL[this.endpoint] ?? ENDPOINT_URL.us;
137
174
  }
138
175
 
139
176
  /**
140
- * Two-phase init: fetch dataToken, then start harvest interval.
177
+ * Two-phase init: fetch dataToken, then start harvest interval.
141
178
  * @returns {Promise<void>}
142
179
  */
143
- async initialise() {
180
+ async initialise(): Promise<void> {
144
181
  await this.fetchDataTokens();
145
182
  // Defensive guard: if dispose() ran while fetchDataTokens was awaiting,
146
183
  // do not start an interval that will outlive the harvester.
@@ -153,7 +190,7 @@ export default class ConnectedDeviceHarvester {
153
190
  /**
154
191
  * POST `/v5/connect` to obtain a dataToken. Retries up to
155
192
  * CD_CONNECT_MAX_ATTEMPTS times with a fixed CD_CONNECT_RETRY_DELAY_MS
156
- * wait between attempts.
193
+ * wait between attempts.
157
194
  *
158
195
  * Iterative loop keeps the call stack flat across all attempts.
159
196
  * `_isFetchingToken` guard ensures only one connect sequence runs at a
@@ -163,7 +200,7 @@ export default class ConnectedDeviceHarvester {
163
200
  *
164
201
  * @returns {Promise<void>}
165
202
  */
166
- async fetchDataTokens() {
203
+ async fetchDataTokens(): Promise<void> {
167
204
  if (this._isFetchingToken) return;
168
205
  this._isFetchingToken = true;
169
206
 
@@ -205,7 +242,7 @@ export default class ConnectedDeviceHarvester {
205
242
  this._connectAttempt = attempt + 1;
206
243
  Log.notice("ConnectedDeviceHarvester: dataToken acquired");
207
244
  return;
208
- } catch (err) {
245
+ } catch (err: any) {
209
246
  this._connectAttempt = attempt + 1;
210
247
  Log.error(
211
248
  `ConnectedDeviceHarvester: /v5/connect failed (attempt ${this._connectAttempt}/${CD_CONNECT_MAX_ATTEMPTS}):`,
@@ -231,7 +268,7 @@ export default class ConnectedDeviceHarvester {
231
268
  * Starts the periodic harvest timer.
232
269
  * Idempotent — safe to call repeatedly.
233
270
  */
234
- startHarvestInterval() {
271
+ startHarvestInterval(): void {
235
272
  if (this.isDisposed) return;
236
273
  this.timer.start();
237
274
  }
@@ -247,14 +284,14 @@ export default class ConnectedDeviceHarvester {
247
284
  * @param {number} threshold - The threshold percentage that triggered the harvest (60 or 90).
248
285
  * @returns {Promise<void>}
249
286
  */
250
- async triggerSmartHarvest(type, threshold) {
287
+ async triggerSmartHarvest(type: string, threshold: number): Promise<void> {
251
288
  Log.notice(
252
289
  `ConnectedDeviceHarvester: smart-harvest trigger (${type} at ${threshold}%)`
253
290
  );
254
291
  if (!this.eventBuffer || this.eventBuffer.isEmpty()) return;
255
292
  try {
256
293
  await this.sendBufferedEvents();
257
- } catch (error) {
294
+ } catch (error: any) {
258
295
  Log.error(`${type} smart-harvest failed:`, error.message);
259
296
  } finally {
260
297
  // Reset the periodic clock — next periodic tick happens `harvestInterval`
@@ -269,13 +306,13 @@ export default class ConnectedDeviceHarvester {
269
306
  * @param {object} eventObject
270
307
  * @returns {boolean}
271
308
  */
272
- addEvent(eventObject) {
309
+ addEvent(eventObject: EventAttributes): boolean {
273
310
  try {
274
311
  // Shared QOE_AGGREGATE dedup + non-QoE append. Timestamp
275
312
  // is preserved from `recordEvent.js` (emit-time), matching the Browser
276
313
  // pipeline. Cross-pipeline analytics produce consistent timestamps.
277
314
  return bufferEventWithQoeDedup(this.eventBuffer, eventObject);
278
- } catch (err) {
315
+ } catch (err: any) {
279
316
  Log.error("ConnectedDeviceHarvester.addEvent failed:", err.message);
280
317
  return false;
281
318
  }
@@ -285,7 +322,7 @@ export default class ConnectedDeviceHarvester {
285
322
  * Forces the next harvest cycle to ship QOE_AGGREGATE regardless of cycle
286
323
  * multiplier or dirty check. Used at CONTENT_END for final QoE flush.
287
324
  */
288
- forceQoeNextHarvest() {
325
+ forceQoeNextHarvest(): void {
289
326
  this.forceQoeNextCycle = true;
290
327
  }
291
328
 
@@ -295,7 +332,7 @@ export default class ConnectedDeviceHarvester {
295
332
  * before they ship.
296
333
  * @param {Function|null} cb
297
334
  */
298
- setBeforeDrainCallback(cb) {
335
+ setBeforeDrainCallback(cb: (() => void) | null): void {
299
336
  if (typeof cb === "function" || cb === null) {
300
337
  this.beforeDrainCallback = cb;
301
338
  }
@@ -309,7 +346,7 @@ export default class ConnectedDeviceHarvester {
309
346
  * @param {object} freshKpis - Object with latest KPI values
310
347
  * @param {string} [viewId] - The viewId of the player whose QoE event to update
311
348
  */
312
- refreshQoeKpis(freshKpis, viewId) {
349
+ refreshQoeKpis(freshKpis: EventAttributes, viewId?: string): void {
313
350
  refreshQoeKpisInBuffer(this.eventBuffer, freshKpis, viewId);
314
351
  }
315
352
 
@@ -320,7 +357,7 @@ export default class ConnectedDeviceHarvester {
320
357
  *
321
358
  * @param {number} interval - New cadence in ms.
322
359
  */
323
- setHarvestInterval(interval) {
360
+ setHarvestInterval(interval: number): void {
324
361
  if (typeof interval !== "number" || interval <= 0) return;
325
362
  this.harvestInterval = interval;
326
363
  this.timer.updateInterval(interval);
@@ -336,7 +373,7 @@ export default class ConnectedDeviceHarvester {
336
373
  *
337
374
  * @returns {Promise<void>}
338
375
  */
339
- async sendBufferedEvents() {
376
+ async sendBufferedEvents(): Promise<void> {
340
377
  if (this.isHarvesting) return;
341
378
  if (!this.dataToken) return;
342
379
  if (this.eventBuffer.isEmpty()) return;
@@ -345,7 +382,7 @@ export default class ConnectedDeviceHarvester {
345
382
  if (typeof this.beforeDrainCallback === "function") {
346
383
  try {
347
384
  this.beforeDrainCallback();
348
- } catch (e) {
385
+ } catch (e: any) {
349
386
  Log.error("ConnectedDeviceHarvester beforeDrainCallback failed:", e.message);
350
387
  }
351
388
  }
@@ -426,7 +463,7 @@ export default class ConnectedDeviceHarvester {
426
463
 
427
464
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
428
465
  Log.notice(`ConnectedDeviceHarvester: sent ${filtered.length} events`);
429
- } catch (err) {
466
+ } catch (err: any) {
430
467
  Log.error("ConnectedDeviceHarvester: /v3/data send failed:", err.message);
431
468
  // Re-queue drained events into buffer; next tick retries.
432
469
  for (const e of filtered) this.eventBuffer.add(e);
@@ -443,7 +480,7 @@ export default class ConnectedDeviceHarvester {
443
480
  * @returns {Array}
444
481
  * @private
445
482
  */
446
- _buildDeviceInfo() {
483
+ _buildDeviceInfo(): any[] {
447
484
  const d = this.deviceInfo;
448
485
  return [
449
486
  CD_DEVICE_INFO[0], // osName — fixed ("Vega")
@@ -469,7 +506,7 @@ export default class ConnectedDeviceHarvester {
469
506
  * @returns {object}
470
507
  * @private
471
508
  */
472
- _buildMetadata() {
509
+ _buildMetadata(): Record<string, any> {
473
510
  const d = this.deviceInfo;
474
511
  return {
475
512
  ...CD_METADATA,
@@ -495,7 +532,7 @@ export default class ConnectedDeviceHarvester {
495
532
  * @returns {Promise<Response>}
496
533
  * @private
497
534
  */
498
- _fetchWithTimeout(url, options, timeoutMs) {
535
+ _fetchWithTimeout(url: string, options: RequestInit, timeoutMs: number): Promise<Response> {
499
536
  const controller = new AbortController();
500
537
  const timerId = setTimeout(() => controller.abort(), timeoutMs);
501
538
  return fetch(url, { ...options, signal: controller.signal })
@@ -506,7 +543,7 @@ export default class ConnectedDeviceHarvester {
506
543
  * Stops the harvest interval and attempts one final best-effort send.
507
544
  * @returns {Promise<void>}
508
545
  */
509
- async dispose() {
546
+ async dispose(): Promise<void> {
510
547
  this.isDisposed = true;
511
548
  this.timer.stop();
512
549
  return this.sendBufferedEvents();
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Constants for the library.
3
+ * @class Constants
4
+ * @static
5
+ */
6
+ class Constants {
7
+ /**
8
+ * Enum for types/positions of ads.
9
+ * @example var type = Constants.AdPositions.PRE
10
+ * @enum {String}
11
+ */
12
+ static AdPositions = {
13
+ /** For ads shown before the content. */
14
+ PRE: "pre",
15
+ /** For ads shown during the content. */
16
+ MID: "mid",
17
+ /** For ads shown after the content. */
18
+ POST: "post",
19
+ };
20
+
21
+ // bam.nr-data.net
22
+ //bam-cell.nr-data.net
23
+ static COLLECTOR: Record<string, string | string[]> = {
24
+ US: ["bam.nr-data.net", "bam-cell.nr-data.net"],
25
+ EU: "bam.eu01.nr-data.net",
26
+ JP: "bam.jp.nr-data.net",
27
+ Staging: "staging-bam-cell.nr-data.net",
28
+ GOV: "gov-bam.nr-data.net",
29
+ };
30
+
31
+ // ====== VALID EVENT TYPES ======
32
+ static VALID_EVENT_TYPES = [
33
+ "VideoAction",
34
+ "VideoAdAction",
35
+ "VideoErrorAction",
36
+ "VideoCustomAction",
37
+ ];
38
+
39
+ static MAX_PAYLOAD_SIZE = 1048576; // 1MB = 1024 × 1024 bytes
40
+ static MAX_BEACON_SIZE = 61440; // 60KB = 60 × 1024 bytes
41
+ static MAX_EVENTS_PER_BATCH = 1000;
42
+ static INTERVAL = 10000; //10 seconds
43
+ static DEFAULT_QOE_INTERVAL_FACTOR = 2;
44
+
45
+ static QOE_KPI_KEYS = [
46
+ "startupTime", "peakBitrate", "averageBitrate", "totalPlaytime",
47
+ "totalRebufferingTime", "rebufferingRatio", "hadStartupError",
48
+ "hadPlaybackError", "numberOfErrors"
49
+ ];
50
+
51
+ static QOE_AGGREGATE_KEYS = [
52
+ "coreVersion", "instrumentation.name",
53
+ "instrumentation.provider", "instrumentation.version", "isBackgroundEvent", "playerName", "playerVersion",
54
+ "src", "viewId", "viewSession", "contentIsAutoplayed", "contentIsMuted", "contentRenditionHeight", "contentRenditionWidth",
55
+ "contentSrc", "numberOfVideos", "pageUrl", "trackerName", "trackerVersion", "contentDuration", "contentPlayrate", "contentPlayhead",
56
+ "contentPreload", "elapsedTime", "contentTitle", "contentId", "contentIsLive", "deviceType", "deviceGroup", "deviceManufacturer",
57
+ "deviceModel", "deviceName", "deviceSize", "deviceUuid", "contentRenditionName", "contentIsFullscreen", "contentCdn",
58
+ "contentFps", "asnOrganization", "asnLongitude", "asnLatitude", "asn", "timeSinceRequested", "timeSinceStarted"
59
+ ];
60
+ }
61
+
62
+ export default Constants;
@@ -1,6 +1,19 @@
1
1
  import Log from "./log";
2
2
  import { recordEvent } from "./recordEvent";
3
- import { setVideoConfig } from "./videoConfiguration";
3
+ import { setVideoConfig, RawVideoConfigInfo, RawVideoConfigOptions } from "./videoConfiguration";
4
+ import Emitter from "./emitter";
5
+
6
+ /** Structural contract Core needs from anything passed to addTracker/removeTracker. */
7
+ export interface TrackerLike extends Emitter {
8
+ dispose(): void;
9
+ trackerInit?(): void;
10
+ }
11
+
12
+ export interface CoreAddTrackerOptions {
13
+ info?: RawVideoConfigInfo;
14
+ config?: RawVideoConfigOptions;
15
+ src?: string;
16
+ }
4
17
 
5
18
  /**
6
19
  * Static class that sums up core functionalities of the library.
@@ -13,14 +26,14 @@ class Core {
13
26
  * @param {(Emitter|Tracker)} tracker Tracker instance to add.
14
27
  * @param {object} options Configuration options including video analytics settings.
15
28
  */
16
- static addTracker(tracker, options) {
29
+ static addTracker(tracker: TrackerLike, options?: CoreAddTrackerOptions): void {
17
30
  // Set video analytics configuration. The optional `options.src` field
18
31
  // selects the pipeline: `'Vega'` writes globalThis.__NRVIDEO_CD__,
19
- // anything else writes window.NRVIDEO.
32
+ // anything else writes window.NRVIDEO.
20
33
  if (options?.info) {
21
34
  setVideoConfig(options.info, options?.config, options?.src);
22
35
  }
23
-
36
+
24
37
  if (tracker.on && tracker.emit) {
25
38
  trackers.push(tracker);
26
39
  tracker.on("*", eventHandler);
@@ -37,7 +50,7 @@ class Core {
37
50
  *
38
51
  * @param {Tracker} tracker Tracker to remove.
39
52
  */
40
- static removeTracker(tracker) {
53
+ static removeTracker(tracker: TrackerLike): void {
41
54
  tracker.off("*", eventHandler);
42
55
  tracker.dispose();
43
56
  let index = trackers.indexOf(tracker);
@@ -49,7 +62,7 @@ class Core {
49
62
  *
50
63
  * @returns {Tracker[]} Array of trackers.
51
64
  */
52
- static getTrackers() {
65
+ static getTrackers(): TrackerLike[] {
53
66
  return trackers;
54
67
  }
55
68
 
@@ -59,13 +72,13 @@ class Core {
59
72
  * @param {string} actionName - Action name
60
73
  * @param {object} data - Event data
61
74
  */
62
- static send(eventType, actionName, data) {
75
+ static send(eventType: string, actionName: string, data?: Record<string, any>): boolean | undefined {
63
76
  const enrichedData = {
64
77
  actionName,
65
78
  ...data,
66
-
79
+
67
80
  };
68
-
81
+
69
82
  return recordEvent(eventType, enrichedData);
70
83
  }
71
84
 
@@ -77,7 +90,7 @@ class Core {
77
90
  *
78
91
  * @param {object} att attributes to be sent along the error.
79
92
  */
80
- static sendError(att) {
93
+ static sendError(att?: Record<string, any>): boolean | undefined {
81
94
  return recordEvent("VideoErrorAction", {
82
95
  actionName: "ERROR",
83
96
  ...att
@@ -86,8 +99,8 @@ class Core {
86
99
 
87
100
  }
88
101
 
89
- let trackers = [];
90
- let isErrorShown = false;
102
+ let trackers: TrackerLike[] = [];
103
+ let isErrorShown: boolean = false;
91
104
 
92
105
  /**
93
106
  * Enhanced event handler with error handling and performance monitoring.
@@ -95,11 +108,11 @@ let isErrorShown = false;
95
108
  * @private
96
109
  * @param {Event} e Event
97
110
  */
98
- function eventHandler(e) {
111
+ function eventHandler(e: any) {
99
112
  try {
100
113
  let data = cleanData(e.data);
101
-
102
- if (Log.level <= Log.Levels.DEBUG) {
114
+
115
+ if ((Log.level as any) <= Log.Levels.DEBUG) {
103
116
  Log.notice("Sent", e.type, data);
104
117
  } else {
105
118
  Log.notice("Sent", e.type);
@@ -108,7 +121,7 @@ function eventHandler(e) {
108
121
  // Send event without priority discrimination
109
122
  Core.send(e.eventType, e.type, data);
110
123
 
111
- } catch (error) {
124
+ } catch (error: any) {
112
125
  Log.error("Error in event handler:", error.message);
113
126
  }
114
127
  }
@@ -119,8 +132,8 @@ function eventHandler(e) {
119
132
  * @param {Object} data Data to clean
120
133
  * @returns {Object} Cleaned object
121
134
  */
122
- function cleanData(data) {
123
- let ret = {};
135
+ function cleanData(data: Record<string, any>): Record<string, any> {
136
+ let ret: Record<string, any> = {};
124
137
  for (let i in data) {
125
138
  if (data[i] !== null && typeof data[i] !== "undefined") ret[i] = data[i];
126
139
  }
@@ -1,3 +1,12 @@
1
+ export interface TrackerEventPayload {
2
+ eventType: string;
3
+ type: string;
4
+ data: Record<string, unknown>;
5
+ target: Emitter;
6
+ }
7
+
8
+ export type ListenerCallback = (payload: TrackerEventPayload) => void;
9
+
1
10
  /**
2
11
  * This base class implements a basic behavior of listeners and events. Extend this object to have
3
12
  * this feature built-in inside your classes.
@@ -5,6 +14,8 @@
5
14
  * @class Emitter
6
15
  */
7
16
  class Emitter {
17
+ _listeners?: Record<string, ListenerCallback[]>;
18
+
8
19
  /**
9
20
  * Sets a listener to a given event. Use {@link emit} to trigger those events.
10
21
  * Pass '*' to listen ALL events.
@@ -13,7 +24,7 @@ class Emitter {
13
24
  * @param {function} callback Callback of the event. Receives event and data.
14
25
  * @return this
15
26
  */
16
- on(event, callback) {
27
+ on(event: string, callback: ListenerCallback): this | undefined {
17
28
  this._listeners = this._listeners || {};
18
29
  if (typeof callback === "function") {
19
30
  this._listeners[event] = this._listeners[event] || [];
@@ -29,7 +40,7 @@ class Emitter {
29
40
  * @param {function} callback Callback of the event.
30
41
  * @return this
31
42
  */
32
- off(event, callback) {
43
+ off(event: string, callback: ListenerCallback): this {
33
44
  this._listeners = this._listeners || {};
34
45
 
35
46
  if (this._listeners[event]) {
@@ -48,7 +59,7 @@ class Emitter {
48
59
  * @param {object} [data] Custom data to be sent to the callbacks.
49
60
  * @return this
50
61
  */
51
- emit(eventType, event, data) {
62
+ emit(eventType: string, event: string, data?: Record<string, unknown>): this {
52
63
  this._listeners = this._listeners || {};
53
64
  data = data || {};
54
65
 
@@ -57,7 +68,7 @@ class Emitter {
57
68
  callback.call(this, {
58
69
  eventType,
59
70
  type: event,
60
- data: data,
71
+ data: data as Record<string, unknown>,
61
72
  target: this,
62
73
  });
63
74
  });
@@ -68,7 +79,7 @@ class Emitter {
68
79
  callback.call(this, {
69
80
  eventType,
70
81
  type: event,
71
- data: data,
82
+ data: data as Record<string, unknown>,
72
83
  target: this,
73
84
  });
74
85
  });