@newrelic/video-core 5.0.2 → 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 +11 -0
- package/README.md +13 -0
- 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 +14 -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
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
import Log from "./log";
|
|
2
2
|
import Constants from "./constants";
|
|
3
3
|
import { dataSize } from "./utils";
|
|
4
|
+
import { EventAttributes } from "./utils/eventBuilder";
|
|
4
5
|
|
|
5
6
|
const { MAX_PAYLOAD_SIZE, MAX_EVENTS_PER_BATCH } = Constants;
|
|
6
7
|
|
|
8
|
+
export type SmartHarvestCallback = (reason: "overflow" | "smart", percentage: number) => void;
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* Enhanced event buffer that manages video events with unified priority handling
|
|
9
12
|
* and automatic size management. All events are treated with equal priority
|
|
10
13
|
* unless explicitly specified otherwise.
|
|
11
14
|
*/
|
|
12
15
|
export class NrVideoEventAggregator {
|
|
16
|
+
buffer: EventAttributes[];
|
|
17
|
+
maxPayloadSize: number;
|
|
18
|
+
maxEventsPerBatch: number;
|
|
19
|
+
currentPayloadSize: number;
|
|
20
|
+
totalEvents: number;
|
|
21
|
+
smartHarvestPayloadThreshold: number;
|
|
22
|
+
overflowPayloadThreshold: number;
|
|
23
|
+
smartHarvestEventThreshold: number;
|
|
24
|
+
overflowEventThreshold: number;
|
|
25
|
+
onSmartHarvestTrigger: SmartHarvestCallback | null;
|
|
26
|
+
|
|
13
27
|
constructor() {
|
|
14
28
|
// Simplified to single priority queue for equal treatment
|
|
15
29
|
|
|
@@ -39,7 +53,7 @@ export class NrVideoEventAggregator {
|
|
|
39
53
|
* @param {object} eventObject - The event object to add or use as replacement. Should contain an actionName property.
|
|
40
54
|
* @returns {boolean} True if the operation succeeded, false if an error occurred
|
|
41
55
|
*/
|
|
42
|
-
addOrReplaceByActionName(actionName, eventObject) {
|
|
56
|
+
addOrReplaceByActionName(actionName: string, eventObject: EventAttributes): boolean {
|
|
43
57
|
const i = this.buffer.findIndex(e => e.actionName === actionName);
|
|
44
58
|
|
|
45
59
|
try {
|
|
@@ -49,7 +63,7 @@ export class NrVideoEventAggregator {
|
|
|
49
63
|
this.add(eventObject, i);
|
|
50
64
|
}
|
|
51
65
|
return true;
|
|
52
|
-
} catch (error) {
|
|
66
|
+
} catch (error: any) {
|
|
53
67
|
Log.error("Failed to set or replace the event to buffer:", error.message);
|
|
54
68
|
return false;
|
|
55
69
|
}
|
|
@@ -63,7 +77,7 @@ export class NrVideoEventAggregator {
|
|
|
63
77
|
* @param {object} eventObject - The event object to add or use as replacement.
|
|
64
78
|
* @returns {boolean} True if the operation succeeded, false if an error occurred
|
|
65
79
|
*/
|
|
66
|
-
addOrReplaceByActionNameAndViewId(actionName, viewId, eventObject) {
|
|
80
|
+
addOrReplaceByActionNameAndViewId(actionName: string, viewId: string, eventObject: EventAttributes): boolean {
|
|
67
81
|
const i = this.buffer.findIndex(
|
|
68
82
|
e => e.actionName === actionName && e.viewId === viewId
|
|
69
83
|
);
|
|
@@ -74,7 +88,7 @@ export class NrVideoEventAggregator {
|
|
|
74
88
|
this.add(eventObject, i);
|
|
75
89
|
}
|
|
76
90
|
return true;
|
|
77
|
-
} catch (error) {
|
|
91
|
+
} catch (error: any) {
|
|
78
92
|
Log.error("Failed to set or replace the event to buffer:", error.message);
|
|
79
93
|
return false;
|
|
80
94
|
}
|
|
@@ -86,7 +100,7 @@ export class NrVideoEventAggregator {
|
|
|
86
100
|
* @param {string} viewId
|
|
87
101
|
* @returns {object|null}
|
|
88
102
|
*/
|
|
89
|
-
findByActionNameAndViewId(actionName, viewId) {
|
|
103
|
+
findByActionNameAndViewId(actionName: string, viewId: string): EventAttributes | null {
|
|
90
104
|
const event = this.buffer.find(
|
|
91
105
|
e => e.actionName === actionName && e.viewId === viewId
|
|
92
106
|
);
|
|
@@ -98,7 +112,7 @@ export class NrVideoEventAggregator {
|
|
|
98
112
|
* @param {string} actionName
|
|
99
113
|
* @returns {object|null}
|
|
100
114
|
*/
|
|
101
|
-
findByActionName(actionName) {
|
|
115
|
+
findByActionName(actionName: string): EventAttributes | null {
|
|
102
116
|
const event = this.buffer.find(e => e.actionName === actionName);
|
|
103
117
|
return event || null;
|
|
104
118
|
}
|
|
@@ -109,10 +123,10 @@ export class NrVideoEventAggregator {
|
|
|
109
123
|
* @param {object} eventObject - The event to add
|
|
110
124
|
* @param {number} index - index at which the event should be replaced with
|
|
111
125
|
*/
|
|
112
|
-
add(eventObject, index) {
|
|
126
|
+
add(eventObject: EventAttributes, index?: number): boolean {
|
|
113
127
|
try {
|
|
114
128
|
// Calculate event payload size
|
|
115
|
-
const eventSize = dataSize(eventObject);
|
|
129
|
+
const eventSize = dataSize(eventObject) ?? 0;
|
|
116
130
|
|
|
117
131
|
// Check if we need to make room based on EITHER payload size OR event count limits
|
|
118
132
|
const wouldExceedPayload =
|
|
@@ -126,7 +140,7 @@ export class NrVideoEventAggregator {
|
|
|
126
140
|
|
|
127
141
|
if(index !== undefined && index !== null && index > -1) {
|
|
128
142
|
// replace in unified buffer
|
|
129
|
-
const previousPayloadSize = dataSize(this.buffer[index]);
|
|
143
|
+
const previousPayloadSize = dataSize(this.buffer[index]) ?? 0;
|
|
130
144
|
this.buffer[index] = eventObject;
|
|
131
145
|
// Updating the payload size for the replaced event
|
|
132
146
|
this.currentPayloadSize += eventSize - previousPayloadSize;
|
|
@@ -141,7 +155,7 @@ export class NrVideoEventAggregator {
|
|
|
141
155
|
this.checkSmartHarvestTrigger();
|
|
142
156
|
|
|
143
157
|
return true;
|
|
144
|
-
} catch (error) {
|
|
158
|
+
} catch (error: any) {
|
|
145
159
|
Log.error("Failed to add event to buffer:", error.message);
|
|
146
160
|
return false;
|
|
147
161
|
}
|
|
@@ -154,7 +168,7 @@ export class NrVideoEventAggregator {
|
|
|
154
168
|
* - 90% of payload size (900KB) OR 90% of event count (900 events)
|
|
155
169
|
* @private
|
|
156
170
|
*/
|
|
157
|
-
checkSmartHarvestTrigger() {
|
|
171
|
+
checkSmartHarvestTrigger(): void {
|
|
158
172
|
const payloadPercentage = this.currentPayloadSize / this.maxPayloadSize;
|
|
159
173
|
const eventPercentage = this.totalEvents / this.maxEventsPerBatch;
|
|
160
174
|
|
|
@@ -209,7 +223,7 @@ export class NrVideoEventAggregator {
|
|
|
209
223
|
* Sets the callback function for smart harvest triggers.
|
|
210
224
|
* @param {Function} callback - Function to call when smart harvest is triggered
|
|
211
225
|
*/
|
|
212
|
-
setSmartHarvestCallback(callback) {
|
|
226
|
+
setSmartHarvestCallback(callback: SmartHarvestCallback): void {
|
|
213
227
|
this.onSmartHarvestTrigger = callback;
|
|
214
228
|
}
|
|
215
229
|
|
|
@@ -218,7 +232,7 @@ export class NrVideoEventAggregator {
|
|
|
218
232
|
* No limits needed since buffer already manages size via makeRoom() and smart harvest triggers.
|
|
219
233
|
* @returns {Array} Array of events in order they were added
|
|
220
234
|
*/
|
|
221
|
-
drain() {
|
|
235
|
+
drain(): EventAttributes[] {
|
|
222
236
|
try {
|
|
223
237
|
// Drain ALL events - buffer size is already managed by makeRoom() and smart harvest
|
|
224
238
|
const events = this.buffer.splice(0);
|
|
@@ -228,7 +242,7 @@ export class NrVideoEventAggregator {
|
|
|
228
242
|
this.currentPayloadSize = 0;
|
|
229
243
|
|
|
230
244
|
return events;
|
|
231
|
-
} catch (error) {
|
|
245
|
+
} catch (error: any) {
|
|
232
246
|
Log.error("Failed to drain events from buffer:", error.message);
|
|
233
247
|
return [];
|
|
234
248
|
}
|
|
@@ -238,7 +252,7 @@ export class NrVideoEventAggregator {
|
|
|
238
252
|
* Checks if the buffer is empty.
|
|
239
253
|
* @returns {boolean} True if all buffers are empty
|
|
240
254
|
*/
|
|
241
|
-
isEmpty() {
|
|
255
|
+
isEmpty(): boolean {
|
|
242
256
|
return this.totalEvents === 0;
|
|
243
257
|
}
|
|
244
258
|
|
|
@@ -246,14 +260,14 @@ export class NrVideoEventAggregator {
|
|
|
246
260
|
* Gets the total number of events across all buffers.
|
|
247
261
|
* @returns {number} Total event count
|
|
248
262
|
*/
|
|
249
|
-
size() {
|
|
263
|
+
size(): number {
|
|
250
264
|
return this.totalEvents;
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
/**
|
|
254
268
|
* Clears the entire buffer.
|
|
255
269
|
*/
|
|
256
|
-
clear() {
|
|
270
|
+
clear(): void {
|
|
257
271
|
this.buffer = [];
|
|
258
272
|
this.totalEvents = 0;
|
|
259
273
|
}
|
|
@@ -263,7 +277,7 @@ export class NrVideoEventAggregator {
|
|
|
263
277
|
* Uses FIFO approach - removes the first (oldest) event.
|
|
264
278
|
* @private
|
|
265
279
|
*/
|
|
266
|
-
makeRoom(newEventSize) {
|
|
280
|
+
makeRoom(newEventSize: number): void {
|
|
267
281
|
// Before the while loop in makeRoom()
|
|
268
282
|
if (newEventSize > this.maxPayloadSize) {
|
|
269
283
|
Log.error("Event dropped: Event size exceeds maximum payload size.");
|
|
@@ -281,7 +295,7 @@ export class NrVideoEventAggregator {
|
|
|
281
295
|
const removed = this.buffer.shift(); // Remove the oldest event (FIFO)
|
|
282
296
|
|
|
283
297
|
// Recalculate size and count after removal
|
|
284
|
-
const removedSize = dataSize(removed);
|
|
298
|
+
const removedSize = dataSize(removed) ?? 0;
|
|
285
299
|
this.totalEvents--;
|
|
286
300
|
this.currentPayloadSize -= removedSize;
|
|
287
301
|
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient global augmentation for the two configuration slots the library
|
|
3
|
+
* writes at runtime — see videoConfiguration.ts::initializeGlobalConfig,
|
|
4
|
+
* which is the single source of truth for these shapes.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
NrVideoBrowserInfo,
|
|
9
|
+
NrVideoBrowserConfig,
|
|
10
|
+
NrVideoCdInfo,
|
|
11
|
+
NrVideoCdConfig,
|
|
12
|
+
} from "./videoConfiguration";
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
NRVIDEO?: { info: NrVideoBrowserInfo; config: NrVideoBrowserConfig };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// eslint-disable-next-line no-var
|
|
20
|
+
var __NRVIDEO_CD__:
|
|
21
|
+
| { info: NrVideoCdInfo; config: NrVideoCdConfig }
|
|
22
|
+
| undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {};
|
package/src/{log.js → log.ts}
RENAMED
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
* @static
|
|
6
6
|
*/
|
|
7
7
|
class Log {
|
|
8
|
+
static Levels: Record<string, number>;
|
|
9
|
+
static level: number | string;
|
|
10
|
+
static colorful: boolean;
|
|
11
|
+
static includeTime: boolean;
|
|
12
|
+
static prefix: string;
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Sends an error console log.
|
|
10
16
|
* @param {...any} [msg] Message to show
|
|
11
17
|
* @static
|
|
12
18
|
*/
|
|
13
|
-
static error(...msg) {
|
|
19
|
+
static error(...msg: any[]) {
|
|
14
20
|
_report(msg, Log.Levels.ERROR, "darkred");
|
|
15
21
|
}
|
|
16
22
|
|
|
@@ -20,7 +26,7 @@ class Log {
|
|
|
20
26
|
* @static
|
|
21
27
|
* @param {...any} msg Message to show
|
|
22
28
|
*/
|
|
23
|
-
static warn(...msg) {
|
|
29
|
+
static warn(...msg: any[]) {
|
|
24
30
|
_report(msg, Log.Levels.WARNING, "darkorange");
|
|
25
31
|
}
|
|
26
32
|
|
|
@@ -30,7 +36,7 @@ class Log {
|
|
|
30
36
|
* @static
|
|
31
37
|
* @param {...any} msg Message to show
|
|
32
38
|
*/
|
|
33
|
-
static notice(...msg) {
|
|
39
|
+
static notice(...msg: any[]) {
|
|
34
40
|
_report([].slice.call(arguments), Log.Levels.NOTICE, "darkcyan");
|
|
35
41
|
}
|
|
36
42
|
|
|
@@ -40,7 +46,7 @@ class Log {
|
|
|
40
46
|
* @static
|
|
41
47
|
* @param {...any} msg Message to show
|
|
42
48
|
*/
|
|
43
|
-
static debug(...msg) {
|
|
49
|
+
static debug(...msg: any[]) {
|
|
44
50
|
_report(msg, Log.Levels.DEBUG, "indigo");
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -63,12 +69,12 @@ class Log {
|
|
|
63
69
|
* @param {function} [report] Callback function called to report events.
|
|
64
70
|
* Default calls Log.debug()
|
|
65
71
|
*/
|
|
66
|
-
static debugCommonVideoEvents(o, extraEvents, report) {
|
|
72
|
+
static debugCommonVideoEvents(o: any, extraEvents?: any[], report?: (e: any) => void) {
|
|
67
73
|
try {
|
|
68
|
-
if (Log.level <= Log.Levels.DEBUG) {
|
|
74
|
+
if ((Log.level as any) <= Log.Levels.DEBUG) {
|
|
69
75
|
report =
|
|
70
76
|
report ||
|
|
71
|
-
function (e) {
|
|
77
|
+
function (e: any) {
|
|
72
78
|
Log.debug("Event: " + e.type);
|
|
73
79
|
};
|
|
74
80
|
|
|
@@ -186,7 +192,7 @@ Log.prefix = "[nrvideo]";
|
|
|
186
192
|
* @param {string} [color='darkgreen'] Color of the header
|
|
187
193
|
* @see {@link Log.level}
|
|
188
194
|
*/
|
|
189
|
-
function _report(msg, level, color) {
|
|
195
|
+
function _report(msg: any[], level?: number | string, color?: string) {
|
|
190
196
|
level = level || Log.Levels.NOTICE;
|
|
191
197
|
color = color || "darkcyan";
|
|
192
198
|
|
|
@@ -195,23 +201,23 @@ function _report(msg, level, color) {
|
|
|
195
201
|
prefix += _level2letter(level) + ":";
|
|
196
202
|
|
|
197
203
|
// Show messages in actual console if level is enought
|
|
198
|
-
if (Log.level <= level && level !== Log.Levels.SILENT) {
|
|
204
|
+
if ((Log.level as any) <= level && level !== Log.Levels.SILENT) {
|
|
199
205
|
if (
|
|
200
206
|
!Log.colorful ||
|
|
201
|
-
(typeof document !== "undefined" && document.documentMode)
|
|
207
|
+
(typeof document !== "undefined" && (document as any).documentMode)
|
|
202
208
|
) {
|
|
203
209
|
// document.documentMode exits only in IE
|
|
204
210
|
_plainReport(msg, prefix);
|
|
205
211
|
} else {
|
|
206
212
|
// choose log method
|
|
207
|
-
var logMethod;
|
|
213
|
+
var logMethod: (...args: any[]) => void;
|
|
208
214
|
if (level === Log.Levels.ERROR && console.error) {
|
|
209
215
|
logMethod = console.error;
|
|
210
216
|
} else if (level === Log.Levels.WARNING && console.warn) {
|
|
211
217
|
logMethod = console.warn;
|
|
212
218
|
} else if (level === Log.Levels.DEBUG && console.debug) {
|
|
213
219
|
// NOTE: for some reason console.debug doesn't work on CAF Receivers.
|
|
214
|
-
if (window.cast == undefined) {
|
|
220
|
+
if ((window as any).cast == undefined) {
|
|
215
221
|
logMethod = console.debug;
|
|
216
222
|
} else {
|
|
217
223
|
logMethod = console.log;
|
|
@@ -233,7 +239,7 @@ function _report(msg, level, color) {
|
|
|
233
239
|
* @private
|
|
234
240
|
* @return {string} Current time.
|
|
235
241
|
*/
|
|
236
|
-
function _getCurrentTime() {
|
|
242
|
+
function _getCurrentTime(): string {
|
|
237
243
|
var d = new Date();
|
|
238
244
|
var hh = ("0" + d.getDate()).slice(-2);
|
|
239
245
|
var mm = ("0" + d.getMinutes()).slice(-2);
|
|
@@ -249,7 +255,7 @@ function _getCurrentTime() {
|
|
|
249
255
|
* @param {(string|object|array)} msg Message string, object or array of messages.
|
|
250
256
|
* @param {string} prefix Prefix of the message.
|
|
251
257
|
*/
|
|
252
|
-
function _plainReport(msg, prefix) {
|
|
258
|
+
function _plainReport(msg: any, prefix: string) {
|
|
253
259
|
if (msg instanceof Array) {
|
|
254
260
|
for (var m in msg) {
|
|
255
261
|
_plainReport(msg[m], prefix);
|
|
@@ -264,7 +270,7 @@ function _plainReport(msg, prefix) {
|
|
|
264
270
|
}
|
|
265
271
|
}
|
|
266
272
|
|
|
267
|
-
const _letters = {
|
|
273
|
+
const _letters: Record<number, string> = {
|
|
268
274
|
4: "e", // Error
|
|
269
275
|
3: "w", // Warning
|
|
270
276
|
2: "n", // Notice
|
|
@@ -277,8 +283,8 @@ const _letters = {
|
|
|
277
283
|
* @private
|
|
278
284
|
* @param {sLog.Level} level Level of the message
|
|
279
285
|
*/
|
|
280
|
-
function _level2letter(level) {
|
|
281
|
-
return _letters[level];
|
|
286
|
+
function _level2letter(level: number | string): string {
|
|
287
|
+
return _letters[level as number];
|
|
282
288
|
}
|
|
283
289
|
|
|
284
290
|
/**
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import Log from "./log";
|
|
2
2
|
|
|
3
|
+
export interface ObfuscationRule {
|
|
4
|
+
regex: string | RegExp;
|
|
5
|
+
replacement: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Applies obfuscation rules to a JSON string before sending to the collector.
|
|
5
10
|
* Each rule replaces matches of `regex` with `replacement` in the string.
|
|
@@ -8,7 +13,7 @@ import Log from "./log";
|
|
|
8
13
|
* @param {Array<{regex: string|RegExp, replacement: string}>} rules - Obfuscation rules
|
|
9
14
|
* @returns {string} Obfuscated string
|
|
10
15
|
*/
|
|
11
|
-
export function applyObfuscationRules(jsonString, rules) {
|
|
16
|
+
export function applyObfuscationRules(jsonString: string, rules?: ObfuscationRule[]): string {
|
|
12
17
|
if (!rules || rules.length === 0) return jsonString;
|
|
13
18
|
|
|
14
19
|
return rules.reduce((str, rule) => {
|
|
@@ -2,6 +2,30 @@ import { shouldRetry } from "./utils";
|
|
|
2
2
|
import Log from "./log";
|
|
3
3
|
import { applyObfuscationRules } from "./obfuscate";
|
|
4
4
|
|
|
5
|
+
export interface HttpRequestOptions {
|
|
6
|
+
url: string;
|
|
7
|
+
payload: { body: any };
|
|
8
|
+
options?: { isFinalHarvest?: boolean };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface HttpResultCallback {
|
|
12
|
+
(result: { retry?: boolean; status: number; statusText?: string; error?: string }): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface InternalRequest {
|
|
16
|
+
url: string;
|
|
17
|
+
payload: { body: any };
|
|
18
|
+
options: { isFinalHarvest?: boolean };
|
|
19
|
+
callback: HttpResultCallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RequestResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
status: number;
|
|
25
|
+
statusText?: string;
|
|
26
|
+
error?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
5
29
|
/**
|
|
6
30
|
* Optimized HTTP client for video analytics data transmission with
|
|
7
31
|
* performance monitoring and efficient request handling.
|
|
@@ -16,7 +40,7 @@ export class OptimizedHttpClient {
|
|
|
16
40
|
* @param {Function} callback - Callback function for handling response
|
|
17
41
|
* @returns {Promise<void>}
|
|
18
42
|
*/
|
|
19
|
-
async send(requestOptions, callback) {
|
|
43
|
+
async send(requestOptions: HttpRequestOptions, callback: HttpResultCallback): Promise<void> {
|
|
20
44
|
const { url, payload, options = {} } = requestOptions;
|
|
21
45
|
|
|
22
46
|
try {
|
|
@@ -26,7 +50,7 @@ export class OptimizedHttpClient {
|
|
|
26
50
|
}
|
|
27
51
|
|
|
28
52
|
// Create request object
|
|
29
|
-
const request = {
|
|
53
|
+
const request: InternalRequest = {
|
|
30
54
|
url,
|
|
31
55
|
payload,
|
|
32
56
|
options,
|
|
@@ -35,7 +59,7 @@ export class OptimizedHttpClient {
|
|
|
35
59
|
|
|
36
60
|
// Execute request immediately
|
|
37
61
|
await this.executeRequest(request);
|
|
38
|
-
} catch (error) {
|
|
62
|
+
} catch (error: any) {
|
|
39
63
|
Log.error("Failed to send request:", error.message);
|
|
40
64
|
callback({ retry: false, status: 0, error: error.message });
|
|
41
65
|
}
|
|
@@ -46,7 +70,7 @@ export class OptimizedHttpClient {
|
|
|
46
70
|
* @param {object} request - Request object
|
|
47
71
|
* @private
|
|
48
72
|
*/
|
|
49
|
-
async executeRequest(request) {
|
|
73
|
+
async executeRequest(request: InternalRequest): Promise<void> {
|
|
50
74
|
const { url, payload, options, callback } = request;
|
|
51
75
|
const startTime = Date.now();
|
|
52
76
|
|
|
@@ -59,7 +83,7 @@ export class OptimizedHttpClient {
|
|
|
59
83
|
// Handle final harvest with sendBeacon
|
|
60
84
|
if (options.isFinalHarvest && navigator.sendBeacon) {
|
|
61
85
|
const success = await this.sendWithBeacon(url, requestBody);
|
|
62
|
-
const result = { success, status: success ? 204 : 0 };
|
|
86
|
+
const result: RequestResult = { success, status: success ? 204 : 0 };
|
|
63
87
|
this.handleRequestComplete(request, result, startTime);
|
|
64
88
|
return;
|
|
65
89
|
}
|
|
@@ -78,15 +102,15 @@ export class OptimizedHttpClient {
|
|
|
78
102
|
10000
|
|
79
103
|
);
|
|
80
104
|
|
|
81
|
-
const result = {
|
|
105
|
+
const result: RequestResult = {
|
|
82
106
|
success: response.ok,
|
|
83
107
|
status: response.status,
|
|
84
108
|
statusText: response.statusText,
|
|
85
109
|
};
|
|
86
110
|
|
|
87
111
|
this.handleRequestComplete(request, result, startTime);
|
|
88
|
-
} catch (error) {
|
|
89
|
-
const result = {
|
|
112
|
+
} catch (error: any) {
|
|
113
|
+
const result: RequestResult = {
|
|
90
114
|
success: false,
|
|
91
115
|
status: 0,
|
|
92
116
|
error: error.message,
|
|
@@ -104,7 +128,7 @@ export class OptimizedHttpClient {
|
|
|
104
128
|
* @param {string} endpoint - The endpoint that was used for the request
|
|
105
129
|
* @private
|
|
106
130
|
*/
|
|
107
|
-
handleRequestComplete(request, result) {
|
|
131
|
+
handleRequestComplete(request: InternalRequest, result: RequestResult, startTime?: number): void {
|
|
108
132
|
const { callback } = request;
|
|
109
133
|
|
|
110
134
|
// Use smart retry logic based on HTTP status codes
|
|
@@ -127,10 +151,10 @@ export class OptimizedHttpClient {
|
|
|
127
151
|
* @returns {Promise<boolean>} True if successful
|
|
128
152
|
* @private
|
|
129
153
|
*/
|
|
130
|
-
async sendWithBeacon(url, body) {
|
|
154
|
+
async sendWithBeacon(url: string, body: string): Promise<boolean> {
|
|
131
155
|
try {
|
|
132
156
|
return navigator.sendBeacon(url, body);
|
|
133
|
-
} catch (error) {
|
|
157
|
+
} catch (error: any) {
|
|
134
158
|
Log.warn("sendBeacon failed, falling back to fetch:", error.message);
|
|
135
159
|
return false;
|
|
136
160
|
}
|
|
@@ -145,7 +169,7 @@ export class OptimizedHttpClient {
|
|
|
145
169
|
* @returns {Promise<Response>} Fetch response
|
|
146
170
|
* @private
|
|
147
171
|
*/
|
|
148
|
-
async fetchWithTimeout(url, options, timeout) {
|
|
172
|
+
async fetchWithTimeout(url: string, options: RequestInit, timeout: number): Promise<Response> {
|
|
149
173
|
const controller = new AbortController();
|
|
150
174
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
151
175
|
|
|
@@ -156,7 +180,7 @@ export class OptimizedHttpClient {
|
|
|
156
180
|
});
|
|
157
181
|
clearTimeout(timeoutId);
|
|
158
182
|
return response;
|
|
159
|
-
} catch (error) {
|
|
183
|
+
} catch (error: any) {
|
|
160
184
|
clearTimeout(timeoutId);
|
|
161
185
|
if (error.name === "AbortError") {
|
|
162
186
|
throw new Error(`Request timeout after ${timeout}ms`);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import Constants from "./constants
|
|
2
|
-
import Log from "./log
|
|
3
|
-
import { dispatchRecordEvent } from "./utils/eventBuilder";
|
|
1
|
+
import Constants from "./constants";
|
|
2
|
+
import Log from "./log";
|
|
3
|
+
import { dispatchRecordEvent, EventAttributes, Harvester } from "./utils/eventBuilder";
|
|
4
|
+
|
|
5
|
+
export type { EventAttributes, Harvester };
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Registry-based recordEvent (split build).
|
|
@@ -8,7 +10,7 @@ import { dispatchRecordEvent } from "./utils/eventBuilder";
|
|
|
8
10
|
* No static harvester imports. Each harvester self-registers under a key
|
|
9
11
|
* (`'Browser'` or `'Vega'`) at module load time via `registerHarvester`.
|
|
10
12
|
*/
|
|
11
|
-
const harvesters = Object.create(null);
|
|
13
|
+
const harvesters: Record<string, Harvester> = Object.create(null);
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Register a harvester implementation under a routing key.
|
|
@@ -18,7 +20,7 @@ const harvesters = Object.create(null);
|
|
|
18
20
|
* @param {string} key Routing key matching `attributes.src`.
|
|
19
21
|
* @param {{ addEvent: function }} harvester
|
|
20
22
|
*/
|
|
21
|
-
export function registerHarvester(key, harvester) {
|
|
23
|
+
export function registerHarvester(key: string, harvester: Harvester): void {
|
|
22
24
|
harvesters[key] = harvester;
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -39,11 +41,11 @@ export function registerHarvester(key, harvester) {
|
|
|
39
41
|
* @param {string} key
|
|
40
42
|
* @returns {{ addEvent: function }|undefined}
|
|
41
43
|
*/
|
|
42
|
-
export function getRegisteredHarvester(key) {
|
|
44
|
+
export function getRegisteredHarvester(key: string): Harvester | undefined {
|
|
43
45
|
return harvesters[key];
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
export function recordEvent(eventType, attributes = {}) {
|
|
48
|
+
export function recordEvent(eventType: string, attributes: EventAttributes = {}): boolean | undefined {
|
|
47
49
|
try {
|
|
48
50
|
if (!Constants.VALID_EVENT_TYPES.includes(eventType)) {
|
|
49
51
|
Log.warn("Invalid event type provided to recordEvent", { eventType });
|
|
@@ -67,10 +69,10 @@ export function recordEvent(eventType, attributes = {}) {
|
|
|
67
69
|
: w?.NRVIDEO?.config?.qoeAggregate;
|
|
68
70
|
|
|
69
71
|
return dispatchRecordEvent(
|
|
70
|
-
eventType, attributes, info, harvester, qoeEnabled,
|
|
72
|
+
eventType, attributes, info, harvester, !!qoeEnabled,
|
|
71
73
|
{ addTimeSinceLoad: !isVega }
|
|
72
74
|
);
|
|
73
|
-
} catch (error) {
|
|
75
|
+
} catch (error: any) {
|
|
74
76
|
Log.error("Failed to record event:", error.message);
|
|
75
77
|
return false;
|
|
76
78
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Log from "./log";
|
|
2
2
|
import { dataSize } from "./utils";
|
|
3
3
|
import Constants from "./constants";
|
|
4
|
+
import { EventAttributes } from "./utils/eventBuilder";
|
|
4
5
|
|
|
5
6
|
const { MAX_PAYLOAD_SIZE, MAX_EVENTS_PER_BATCH } = Constants;
|
|
6
7
|
|
|
@@ -9,6 +10,10 @@ const { MAX_PAYLOAD_SIZE, MAX_EVENTS_PER_BATCH } = Constants;
|
|
|
9
10
|
* backoff strategies, and persistent storage capabilities.
|
|
10
11
|
*/
|
|
11
12
|
export class RetryQueueHandler {
|
|
13
|
+
retryQueue: EventAttributes[];
|
|
14
|
+
maxQueueSize: number;
|
|
15
|
+
maxQueueSizeBytes: number;
|
|
16
|
+
|
|
12
17
|
constructor() {
|
|
13
18
|
this.retryQueue = [];
|
|
14
19
|
this.maxQueueSize = MAX_EVENTS_PER_BATCH; // Max 1000 events
|
|
@@ -19,7 +24,7 @@ export class RetryQueueHandler {
|
|
|
19
24
|
* Adds failed events to the retry queue for retry processing.
|
|
20
25
|
* @param {Array|object} events - Failed event(s) to add to retry queue
|
|
21
26
|
*/
|
|
22
|
-
addFailedEvents(events) {
|
|
27
|
+
addFailedEvents(events: EventAttributes | EventAttributes[]): void {
|
|
23
28
|
try {
|
|
24
29
|
const eventsArray = Array.isArray(events) ? events : [events];
|
|
25
30
|
|
|
@@ -34,15 +39,15 @@ export class RetryQueueHandler {
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
// Check queue memory size and make room if necessary
|
|
37
|
-
const eventSize = dataSize(event);
|
|
38
|
-
while (dataSize(this.retryQueue) + eventSize > this.maxQueueSizeBytes) {
|
|
42
|
+
const eventSize = dataSize(event) ?? 0;
|
|
43
|
+
while ((dataSize(this.retryQueue) ?? 0) + eventSize > this.maxQueueSizeBytes) {
|
|
39
44
|
this.evictOldestEvent();
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
// Store event directly - no wrapper needed
|
|
43
48
|
this.retryQueue.push({ ...event });
|
|
44
49
|
}
|
|
45
|
-
} catch (err) {
|
|
50
|
+
} catch (err: any) {
|
|
46
51
|
Log.error("Failed to add events to retry queue:", err.message);
|
|
47
52
|
}
|
|
48
53
|
}
|
|
@@ -53,7 +58,7 @@ export class RetryQueueHandler {
|
|
|
53
58
|
* @param {string} reason - Reason for discarding
|
|
54
59
|
* @private
|
|
55
60
|
*/
|
|
56
|
-
discardEvent(event, reason) {
|
|
61
|
+
discardEvent(event: EventAttributes, reason: string): void {
|
|
57
62
|
Log.warn(`Discarded event`, {
|
|
58
63
|
reason,
|
|
59
64
|
eventType: event.eventType,
|
|
@@ -64,10 +69,10 @@ export class RetryQueueHandler {
|
|
|
64
69
|
* Evicts the oldest event from the queue to make room.
|
|
65
70
|
* @private
|
|
66
71
|
*/
|
|
67
|
-
evictOldestEvent() {
|
|
72
|
+
evictOldestEvent(): void {
|
|
68
73
|
if (this.retryQueue.length > 0) {
|
|
69
74
|
const oldest = this.retryQueue.shift();
|
|
70
|
-
this.discardEvent(oldest, "Queue full - evicted oldest");
|
|
75
|
+
this.discardEvent(oldest as EventAttributes, "Queue full - evicted oldest");
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -78,8 +83,8 @@ export class RetryQueueHandler {
|
|
|
78
83
|
* @param {number} availableEventCount - Available event count
|
|
79
84
|
* @returns {Array} Array of events that fit within limits
|
|
80
85
|
*/
|
|
81
|
-
getRetryEventsToFit(availableSpace, availableEventCount) {
|
|
82
|
-
const retryEvents = [];
|
|
86
|
+
getRetryEventsToFit(availableSpace: number, availableEventCount: number): EventAttributes[] {
|
|
87
|
+
const retryEvents: EventAttributes[] = [];
|
|
83
88
|
let usedSpace = 0;
|
|
84
89
|
let eventCount = 0;
|
|
85
90
|
|
|
@@ -90,7 +95,7 @@ export class RetryQueueHandler {
|
|
|
90
95
|
|
|
91
96
|
if (eventCount >= availableEventCount) break;
|
|
92
97
|
|
|
93
|
-
const eventSize = dataSize(event);
|
|
98
|
+
const eventSize = dataSize(event) ?? 0;
|
|
94
99
|
if (usedSpace + eventSize > availableSpace) break;
|
|
95
100
|
|
|
96
101
|
// Add to beginning of retryEvents to maintain chronological order (oldest first)
|
|
@@ -109,14 +114,14 @@ export class RetryQueueHandler {
|
|
|
109
114
|
* Gets the current retry queue size.
|
|
110
115
|
* @returns {number} Queue size
|
|
111
116
|
*/
|
|
112
|
-
getQueueSize() {
|
|
117
|
+
getQueueSize(): number {
|
|
113
118
|
return this.retryQueue.length;
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
/**
|
|
117
122
|
* Clears the retry queue.
|
|
118
123
|
*/
|
|
119
|
-
clear() {
|
|
124
|
+
clear(): void {
|
|
120
125
|
this.retryQueue = [];
|
|
121
126
|
}
|
|
122
127
|
}
|