@newrelic/video-core 4.1.8 → 5.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +140 -27
  3. package/dist/cjs/browser/index.js +3 -0
  4. package/dist/cjs/browser/index.js.LICENSE.txt +6 -0
  5. package/dist/cjs/browser/index.js.map +1 -0
  6. package/dist/cjs/index.js +1 -1
  7. package/dist/cjs/index.js.LICENSE.txt +1 -1
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/cjs/vega/index.js +3 -0
  10. package/dist/cjs/vega/index.js.LICENSE.txt +6 -0
  11. package/dist/cjs/vega/index.js.map +1 -0
  12. package/dist/esm/browser/index.js +3 -0
  13. package/dist/esm/browser/index.js.LICENSE.txt +6 -0
  14. package/dist/esm/browser/index.js.map +1 -0
  15. package/dist/esm/index.js +1 -1
  16. package/dist/esm/index.js.LICENSE.txt +1 -1
  17. package/dist/esm/index.js.map +1 -1
  18. package/dist/esm/vega/index.js +3 -0
  19. package/dist/esm/vega/index.js.LICENSE.txt +6 -0
  20. package/dist/esm/vega/index.js.map +1 -0
  21. package/dist/umd/nrvideo.min.js +1 -1
  22. package/dist/umd/nrvideo.min.js.LICENSE.txt +1 -1
  23. package/dist/umd/nrvideo.min.js.map +1 -1
  24. package/package.json +25 -7
  25. package/src/{agent.js → browser/agent.js} +14 -35
  26. package/src/{harvestScheduler.js → browser/harvestScheduler.js} +55 -137
  27. package/src/browser/index.js +58 -0
  28. package/src/connectedDevice/connectedDeviceAgent.js +116 -0
  29. package/src/connectedDevice/connectedDeviceConstants.js +112 -0
  30. package/src/connectedDevice/connectedDeviceHarvester.js +514 -0
  31. package/src/connectedDevice/index.js +49 -0
  32. package/src/constants.js +2 -0
  33. package/src/core.js +4 -18
  34. package/src/index.js +9 -10
  35. package/src/recordEvent.js +56 -49
  36. package/src/tracker.js +14 -4
  37. package/src/utils/eventBuilder.js +126 -0
  38. package/src/utils/harvestTimer.js +109 -0
  39. package/src/{utils.js → utils/index.js} +2 -2
  40. package/src/utils/qoeFilters.js +149 -0
  41. package/src/videoConfiguration.js +57 -10
  42. package/src/videotracker.js +26 -9
@@ -1,5 +1,6 @@
1
1
  import Log from "./log";
2
2
  import Constants from "./constants";
3
+ import { ENDPOINT_URL } from "./connectedDevice/connectedDeviceConstants";
3
4
 
4
5
  const { COLLECTOR } = Constants;
5
6
 
