@newrelic/video-core 3.2.0-beta-1 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -3
- package/README.md +17 -39
- 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/esm/index.js +1 -1
- package/dist/esm/index.js.LICENSE.txt +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/umd/nrvideo.min.js +1 -1
- package/dist/umd/nrvideo.min.js.LICENSE.txt +3 -1
- package/dist/umd/nrvideo.min.js.map +1 -1
- package/package.json +6 -3
- package/src/agent.js +74 -5
- package/src/constants.js +15 -13
- package/src/core.js +59 -15
- package/src/eventAggregator.js +189 -37
- package/src/harvestScheduler.js +416 -0
- package/src/index.js +21 -0
- package/src/optimizedHttpClient.js +165 -0
- package/src/recordEvent.js +23 -50
- package/src/retryQueueHandler.js +124 -0
- package/src/tracker.js +23 -6
- package/src/utils.js +124 -63
- package/src/videoConfiguration.js +113 -0
- package/src/videotrackerstate.js +25 -1
- package/src/authConfiguration.js +0 -138
- package/src/harvester.js +0 -171
package/src/authConfiguration.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import Constants from "./constants";
|
|
2
|
-
|
|
3
|
-
const { COLLECTOR } = Constants;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Validates and initializes New Relic video tracking information.
|
|
7
|
-
* @param {object} info - The options object containing authentication information.
|
|
8
|
-
* @param {string} info.licenseKey - The New Relic license key.
|
|
9
|
-
* @param {string} [info.appName] - The name of the application (required if no applicationID).
|
|
10
|
-
* @param {string} [info.region] - The region for the New Relic collector (required if no beacon).
|
|
11
|
-
* @param {string} [info.beacon] - Custom beacon URL (optional, overrides region-based beacon).
|
|
12
|
-
* @param {string} [info.sa] - Security attributes (optional).
|
|
13
|
-
* @param {string} [info.applicationID] - Application ID for beacon-based configuration (optional).
|
|
14
|
-
* @returns {boolean} True if configuration was set successfully, false otherwise.
|
|
15
|
-
* @throws {Error} Throws error for invalid configuration parameters.
|
|
16
|
-
*/
|
|
17
|
-
export function setAuthConfig(info) {
|
|
18
|
-
try {
|
|
19
|
-
// Input validation
|
|
20
|
-
if (!info || typeof info !== "object" || Array.isArray(info)) {
|
|
21
|
-
throw new Error("setAuthConfig: info parameter must be a valid object");
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (isAuthorised(info)) {
|
|
25
|
-
const { licenseKey, appName, region, beacon, sa, applicationID } = info;
|
|
26
|
-
|
|
27
|
-
// Initialize NRVIDEO global object
|
|
28
|
-
window.NRVIDEO = window.NRVIDEO || {};
|
|
29
|
-
|
|
30
|
-
// Determine beacon URL with fallback
|
|
31
|
-
let beaconUrl = beacon;
|
|
32
|
-
if (!beaconUrl && region) {
|
|
33
|
-
if (!COLLECTOR[region]) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
`setAuthConfig: Invalid region '${region}'. Valid regions: ${Object.keys(
|
|
36
|
-
COLLECTOR
|
|
37
|
-
).join(", ")}`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
beaconUrl = COLLECTOR[region];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
window.NRVIDEO.info = {
|
|
44
|
-
beacon: beaconUrl,
|
|
45
|
-
licenseKey,
|
|
46
|
-
applicationID: applicationID || null,
|
|
47
|
-
appName: appName || null,
|
|
48
|
-
sa: sa || 0,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return true;
|
|
52
|
-
} else {
|
|
53
|
-
const validationError = getValidationError(info);
|
|
54
|
-
throw new Error(`setAuthConfig: ${validationError}`);
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.error("setAuthConfig:", error.message);
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Checks if the provided information contains valid authentication parameters.
|
|
64
|
-
* @param {object} info - The options object.
|
|
65
|
-
* @returns {boolean} True if authorized, false otherwise.
|
|
66
|
-
*/
|
|
67
|
-
function isAuthorised(info) {
|
|
68
|
-
if (!info || typeof info !== "object") {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const { licenseKey, appName, region, applicationID, beacon } = info;
|
|
73
|
-
|
|
74
|
-
// License key is always required
|
|
75
|
-
if (
|
|
76
|
-
!licenseKey ||
|
|
77
|
-
typeof licenseKey !== "string" ||
|
|
78
|
-
licenseKey.trim().length === 0
|
|
79
|
-
) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Two valid configuration modes:
|
|
84
|
-
// 1. applicationID + beacon (for direct beacon configuration)
|
|
85
|
-
// 2. appName + region (for region-based beacon resolution)
|
|
86
|
-
if (applicationID) {
|
|
87
|
-
return !!(beacon && typeof beacon === "string" && beacon.trim().length > 0);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return !!(
|
|
91
|
-
appName &&
|
|
92
|
-
typeof appName === "string" &&
|
|
93
|
-
appName.trim().length > 0 &&
|
|
94
|
-
region &&
|
|
95
|
-
typeof region === "string" &&
|
|
96
|
-
region.trim().length > 0
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Provides detailed validation error message for debugging.
|
|
102
|
-
* @param {object} info - The options object.
|
|
103
|
-
* @returns {string} Detailed error message.
|
|
104
|
-
*/
|
|
105
|
-
function getValidationError(info) {
|
|
106
|
-
if (!info || typeof info !== "object") {
|
|
107
|
-
return "info parameter must be a valid object";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const { licenseKey, appName, region, applicationID, beacon } = info;
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
!licenseKey ||
|
|
114
|
-
typeof licenseKey !== "string" ||
|
|
115
|
-
licenseKey.trim().length === 0
|
|
116
|
-
) {
|
|
117
|
-
return "licenseKey is required and must be a non-empty string";
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (applicationID) {
|
|
121
|
-
if (!beacon || typeof beacon !== "string" || beacon.trim().length === 0) {
|
|
122
|
-
return "beacon URL is required when using applicationID";
|
|
123
|
-
}
|
|
124
|
-
} else {
|
|
125
|
-
if (
|
|
126
|
-
!appName ||
|
|
127
|
-
typeof appName !== "string" ||
|
|
128
|
-
appName.trim().length === 0
|
|
129
|
-
) {
|
|
130
|
-
return "appName is required when not using applicationID";
|
|
131
|
-
}
|
|
132
|
-
if (!region || typeof region !== "string" || region.trim().length === 0) {
|
|
133
|
-
return "region is required when not using applicationID";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return "configuration validation failed";
|
|
138
|
-
}
|
package/src/harvester.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import Constants from "./constants";
|
|
2
|
-
import pkg from "../package.json";
|
|
3
|
-
import { callApi, getPayloadSize } from "./utils";
|
|
4
|
-
|
|
5
|
-
const { INTERVAL, MAX_EVENTS_PER_BATCH, MAX_PAYLOAD_SIZE, MAX_BEACON_SIZE } =
|
|
6
|
-
Constants;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* A scheduler and dispatcher for sending raw event data to the New Relic 'ins' endpoint.
|
|
10
|
-
* It manages the harvest cycle, URL construction, and retries.
|
|
11
|
-
*/
|
|
12
|
-
export class NRVideoHarvester {
|
|
13
|
-
#started = false;
|
|
14
|
-
#aggregate; // EventAggregator instance
|
|
15
|
-
#timerId = null; // Timer ID for cleanup
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param {object} agentController - The agent's configuration object.
|
|
19
|
-
* @param {object} aggregate - The aggregator instance (e.g., EventAggregator).
|
|
20
|
-
*/
|
|
21
|
-
constructor(aggregate) {
|
|
22
|
-
this.#aggregate = aggregate;
|
|
23
|
-
// Ensure any queued data is sent when the user navigates away.
|
|
24
|
-
window.addEventListener("pagehide", () =>
|
|
25
|
-
this.triggerHarvest({ isFinalHarvest: true })
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Starts the periodic harvest timer.
|
|
31
|
-
*/
|
|
32
|
-
startTimer() {
|
|
33
|
-
if (this.#started) return;
|
|
34
|
-
this.#started = true;
|
|
35
|
-
const onHarvestInterval = () => {
|
|
36
|
-
this.triggerHarvest({});
|
|
37
|
-
if (this.#started) {
|
|
38
|
-
this.#timerId = setTimeout(onHarvestInterval, INTERVAL);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
this.#timerId = setTimeout(onHarvestInterval, INTERVAL);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Stops the harvest timer and cleans up resources.
|
|
46
|
-
*/
|
|
47
|
-
stopTimer() {
|
|
48
|
-
this.#started = false;
|
|
49
|
-
if (this.#timerId) {
|
|
50
|
-
clearTimeout(this.#timerId);
|
|
51
|
-
this.#timerId = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Executes a harvest cycle by draining the queue and sending it in chunks.
|
|
57
|
-
*/
|
|
58
|
-
|
|
59
|
-
triggerHarvest(options = {}) {
|
|
60
|
-
if (this.#aggregate.isEmpty()) return;
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
// 1. Drain the entire queue to get all pending events.
|
|
64
|
-
const allEvents = this.#aggregate.drain();
|
|
65
|
-
|
|
66
|
-
// 2. Determine the correct size limit for this harvest.
|
|
67
|
-
const maxChunkSize = options.isFinalHarvest
|
|
68
|
-
? MAX_BEACON_SIZE
|
|
69
|
-
: MAX_PAYLOAD_SIZE;
|
|
70
|
-
|
|
71
|
-
// 3. Split the events into chunks that respect size and count limits.
|
|
72
|
-
const chunks = this.chunkEvents(allEvents, maxChunkSize);
|
|
73
|
-
|
|
74
|
-
// 4. Send each chunk sequentially.
|
|
75
|
-
chunks.forEach((chunk, index) => {
|
|
76
|
-
const isLastChunk = index === chunks.length - 1;
|
|
77
|
-
this.sendChunk(chunk, options, isLastChunk);
|
|
78
|
-
});
|
|
79
|
-
} catch (error) {
|
|
80
|
-
console.error("Error during harvest:", error);
|
|
81
|
-
// Re-add events to the queue if something went wrong
|
|
82
|
-
// This is a failsafe to prevent data loss
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Splits an array of events into multiple smaller arrays (chunks).
|
|
88
|
-
*/
|
|
89
|
-
chunkEvents(events, maxChunkSize) {
|
|
90
|
-
const chunks = [];
|
|
91
|
-
let currentChunk = [];
|
|
92
|
-
|
|
93
|
-
for (const event of events) {
|
|
94
|
-
if (currentChunk.length >= MAX_EVENTS_PER_BATCH) {
|
|
95
|
-
chunks.push(currentChunk);
|
|
96
|
-
currentChunk = [];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
currentChunk.push(event);
|
|
100
|
-
const payloadSize = getPayloadSize({ ins: currentChunk });
|
|
101
|
-
// Use the maxChunkSize passed into the function
|
|
102
|
-
if (payloadSize > maxChunkSize) {
|
|
103
|
-
const lastEvent = currentChunk.pop();
|
|
104
|
-
if (currentChunk.length > 0) {
|
|
105
|
-
chunks.push(currentChunk);
|
|
106
|
-
}
|
|
107
|
-
currentChunk = [lastEvent];
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (currentChunk.length > 0) {
|
|
112
|
-
chunks.push(currentChunk);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return chunks;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Sends a single chunk of events.
|
|
120
|
-
*/
|
|
121
|
-
sendChunk(chunk, options, isLastChunk) {
|
|
122
|
-
const url = this.#buildUrl();
|
|
123
|
-
if (!url) {
|
|
124
|
-
// If URL construction failed, treat as a failed request that shouldn't be retried
|
|
125
|
-
this.#aggregate.postHarvestCleanup({ retry: false, status: 0 });
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const payload = { body: { ins: chunk } };
|
|
130
|
-
|
|
131
|
-
callApi(
|
|
132
|
-
{
|
|
133
|
-
url: url,
|
|
134
|
-
payload: payload,
|
|
135
|
-
options: options,
|
|
136
|
-
},
|
|
137
|
-
(result) => {
|
|
138
|
-
// Pass the failed chunk back to the aggregator for re-queuing.
|
|
139
|
-
if (result.retry) {
|
|
140
|
-
result.chunk = chunk;
|
|
141
|
-
}
|
|
142
|
-
this.#aggregate.postHarvestCleanup(result);
|
|
143
|
-
}
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Constructs the specific URL for the New Relic 'ins' endpoint with all required parameters.
|
|
149
|
-
* @private
|
|
150
|
-
*/
|
|
151
|
-
|
|
152
|
-
#buildUrl() {
|
|
153
|
-
try {
|
|
154
|
-
if (!window.NRVIDEO || !window.NRVIDEO.info) {
|
|
155
|
-
throw new Error("NRVIDEO info is not available.");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const { beacon, licenseKey, applicationID, sa } = window.NRVIDEO.info;
|
|
159
|
-
|
|
160
|
-
if (!beacon || !licenseKey || !applicationID)
|
|
161
|
-
throw new Error(
|
|
162
|
-
"Options object provided by New Relic is not correctly initialized"
|
|
163
|
-
);
|
|
164
|
-
const url = `https://${beacon}/ins/1/${licenseKey}?a=${applicationID}&v=${pkg.version}&ref=${window.location.href}&ca=VA`;
|
|
165
|
-
return url;
|
|
166
|
-
} catch (error) {
|
|
167
|
-
console.error(error.message);
|
|
168
|
-
return null; // Return null instead of undefined
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|