@newrelic/video-core 3.2.0-beta-0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- }