@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.
- package/CHANGELOG.md +20 -2
- package/README.md +13 -0
- package/__mock__.js +8 -4
- package/dist/cjs/browser/index.js +1 -1
- package/dist/cjs/browser/index.js.LICENSE.txt +1 -1
- package/dist/cjs/browser/index.js.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.LICENSE.txt +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/vega/index.js +1 -1
- package/dist/cjs/vega/index.js.LICENSE.txt +1 -1
- package/dist/cjs/vega/index.js.map +1 -1
- package/dist/esm/browser/index.js +1 -1
- package/dist/esm/browser/index.js.LICENSE.txt +1 -1
- package/dist/esm/browser/index.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.LICENSE.txt +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/vega/index.js +1 -1
- package/dist/esm/vega/index.js.LICENSE.txt +1 -1
- package/dist/esm/vega/index.js.map +1 -1
- package/dist/types/browser/agent.d.ts +46 -0
- package/dist/types/browser/harvestScheduler.d.ts +125 -0
- package/dist/types/browser/index.d.ts +34 -0
- package/dist/types/chrono.d.ts +52 -0
- package/dist/types/connectedDevice/connectedDeviceAgent.d.ts +63 -0
- package/dist/types/connectedDevice/connectedDeviceConstants.d.ts +51 -0
- package/dist/types/connectedDevice/connectedDeviceHarvester.d.ts +198 -0
- package/dist/types/connectedDevice/index.d.ts +30 -0
- package/dist/types/constants.d.ts +30 -0
- package/dist/types/core.d.ts +54 -0
- package/dist/types/emitter.d.ts +42 -0
- package/dist/types/eventAggregator.d.ts +97 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/log.d.ts +61 -0
- package/dist/types/obfuscate.d.ts +13 -0
- package/dist/types/optimizedHttpClient.d.ts +83 -0
- package/dist/types/recordEvent.d.ts +30 -0
- package/dist/types/retryQueueHandler.d.ts +46 -0
- package/dist/types/tracker.d.ts +176 -0
- package/dist/types/utils/eventBuilder.d.ts +69 -0
- package/dist/types/utils/harvestTimer.d.ts +45 -0
- package/dist/types/utils/index.d.ts +38 -0
- package/dist/types/utils/qoeFilters.d.ts +88 -0
- package/dist/types/videoConfiguration.d.ts +90 -0
- package/dist/types/videotracker.d.ts +366 -0
- package/dist/types/videotrackerstate.d.ts +228 -0
- package/dist/umd/nrvideo.min.js +1 -1
- package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
- package/dist/umd/nrvideo.min.js.map +1 -1
- package/package.json +15 -2
- package/src/browser/{agent.js → agent.ts} +21 -16
- package/src/browser/{harvestScheduler.js → harvestScheduler.ts} +51 -24
- package/src/{chrono.js → chrono.ts} +29 -24
- package/src/connectedDevice/{connectedDeviceAgent.js → connectedDeviceAgent.ts} +14 -10
- package/src/connectedDevice/{connectedDeviceConstants.js → connectedDeviceConstants.ts} +5 -3
- package/src/connectedDevice/{connectedDeviceHarvester.js → connectedDeviceHarvester.ts} +71 -34
- package/src/constants.ts +62 -0
- package/src/{core.js → core.ts} +31 -18
- package/src/{emitter.js → emitter.ts} +16 -5
- package/src/{eventAggregator.js → eventAggregator.ts} +33 -19
- package/src/global.d.ts +25 -0
- package/src/{log.js → log.ts} +23 -17
- package/src/{obfuscate.js → obfuscate.ts} +6 -1
- package/src/{optimizedHttpClient.js → optimizedHttpClient.ts} +37 -13
- package/src/{recordEvent.js → recordEvent.ts} +11 -9
- package/src/{retryQueueHandler.js → retryQueueHandler.ts} +17 -12
- package/src/{tracker.js → tracker.ts} +56 -23
- package/src/utils/{eventBuilder.js → eventBuilder.ts} +31 -14
- package/src/utils/{harvestTimer.js → harvestTimer.ts} +21 -4
- package/src/utils/{index.js → index.ts} +14 -14
- package/src/utils/{qoeFilters.js → qoeFilters.ts} +19 -8
- package/src/{videoConfiguration.js → videoConfiguration.ts} +44 -10
- package/src/{videotracker.js → videotracker.ts} +119 -78
- package/src/{videotrackerstate.js → videotrackerstate.ts} +113 -49
- package/src/constants.js +0 -63
- /package/src/browser/{index.js → index.ts} +0 -0
- /package/src/connectedDevice/{index.js → index.ts} +0 -0
- /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();
|
package/src/constants.ts
ADDED
|
@@ -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;
|
package/src/{core.js → core.ts}
RENAMED
|
@@ -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
|
});
|