@@ -15,18 +16,42 @@ class VideoConfiguration {
15
16
  * @returns {boolean} True if configuration is valid and set
16
17
  */
17
18
 
18
- setConfiguration(userInfo, config) {
19
- if (!this.validateRequiredFields(userInfo)) {
19
+ setConfiguration(userInfo, config, src) {
20
+ const validated = src === "Vega"
21
+ ? this.validateVegaFields(userInfo)
22
+ : this.validateRequiredFields(userInfo);
23
+ if (!validated) {
20
24
  return false;
21
25
  }
22
26
  if (!this.validateConfigFields(config)) {
23
27
  return false;
24
28
  }
25
- this.initializeGlobalConfig(userInfo, config);
29
+ this.initializeGlobalConfig(userInfo, config, src);
26
30
  Log.notice("Video analytics configuration initialized successfully");
27
31
  return true;
28
32
  }
29
33
 
34
+ /**
35
+ * Validates required Vega configuration fields.
36
+ * @param {object} info
37
+ * @returns {boolean} True if valid
38
+ */
39
+ validateVegaFields(info) {
40
+ if (!info || typeof info !== "object") {
41
+ Log.error("Configuration must be an object");
42
+ return false;
43
+ }
44
+ if (!info.applicationToken) {
45
+ Log.error("applicationToken is required");
46
+ return false;
47
+ }
48
+ if (!(info.endpoint?.toLowerCase() in ENDPOINT_URL)) {
49
+ Log.error("Invalid endpoint (must be us, eu, staging, gov, or jp)");
50
+ return false;
51
+ }
52
+ return true;
53
+ }
54
+
30
55
  /**
31
56
  * Validates required configuration fields.
32
57
  * @param {object} config - Configuration to validate
@@ -126,15 +151,16 @@ class VideoConfiguration {
126
151
  }
127
152
 
128
153
  /**
129
- * Sanitizes qoeIntervalFactor, defaulting to 2 if the value is not a positive integer.
154
+ * Sanitizes qoeIntervalFactor, defaulting to Constants.DEFAULT_QOE_INTERVAL_FACTOR
155
+ * if the value is not a positive integer.
130
156
  * @param {*} value
131
157
  * @returns {number}
132
158
  */
133
159
  sanitizeQoeIntervalFactor(value) {
134
- if (value === undefined || value === null) return 2;
160
+ if (value === undefined || value === null) return Constants.DEFAULT_QOE_INTERVAL_FACTOR;
135
161
  if (typeof value === "number" && Number.isInteger(value) && value >= 1) return value;
136
- Log.warn(`Invalid qoeIntervalFactor "${value}" — must be a positive integer. Defaulting to 2.`);
137
- return 2;
162
+ Log.warn(`Invalid qoeIntervalFactor "${value}" — must be a positive integer. Defaulting to ${Constants.DEFAULT_QOE_INTERVAL_FACTOR}.`);
163
+ return Constants.DEFAULT_QOE_INTERVAL_FACTOR;
138
164
  }
139
165
 
140
166
  /**
@@ -142,7 +168,28 @@ class VideoConfiguration {
142
168
  * @param {object} userInfo - User provided configuration
143
169
  * @param {object} [config] - Optional configuration object
144
170
  */
145
- initializeGlobalConfig(userInfo, config) {
171
+ initializeGlobalConfig(userInfo, config, src) {
172
+ // Vega path: write `globalThis.__NRVIDEO_CD__` with info+config only.
173
+ // The harvester is owned by `connectedDeviceAgent.js` as a module singleton — no
174
+ // harvester field on this global.
175
+ if (src === "Vega") {
176
+ globalThis.__NRVIDEO_CD__ = {
177
+ info: {
178
+ accountId: userInfo.accountId,
179
+ applicationToken: userInfo.applicationToken,
180
+ endpoint: userInfo.endpoint?.toLowerCase(),
181
+ ...(userInfo.appName ? { appName: userInfo.appName } : {}),
182
+ ...(userInfo.applicationID ? { applicationID: userInfo.applicationID } : {}),
183
+ ...(userInfo.deviceInfo ? { deviceInfo: userInfo.deviceInfo } : {}),
184
+ },
185
+ config: {
186
+ qoeAggregate: config?.qoeAggregate ?? true,
187
+ qoeIntervalFactor: this.sanitizeQoeIntervalFactor(config?.qoeIntervalFactor),
188
+ obfuscate: this.filterObfuscateRules(config?.obfuscate),
189
+ },
190
+ };
191
+ return;
192
+ }
146
193
 
147
194
  let { licenseKey, appName, region, beacon, applicationID } = userInfo;
148
195
 
@@ -179,8 +226,8 @@ const videoConfiguration = new VideoConfiguration();
179
226
  * @param {object} [config] - Optional configuration object
180
227
  * @returns {boolean} True if configuration was set successfully
181
228
  */
182
- export function setVideoConfig(info, config) {
183
- return videoConfiguration.setConfiguration(info, config);
229
+ export function setVideoConfig(info, config, src) {
230
+ return videoConfiguration.setConfiguration(info, config, src);
184
231
  }
185
232
 
186
233
  export { videoConfiguration };
@@ -1,7 +1,6 @@
1
1
  import Log from "./log";
2
2
  import Tracker from "./tracker";
3
3
  import TrackerState from "./videotrackerstate";
4
- import { videoAnalyticsHarvester } from "./agent";
5
4
  import pkg from "../package.json";
6
5
 
7
6
  /**
@@ -45,6 +44,7 @@ class VideoTracker extends Tracker {
45
44
  */
46
45
  this._lastBufferType = null;
47
46
  this._userId = null;
47
+ this._src = null;
48
48
 
49
49
  options = options || {};
50
50
  this.setOptions(options);
@@ -84,6 +84,16 @@ class VideoTracker extends Tracker {
84
84
  if (typeof options.isAd === "boolean") {
85
85
  this.setIsAd(options.isAd);
86
86
  }
87
+ if (options.src !== undefined) {
88
+ if (this._src !== null && this._src !== options.src) {
89
+ Log.warn(`setOptions: src is locked to '${this._src}'. Ignoring override '${options.src}'.`);
90
+ } else {
91
+ this._src = options.src;
92
+ if (this.adsTracker && this.adsTracker._src == null) {
93
+ this.adsTracker._src = this._src;
94
+ }
95
+ }
96
+ }
87
97
  Tracker.prototype.setOptions.apply(this, arguments);
88
98
  }
89
99
  }
@@ -135,6 +145,12 @@ class VideoTracker extends Tracker {
135
145
  this.adsTracker = tracker;
136
146
  this.adsTracker.setIsAd(true);
137
147
  this.adsTracker.parentTracker = this;
148
+ // Propagate src so ads events route to the same pipeline as content events.
149
+ // Covers the case where setAdsTracker is called after the content tracker
150
+ // is fully initialised and this._src is already set.
151
+ if (this._src != null && tracker._src == null) {
152
+ tracker._src = this._src;
153
+ }
138
154
  this.adsTracker.on("*", funnelAdEvents.bind(this));
139
155
  }
140
156
  }
@@ -465,7 +481,7 @@ class VideoTracker extends Tracker {
465
481
  att["instrumentation.name"] = this.getInstrumentationName();
466
482
  att["instrumentation.version"] = this.getInstrumentationVersion();
467
483
  att["enduser.id"] = this._userId;
468
- att["src"] = "Browser";
484
+ att["src"] = this._src || "Browser";
469
485
 
470
486
  if (type === "customAction") return att;
471
487
 
@@ -639,11 +655,11 @@ class VideoTracker extends Tracker {
639
655
  this.state.setStartupTime(totalAdsTime)
640
656
  this.sendVideoAction(ev, att);
641
657
 
642
- // Register callback to refresh QoE KPIs with latest state before each drain
643
- videoAnalyticsHarvester.setBeforeDrainCallback(() => {
658
+ const harvester = this.getHarvester?.();
659
+ harvester?.setBeforeDrainCallback(() => {
644
660
  if (this.state) {
645
661
  const freshKpis = this.state.getQoeAttributes({}).qoe;
646
- videoAnalyticsHarvester.refreshQoeKpis(freshKpis, this.getViewId());
662
+ harvester.refreshQoeKpis(freshKpis, this.getViewId());
647
663
  }
648
664
  });
649
665
  }
@@ -688,14 +704,15 @@ class VideoTracker extends Tracker {
688
704
  this.state.goViewCountUp();
689
705
  this.state.totalPlaytime = 0;
690
706
  if(!this.isAd()) {
707
+ const harvester = this.getHarvester?.();
691
708
  // Force QoE to be included in the next harvest cycle at content end
692
- videoAnalyticsHarvester.forceNextQoeCycle();
709
+ harvester?.forceNextQoeCycle?.();
693
710
  // Clear the before-drain callback so the next harvest doesn't overwrite
694
711
  // the final QoE (already in buffer) with zeroed-out state values
695
- videoAnalyticsHarvester.setBeforeDrainCallback(null);
712
+ harvester?.setBeforeDrainCallback?.(null);
696
713
  // reset the states after the view count is up
697
- if(this.adsTracker) this.adsTracker.state.clearTotalAdsTime();
698
- this.state.resetViewIdTrackedState();
714
+ if(this.adsTracker) this.adsTracker.state.clearTotalAdsTime();
715
+ this.state.resetViewIdTrackedState();
699
716
  }
700
717
  }
701
718
  }