stormcloud-video-player 0.8.68 → 0.8.70

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/dist/stormcloud-vp.min.js +2 -2
  2. package/lib/Player.cjs +30 -8
  3. package/lib/Player.cjs.map +1 -1
  4. package/lib/Player.d.cts +2 -1
  5. package/lib/StormcloudPlayer.cjs +6104 -5114
  6. package/lib/StormcloudPlayer.cjs.map +1 -1
  7. package/lib/StormcloudPlayer.d.cts +1 -0
  8. package/lib/index.cjs +6217 -5149
  9. package/lib/index.cjs.map +1 -1
  10. package/lib/index.d.cts +2 -1
  11. package/lib/index.d.ts +93 -2
  12. package/lib/index.js +6199 -5158
  13. package/lib/index.js.map +1 -1
  14. package/lib/player/AdBreakOrchestrator.cjs +669 -131
  15. package/lib/player/AdBreakOrchestrator.cjs.map +1 -1
  16. package/lib/player/AdBreakOrchestrator.d.cts +27 -3
  17. package/lib/player/EdgePodRequest.cjs +136 -0
  18. package/lib/player/EdgePodRequest.cjs.map +1 -0
  19. package/lib/player/EdgePodRequest.d.cts +14 -0
  20. package/lib/player/FloorLadder.cjs +578 -0
  21. package/lib/player/FloorLadder.cjs.map +1 -0
  22. package/lib/player/FloorLadder.d.cts +74 -0
  23. package/lib/player/StormcloudVideoPlayer.cjs +6104 -5114
  24. package/lib/player/StormcloudVideoPlayer.cjs.map +1 -1
  25. package/lib/player/StormcloudVideoPlayer.d.cts +13 -1
  26. package/lib/players/HlsPlayer.cjs +6104 -5114
  27. package/lib/players/HlsPlayer.cjs.map +1 -1
  28. package/lib/players/index.cjs +6104 -5114
  29. package/lib/players/index.cjs.map +1 -1
  30. package/lib/props.cjs +30 -8
  31. package/lib/props.cjs.map +1 -1
  32. package/lib/props.d.cts +1 -0
  33. package/lib/sdk/hlsAdPlayer.cjs +120 -19
  34. package/lib/sdk/hlsAdPlayer.cjs.map +1 -1
  35. package/lib/types.d.cts +30 -2
  36. package/lib/ui/StormcloudVideoPlayer.cjs +6169 -5129
  37. package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
  38. package/lib/ui/StormcloudVideoPlayer.d.cts +1 -0
  39. package/lib/utils/vastMacros.cjs +4 -0
  40. package/lib/utils/vastMacros.cjs.map +1 -1
  41. package/lib/utils/vastMacros.d.cts +2 -1
  42. package/package.json +1 -1
@@ -284,84 +284,6 @@ __export(AdBreakOrchestrator_exports, {
284
284
  }
285
285
  });
286
286
  module.exports = __toCommonJS(AdBreakOrchestrator_exports);
287
- // src/utils/trackingIngest.ts
288
- var DEFAULT_TRACKING_INGEST_CONFIG = {
289
- enabled: true,
290
- baseUrl: "https://player-analytics-ingest-api-760490581852.us-central1.run.app/api/adstorm/player-tracking"
291
- };
292
- var trackingIngestConfig = _object_spread({}, DEFAULT_TRACKING_INGEST_CONFIG);
293
- function isTrackingIngestEnabled() {
294
- return trackingIngestConfig.enabled === true;
295
- }
296
- function normalizeIngestBaseUrl(baseUrl) {
297
- return baseUrl.replace(/\/+$/, "");
298
- }
299
- function buildTrackingIngestUrl(channel) {
300
- var base = normalizeIngestBaseUrl(trackingIngestConfig.baseUrl);
301
- if (channel === "impressions") {
302
- return "".concat(base, "/impressions/ingest");
303
- }
304
- return "".concat(base, "/metrics/ingest");
305
- }
306
- function ingestPlayerTracking(licenseKey, channel, body) {
307
- return _async_to_generator(function() {
308
- var response, error;
309
- return _ts_generator(this, function(_state) {
310
- switch(_state.label){
311
- case 0:
312
- if (!isTrackingIngestEnabled() || !licenseKey) {
313
- return [
314
- 2,
315
- false
316
- ];
317
- }
318
- _state.label = 1;
319
- case 1:
320
- _state.trys.push([
321
- 1,
322
- 3,
323
- ,
324
- 4
325
- ]);
326
- return [
327
- 4,
328
- fetch(buildTrackingIngestUrl(channel), {
329
- method: "POST",
330
- headers: {
331
- "Content-Type": "application/json",
332
- Authorization: "License-Key ".concat(licenseKey)
333
- },
334
- body: JSON.stringify(body)
335
- })
336
- ];
337
- case 2:
338
- response = _state.sent();
339
- if (!response.ok) {
340
- console.warn("[StormcloudVideoPlayer] Tracking ingest failed (".concat(response.status, ") for ").concat(channel));
341
- return [
342
- 2,
343
- false
344
- ];
345
- }
346
- return [
347
- 2,
348
- true
349
- ];
350
- case 3:
351
- error = _state.sent();
352
- console.warn("[StormcloudVideoPlayer] Tracking ingest error for ".concat(channel, ":"), error);
353
- return [
354
- 2,
355
- false
356
- ];
357
- case 4:
358
- return [
359
- 2
360
- ];
361
- }
362
- });
363
- })();
364
- }
365
287
  // src/utils/vastEnvironmentSignals.ts
366
288
  var PLAYER_ID_STORAGE_KEY = "stormcloud.player.id";
367
289
  var DEVICE_ID_STORAGE_KEYS = [
@@ -807,6 +729,259 @@ function normalizeBoolean(value) {
807
729
  }
808
730
  return void 0;
809
731
  }
732
+ // src/utils/vastMacros.ts
733
+ var GAM_AD_HOSTS = /* @__PURE__ */ new Set([
734
+ "pubads.g.doubleclick.net",
735
+ "securepubads.g.doubleclick.net"
736
+ ]);
737
+ function ensureVideoAdType(rawUrl) {
738
+ try {
739
+ var url = new URL(rawUrl);
740
+ if (!GAM_AD_HOSTS.has(url.hostname)) return rawUrl;
741
+ if (url.searchParams.has("ad_type")) return rawUrl;
742
+ url.searchParams.set("ad_type", "video");
743
+ return url.toString();
744
+ } catch (unused) {
745
+ return rawUrl;
746
+ }
747
+ }
748
+ // src/player/FloorLadder.ts
749
+ var DEFAULT_WEB_FLOOR_LADDER_CONFIG = Object.freeze({
750
+ enabled: false,
751
+ targetingKey: "airytv_web_floor",
752
+ minFloorCents: 25,
753
+ maxFloorCents: 300,
754
+ stepCents: 25,
755
+ selectedFloorCents: 25,
756
+ fallbackFloorCents: 25,
757
+ readyFloorCents: [],
758
+ mode: "fixed",
759
+ allowedPositions: [
760
+ "midroll"
761
+ ],
762
+ targetAdUnitPath: "/21821455290/airytvadx/airytv-web",
763
+ configurationVersion: "2026-09-29.1"
764
+ });
765
+ var DEFAULT_FLOOR_LADDER_BOUNDS = Object.freeze({
766
+ minCents: 25,
767
+ maxCents: 300,
768
+ stepCents: 25
769
+ });
770
+ function decodeQueryComponent(value) {
771
+ try {
772
+ return decodeURIComponent(value.replace(/\+/g, " "));
773
+ } catch (unused) {
774
+ return void 0;
775
+ }
776
+ }
777
+ function mergeCustParams(decoded, key, cents) {
778
+ var prefix = "".concat(key, "=");
779
+ var kept = decoded.split("&").filter(function(pair) {
780
+ return pair !== "" && pair !== key && pair.indexOf(prefix) !== 0;
781
+ });
782
+ kept.push("".concat(prefix).concat(cents));
783
+ return kept.join("&");
784
+ }
785
+ function applyFloorToVastUrl(url, floor) {
786
+ if (floor.status !== "armed" || floor.floorCents == null) {
787
+ return {
788
+ url: url,
789
+ applied: false,
790
+ reason: floor.reason
791
+ };
792
+ }
793
+ var parsed;
794
+ try {
795
+ parsed = new URL(url);
796
+ } catch (unused) {
797
+ return {
798
+ url: url,
799
+ applied: false,
800
+ reason: "invalid_url"
801
+ };
802
+ }
803
+ if (!GAM_AD_HOSTS.has(parsed.hostname)) {
804
+ return {
805
+ url: url,
806
+ applied: false,
807
+ reason: "not_gam_host"
808
+ };
809
+ }
810
+ if (parsed.searchParams.get("iu") !== floor.targetAdUnitPath) {
811
+ return {
812
+ url: url,
813
+ applied: false,
814
+ reason: "ad_unit_mismatch"
815
+ };
816
+ }
817
+ var hashIndex = url.indexOf("#");
818
+ var beforeHash = hashIndex === -1 ? url : url.slice(0, hashIndex);
819
+ var hash = hashIndex === -1 ? "" : url.slice(hashIndex);
820
+ var queryIndex = beforeHash.indexOf("?");
821
+ var base = beforeHash.slice(0, queryIndex);
822
+ var segments = beforeHash.slice(queryIndex + 1).split("&");
823
+ var existing = [];
824
+ var insertAt = -1;
825
+ var rest = [];
826
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
827
+ try {
828
+ for(var _iterator = segments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
829
+ var segment = _step.value;
830
+ var eq = segment.indexOf("=");
831
+ var rawKey = eq === -1 ? segment : segment.slice(0, eq);
832
+ if (decodeQueryComponent(rawKey) === "cust_params") {
833
+ if (insertAt === -1) insertAt = rest.length;
834
+ var value = eq === -1 ? "" : decodeQueryComponent(segment.slice(eq + 1));
835
+ if (value === void 0) {
836
+ return {
837
+ url: url,
838
+ applied: false,
839
+ reason: "malformed_cust_params"
840
+ };
841
+ }
842
+ if (value) existing.push(value);
843
+ continue;
844
+ }
845
+ rest.push(segment);
846
+ }
847
+ } catch (err) {
848
+ _didIteratorError = true;
849
+ _iteratorError = err;
850
+ } finally{
851
+ try {
852
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
853
+ _iterator.return();
854
+ }
855
+ } finally{
856
+ if (_didIteratorError) {
857
+ throw _iteratorError;
858
+ }
859
+ }
860
+ }
861
+ if (insertAt === -1) insertAt = rest.length;
862
+ var merged = mergeCustParams(existing.join("&"), floor.targetingKey, floor.floorCents);
863
+ rest.splice(insertAt, 0, "cust_params=".concat(encodeURIComponent(merged)));
864
+ var query = rest.filter(function(segment) {
865
+ return segment !== "";
866
+ }).join("&");
867
+ return {
868
+ url: "".concat(base, "?").concat(query).concat(hash),
869
+ applied: true,
870
+ reason: void 0
871
+ };
872
+ }
873
+ // src/player/EdgePodRequest.ts
874
+ var EDGE_POD_MAX_ADS = 10;
875
+ var EDGE_POD_EXCEEDS_BUDGET = "edge_pod_exceeds_budget";
876
+ function buildEdgePodUrl(baseUrl, remainingMs, remainingAdLimit) {
877
+ var url = new URL(baseUrl);
878
+ var maxMs = Math.max(0, Math.floor(remainingMs));
879
+ var maxAds = Math.max(1, Math.min(EDGE_POD_MAX_ADS, Math.floor(remainingAdLimit)));
880
+ url.searchParams.set("pmxd", String(maxMs));
881
+ url.searchParams.set("pmad", String(maxAds));
882
+ url.searchParams.set("duration", String(Math.floor(maxMs / 1e3)));
883
+ url.searchParams.set("count", String(maxAds));
884
+ return url.toString();
885
+ }
886
+ function classifyEdgeVastResponse(result) {
887
+ var _ref;
888
+ var _result_error;
889
+ if (result.error == null) {
890
+ return result.podAdCount > 0 ? "fill" : "no_fill";
891
+ }
892
+ var message = (_ref = (_result_error = result.error) === null || _result_error === void 0 ? void 0 : _result_error.message) !== null && _ref !== void 0 ? _ref : String(result.error);
893
+ if (/no ads available|fill rejected|no media file|no ad loaded/i.test(message) || message.includes(EDGE_POD_EXCEEDS_BUDGET)) {
894
+ return "no_fill";
895
+ }
896
+ return "invalid";
897
+ }
898
+ function describeEdgeUrl(url) {
899
+ try {
900
+ var parsed = new URL(url);
901
+ return "".concat(parsed.origin).concat(parsed.pathname);
902
+ } catch (unused) {
903
+ var _url_split_;
904
+ return (_url_split_ = url.split("?")[0]) !== null && _url_split_ !== void 0 ? _url_split_ : "";
905
+ }
906
+ }
907
+ // src/utils/trackingIngest.ts
908
+ var DEFAULT_TRACKING_INGEST_CONFIG = {
909
+ enabled: true,
910
+ baseUrl: "https://player-analytics-ingest-api-760490581852.us-central1.run.app/api/adstorm/player-tracking"
911
+ };
912
+ var trackingIngestConfig = _object_spread({}, DEFAULT_TRACKING_INGEST_CONFIG);
913
+ function isTrackingIngestEnabled() {
914
+ return trackingIngestConfig.enabled === true;
915
+ }
916
+ function normalizeIngestBaseUrl(baseUrl) {
917
+ return baseUrl.replace(/\/+$/, "");
918
+ }
919
+ function buildTrackingIngestUrl(channel) {
920
+ var base = normalizeIngestBaseUrl(trackingIngestConfig.baseUrl);
921
+ if (channel === "impressions") {
922
+ return "".concat(base, "/impressions/ingest");
923
+ }
924
+ return "".concat(base, "/metrics/ingest");
925
+ }
926
+ function ingestPlayerTracking(licenseKey, channel, body) {
927
+ return _async_to_generator(function() {
928
+ var response, error;
929
+ return _ts_generator(this, function(_state) {
930
+ switch(_state.label){
931
+ case 0:
932
+ if (!isTrackingIngestEnabled() || !licenseKey) {
933
+ return [
934
+ 2,
935
+ false
936
+ ];
937
+ }
938
+ _state.label = 1;
939
+ case 1:
940
+ _state.trys.push([
941
+ 1,
942
+ 3,
943
+ ,
944
+ 4
945
+ ]);
946
+ return [
947
+ 4,
948
+ fetch(buildTrackingIngestUrl(channel), {
949
+ method: "POST",
950
+ headers: {
951
+ "Content-Type": "application/json",
952
+ Authorization: "License-Key ".concat(licenseKey)
953
+ },
954
+ body: JSON.stringify(body)
955
+ })
956
+ ];
957
+ case 2:
958
+ response = _state.sent();
959
+ if (!response.ok) {
960
+ console.warn("[StormcloudVideoPlayer] Tracking ingest failed (".concat(response.status, ") for ").concat(channel));
961
+ return [
962
+ 2,
963
+ false
964
+ ];
965
+ }
966
+ return [
967
+ 2,
968
+ true
969
+ ];
970
+ case 3:
971
+ error = _state.sent();
972
+ console.warn("[StormcloudVideoPlayer] Tracking ingest error for ".concat(channel, ":"), error);
973
+ return [
974
+ 2,
975
+ false
976
+ ];
977
+ case 4:
978
+ return [
979
+ 2
980
+ ];
981
+ }
982
+ });
983
+ })();
984
+ }
810
985
  // src/utils/tracking.ts
811
986
  var cachedBrowserId = null;
812
987
  function buildAnalyticsContext(config) {
@@ -1288,22 +1463,6 @@ function sendHeartbeat(_0) {
1288
1463
  function getAdAudioVolume(adPlayer) {
1289
1464
  return adPlayer.getOriginalMutedState() ? 0 : adPlayer.getOriginalVolume();
1290
1465
  }
1291
- // src/utils/vastMacros.ts
1292
- var GAM_AD_HOSTS = /* @__PURE__ */ new Set([
1293
- "pubads.g.doubleclick.net",
1294
- "securepubads.g.doubleclick.net"
1295
- ]);
1296
- function ensureVideoAdType(rawUrl) {
1297
- try {
1298
- var url = new URL(rawUrl);
1299
- if (!GAM_AD_HOSTS.has(url.hostname)) return rawUrl;
1300
- if (url.searchParams.has("ad_type")) return rawUrl;
1301
- url.searchParams.set("ad_type", "video");
1302
- return url.toString();
1303
- } catch (unused) {
1304
- return rawUrl;
1305
- }
1306
- }
1307
1466
  // src/player/AdBreakOrchestrator.ts
1308
1467
  var REQUEST_FAILURE_STAGES = /* @__PURE__ */ new Set([
1309
1468
  "no_fill",
@@ -1332,6 +1491,8 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1332
1491
  this.controllerCallInFlight = false;
1333
1492
  this.playbackStartedForCurrentRequest = false;
1334
1493
  this.opportunityEpoch = -1;
1494
+ this.edgeExhaustedForBreak = false;
1495
+ this.edgeRequestsInBreak = 0;
1335
1496
  this.adPodQueue = [];
1336
1497
  this.defaultMinRemainingMsToRequestAd = 1e4;
1337
1498
  this.defaultMaxNoFillsPerBreak = 2;
@@ -1482,7 +1643,9 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1482
1643
  _this.timing.clearAdRequestWatchdog();
1483
1644
  _this.timing.activeAdRequestToken = null;
1484
1645
  _this.showAds = true;
1485
- _this.timing.resetGamNoFillCounter();
1646
+ if (_this.currentPodSource !== "edge") {
1647
+ _this.timing.resetGamNoFillCounter();
1648
+ }
1486
1649
  if (_this.inAdBreak && _this.timing.expectedAdBreakDurationMs != null && _this.timing.getAdBreakEndWallClockMs() == null) {
1487
1650
  _this.timing.scheduleAdStopAtBreakBoundary();
1488
1651
  if (_this.debug) {
@@ -1612,20 +1775,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1612
1775
  },
1613
1776
  {
1614
1777
  key: "beginVmapBreak",
1615
- value: function beginVmapBreak(breakKey) {
1778
+ value: function beginVmapBreak(breakKey, position) {
1616
1779
  this.breakSource = "vmap";
1780
+ this.resetEdgeStateForBreak();
1617
1781
  this.followUpBaseVastUrl = void 0;
1618
1782
  this.unusableResponsesInBreak = 0;
1619
1783
  this.timing.totalAdRequestsInBreak = 0;
1620
1784
  this.setSlotState("NEEDS_NEXT_AD", "vmap_break_start");
1621
- this.noteOpportunity("vmap", breakKey);
1785
+ this.noteOpportunity("vmap", breakKey, position);
1622
1786
  }
1623
1787
  },
1624
1788
  {
1625
1789
  key: "handleAdStart",
1626
1790
  value: function handleAdStart(marker) {
1627
1791
  return _async_to_generator(function() {
1628
- var baseVastUrl, adBreakDurationMs, currentMuted, currentVolume, epoch, hasPrefetchedUrl, mode, podPlayed;
1792
+ var baseVastUrl, edgeConfigured, adBreakDurationMs, currentMuted, currentVolume, epoch, hasPrefetchedUrl, mode, edgeUrl, outcome, podPlayed;
1629
1793
  return _ts_generator(this, function(_state) {
1630
1794
  switch(_state.label){
1631
1795
  case 0:
@@ -1639,7 +1803,8 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1639
1803
  ];
1640
1804
  }
1641
1805
  baseVastUrl = this.adConfig.apiVastTagUrl;
1642
- if (!baseVastUrl) {
1806
+ edgeConfigured = !!this.host.getEdgePodBaseUrl();
1807
+ if (!baseVastUrl && !edgeConfigured) {
1643
1808
  console.warn("[AdBreak] SCTE-35 break started but no direct VAST tag is configured");
1644
1809
  this.clearPendingAdBreak();
1645
1810
  return [
@@ -1655,6 +1820,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1655
1820
  this.breakSource = "scte35";
1656
1821
  this.followUpBaseVastUrl = baseVastUrl;
1657
1822
  this.unusableResponsesInBreak = 0;
1823
+ this.resetEdgeStateForBreak();
1658
1824
  this.setSlotState("NEEDS_NEXT_AD", "scte35_break_start");
1659
1825
  if (this.adConfig.podAssignedByPrefetch) {
1660
1826
  this.adConfig.podAssignedByPrefetch = false;
@@ -1673,10 +1839,10 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1673
1839
  if (this.timing.expectedAdBreakDurationMs == null && adBreakDurationMs != null) {
1674
1840
  this.timing.expectedAdBreakDurationMs = adBreakDurationMs;
1675
1841
  }
1676
- this.noteOpportunity("scte35", this.cueManager.activeScte35BreakKey);
1842
+ this.noteOpportunity("scte35", this.cueManager.activeScte35BreakKey, "midroll");
1677
1843
  epoch = this.timing.adBreakEpoch;
1678
1844
  hasPrefetchedUrl = !!(this.pendingAdBreak && this.pendingAdBreak.vastUrls.length > 0);
1679
- if (!(adBreakDurationMs == null && !hasPrefetchedUrl)) return [
1845
+ if (!(adBreakDurationMs == null && (!hasPrefetchedUrl || edgeConfigured))) return [
1680
1846
  3,
1681
1847
  2
1682
1848
  ];
@@ -1695,11 +1861,46 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1695
1861
  mode = this.host.config.isLiveStream ? "LIVE" : "VOD";
1696
1862
  console.log("[AdBreak] ".concat(mode, " break start: target duration=").concat(adBreakDurationMs != null ? "".concat(adBreakDurationMs, "ms") : "unknown"));
1697
1863
  }
1864
+ if (!edgeConfigured) return [
1865
+ 3,
1866
+ 5
1867
+ ];
1868
+ edgeUrl = this.canRequestEdge() ? this.prepareEdgePodRequest() : void 0;
1869
+ if (!edgeUrl) return [
1870
+ 3,
1871
+ 4
1872
+ ];
1873
+ this.clearPendingAdBreak();
1698
1874
  return [
1699
1875
  4,
1700
- this.startOptimizedPod(baseVastUrl, adBreakDurationMs)
1876
+ this.runEdgePod(edgeUrl, "edge_break_start")
1701
1877
  ];
1702
1878
  case 3:
1879
+ outcome = _state.sent();
1880
+ this.continueAfterEdgeOutcome(outcome, epoch);
1881
+ return [
1882
+ 2
1883
+ ];
1884
+ case 4:
1885
+ if (!this.edgeExhaustedForBreak) {
1886
+ this.markEdgeExhausted("not_eligible_at_break_start", {
1887
+ isLive: this.isLiveBreak(),
1888
+ durationMs: this.timing.expectedAdBreakDurationMs
1889
+ });
1890
+ }
1891
+ _state.label = 5;
1892
+ case 5:
1893
+ if (!baseVastUrl) {
1894
+ this.requestNextAd("no_gam_source");
1895
+ return [
1896
+ 2
1897
+ ];
1898
+ }
1899
+ return [
1900
+ 4,
1901
+ this.startOptimizedPod(baseVastUrl, adBreakDurationMs)
1902
+ ];
1903
+ case 6:
1703
1904
  podPlayed = _state.sent();
1704
1905
  if (!podPlayed && this.inAdBreak && epoch === this.timing.adBreakEpoch) {
1705
1906
  this.setSlotState("NEEDS_NEXT_AD", "initial_request_failed");
@@ -1767,7 +1968,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1767
1968
  // ───────────────────────────────────────────────────────────────
1768
1969
  function startOptimizedPod(baseVastUrl, breakDurationMs) {
1769
1970
  return _async_to_generator(function() {
1770
- var podUrl, prefetched, epoch, generation, adPlayer, requestToken, _ref, _adPlayer_getPodAdCount, podCount, error;
1971
+ var podUrl, prefetched, epoch, generation, adPlayer, requestToken, requestUrl, _ref, _adPlayer_getPodAdCount, podCount, error;
1771
1972
  return _ts_generator(this, function(_state) {
1772
1973
  switch(_state.label){
1773
1974
  case 0:
@@ -1791,9 +1992,11 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1791
1992
  adPlayer = this.host.getAdPlayer();
1792
1993
  requestToken = ++this.timing.adRequestTokenCounter;
1793
1994
  this.timing.activeAdRequestToken = requestToken;
1794
- this.currentVastUrl = podUrl;
1995
+ this.currentPodSource = "gam";
1795
1996
  this.setSlotState("REQUESTING", "initial_pod");
1796
1997
  this.beginFunnelRequest(adPlayer, requestToken, "initial_pod");
1998
+ requestUrl = this.applyOpportunityFloor(podUrl);
1999
+ this.currentVastUrl = requestUrl;
1797
2000
  _state.label = 1;
1798
2001
  case 1:
1799
2002
  _state.trys.push([
@@ -1816,7 +2019,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1816
2019
  return [
1817
2020
  4,
1818
2021
  this.callController(function() {
1819
- return adPlayer.requestAds(podUrl);
2022
+ return adPlayer.requestAds(requestUrl);
1820
2023
  })
1821
2024
  ];
1822
2025
  case 3:
@@ -1848,7 +2051,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1848
2051
  }
1849
2052
  sendAdLoadedTracking(this.host.config.licenseKey, {
1850
2053
  source: "hls",
1851
- vastUrl: podUrl,
2054
+ vastUrl: requestUrl,
1852
2055
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
1853
2056
  }, this.analyticsContext).catch(function() {});
1854
2057
  this.timing.startAdFailsafeTimer(requestToken);
@@ -1906,6 +2109,224 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1906
2109
  }).call(this);
1907
2110
  }
1908
2111
  },
2112
+ {
2113
+ // ───────────────────────────────────────────────────────────────
2114
+ // Edge-first pods (live SCTE-35 only, GAM fallback)
2115
+ // ───────────────────────────────────────────────────────────────
2116
+ key: "resetEdgeStateForBreak",
2117
+ value: function resetEdgeStateForBreak() {
2118
+ this.edgeExhaustedForBreak = false;
2119
+ this.edgeRequestsInBreak = 0;
2120
+ this.currentPodSource = void 0;
2121
+ }
2122
+ },
2123
+ {
2124
+ key: "isLiveBreak",
2125
+ value: function isLiveBreak() {
2126
+ return this.host.isLiveStream() || this.host.config.isLiveStream === true;
2127
+ }
2128
+ },
2129
+ {
2130
+ key: "canRequestEdge",
2131
+ value: function canRequestEdge() {
2132
+ return this.breakSource === "scte35" && !this.edgeExhaustedForBreak && !!this.host.getEdgePodBaseUrl() && this.isLiveBreak() && this.timing.expectedAdBreakDurationMs != null && this.edgeRequestsInBreak < this.timing.maxTotalAdRequestsPerBreak;
2133
+ }
2134
+ },
2135
+ {
2136
+ key: "markEdgeExhausted",
2137
+ value: function markEdgeExhausted(reason) {
2138
+ var extra = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
2139
+ this.edgeExhaustedForBreak = true;
2140
+ this.countFunnelStage("edge_fallback:".concat(reason));
2141
+ this.logOrchestratorFunnel("edge_exhausted", _object_spread({
2142
+ reason: reason,
2143
+ edgeRequests: this.edgeRequestsInBreak,
2144
+ remainingMs: this.timing.getRemainingAdMs()
2145
+ }, extra));
2146
+ }
2147
+ },
2148
+ {
2149
+ /**
2150
+ * Builds the next Edge request from the budget left right now, or marks Edge
2151
+ * exhausted for this break. Synchronous on purpose: callers must reach the
2152
+ * REQUESTING state in the same tick so a second completion event cannot race
2153
+ * in a parallel request.
2154
+ */ key: "prepareEdgePodRequest",
2155
+ value: function prepareEdgePodRequest() {
2156
+ var baseUrl = this.host.getEdgePodBaseUrl();
2157
+ if (!baseUrl) return void 0;
2158
+ var remainingMs = this.timing.getRemainingAdMs();
2159
+ var remainingAds = Math.min(EDGE_POD_MAX_ADS, this.adConfig.resolvePodParams().maxAds - this.timing.totalAdsInBreak);
2160
+ if (remainingAds < 1) {
2161
+ this.markEdgeExhausted("ad_capacity_reached", {
2162
+ adsInBreak: this.timing.totalAdsInBreak
2163
+ });
2164
+ return void 0;
2165
+ }
2166
+ if (remainingMs < this.getMinRemainingMsToRequestAd()) {
2167
+ this.markEdgeExhausted("insufficient_break_time", {
2168
+ remainingMs: remainingMs
2169
+ });
2170
+ return void 0;
2171
+ }
2172
+ try {
2173
+ return buildEdgePodUrl(baseUrl, remainingMs, remainingAds);
2174
+ } catch (unused) {
2175
+ this.markEdgeExhausted("invalid_edge_url");
2176
+ return void 0;
2177
+ }
2178
+ }
2179
+ },
2180
+ {
2181
+ key: "runEdgePod",
2182
+ value: function runEdgePod(requestUrl, trigger) {
2183
+ return _async_to_generator(function() {
2184
+ var _params_get, _params_get1, epoch, generation, adPlayer, requestToken, params, _ref, _adPlayer_getPodAdCount, _adPlayer_getPodTotalDurationMs, remainingMs, remainingAds, podCount, error, errorMessage, classification, diag;
2185
+ return _ts_generator(this, function(_state) {
2186
+ switch(_state.label){
2187
+ case 0:
2188
+ epoch = this.timing.adBreakEpoch;
2189
+ this.recreateAdController("new_request");
2190
+ generation = this.controllerGeneration;
2191
+ adPlayer = this.host.getAdPlayer();
2192
+ requestToken = ++this.timing.adRequestTokenCounter;
2193
+ this.timing.activeAdRequestToken = requestToken;
2194
+ this.currentPodSource = "edge";
2195
+ this.edgeRequestsInBreak++;
2196
+ this.setSlotState("REQUESTING", trigger);
2197
+ this.beginFunnelRequest(adPlayer, requestToken, trigger, "edge");
2198
+ this.currentVastUrl = requestUrl;
2199
+ params = new URL(requestUrl).searchParams;
2200
+ this.logOrchestratorFunnel("edge_request_sent", {
2201
+ trigger: trigger,
2202
+ attempt: this.edgeRequestsInBreak,
2203
+ endpoint: describeEdgeUrl(requestUrl),
2204
+ pmxd: (_params_get = params.get("pmxd")) !== null && _params_get !== void 0 ? _params_get : void 0,
2205
+ pmad: (_params_get1 = params.get("pmad")) !== null && _params_get1 !== void 0 ? _params_get1 : void 0
2206
+ });
2207
+ _state.label = 1;
2208
+ case 1:
2209
+ _state.trys.push([
2210
+ 1,
2211
+ 4,
2212
+ ,
2213
+ 5
2214
+ ]);
2215
+ this.timing.startAdRequestWatchdog(requestToken);
2216
+ return [
2217
+ 4,
2218
+ this.callController(function() {
2219
+ return adPlayer.requestAds(requestUrl, {
2220
+ source: "edge"
2221
+ });
2222
+ })
2223
+ ];
2224
+ case 2:
2225
+ _state.sent();
2226
+ this.timing.clearAdRequestWatchdog();
2227
+ if (this.isStale(epoch, generation)) {
2228
+ this.logOrchestratorFunnel("stale_request_result_ignored", {
2229
+ requestId: requestToken,
2230
+ source: "edge"
2231
+ });
2232
+ return [
2233
+ 2,
2234
+ "stale"
2235
+ ];
2236
+ }
2237
+ remainingMs = this.timing.getRemainingAdMs();
2238
+ remainingAds = Math.max(1, Math.min(EDGE_POD_MAX_ADS, this.adConfig.resolvePodParams().maxAds - this.timing.totalAdsInBreak));
2239
+ podCount = adPlayer.limitPodToBudget ? adPlayer.limitPodToBudget(remainingMs, remainingAds) : (_ref = (_adPlayer_getPodAdCount = adPlayer.getPodAdCount) === null || _adPlayer_getPodAdCount === void 0 ? void 0 : _adPlayer_getPodAdCount.call(adPlayer)) !== null && _ref !== void 0 ? _ref : 0;
2240
+ if (classifyEdgeVastResponse({
2241
+ podAdCount: podCount
2242
+ }) !== "fill") {
2243
+ throw new Error("".concat(EDGE_POD_EXCEEDS_BUDGET, ": no Edge ad fits ").concat(remainingMs, "ms"));
2244
+ }
2245
+ this.setSlotState("READY", "edge_vast_ready");
2246
+ this.logOrchestratorFunnel("edge_fill", {
2247
+ podAdCount: podCount,
2248
+ podDurationMs: (_adPlayer_getPodTotalDurationMs = adPlayer.getPodTotalDurationMs) === null || _adPlayer_getPodTotalDurationMs === void 0 ? void 0 : _adPlayer_getPodTotalDurationMs.call(adPlayer),
2249
+ remainingMs: remainingMs
2250
+ });
2251
+ sendAdLoadedTracking(this.host.config.licenseKey, {
2252
+ source: "hls",
2253
+ vastUrl: requestUrl,
2254
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2255
+ }, this.analyticsContext).catch(function() {});
2256
+ this.timing.startAdFailsafeTimer(requestToken);
2257
+ return [
2258
+ 4,
2259
+ this.callController(function() {
2260
+ return adPlayer.play();
2261
+ })
2262
+ ];
2263
+ case 3:
2264
+ _state.sent();
2265
+ if (this.isStale(epoch, generation)) return [
2266
+ 2,
2267
+ "stale"
2268
+ ];
2269
+ if (this.timing.expectedAdBreakDurationMs != null && this.timing.getAdBreakEndWallClockMs() == null) {
2270
+ this.timing.scheduleAdStopAtBreakBoundary();
2271
+ }
2272
+ adPlayer.setAdVolume(getAdAudioVolume(adPlayer));
2273
+ return [
2274
+ 2,
2275
+ "fill"
2276
+ ];
2277
+ case 4:
2278
+ error = _state.sent();
2279
+ this.timing.clearAdRequestWatchdog();
2280
+ this.timing.clearAdFailsafeTimer();
2281
+ if (this.timing.activeAdRequestToken === requestToken) {
2282
+ this.timing.activeAdRequestToken = null;
2283
+ }
2284
+ errorMessage = error === null || error === void 0 ? void 0 : error.message;
2285
+ if (this.isStale(epoch, generation)) {
2286
+ this.logOrchestratorFunnel("stale_request_result_ignored", {
2287
+ requestId: requestToken,
2288
+ source: "edge",
2289
+ errorMessage: errorMessage
2290
+ });
2291
+ return [
2292
+ 2,
2293
+ "stale"
2294
+ ];
2295
+ }
2296
+ classification = classifyEdgeVastResponse({
2297
+ podAdCount: 0,
2298
+ error: error
2299
+ });
2300
+ diag = this.requestDiagnostics.get(requestToken);
2301
+ if (diag && !diag.failureReason) diag.failureReason = "edge_".concat(classification, ": ").concat(errorMessage);
2302
+ this.markEdgeExhausted("edge_".concat(classification), {
2303
+ errorMessage: errorMessage
2304
+ });
2305
+ return [
2306
+ 2,
2307
+ "fallback"
2308
+ ];
2309
+ case 5:
2310
+ return [
2311
+ 2
2312
+ ];
2313
+ }
2314
+ });
2315
+ }).call(this);
2316
+ }
2317
+ },
2318
+ {
2319
+ key: "continueAfterEdgeOutcome",
2320
+ value: function continueAfterEdgeOutcome(outcome, epoch) {
2321
+ if (outcome !== "fallback") return;
2322
+ if (!this.inAdBreak || epoch !== this.timing.adBreakEpoch) return;
2323
+ if (this.debug) {
2324
+ console.log("[AdBreak][EDGE] No usable Edge pod; checking with GAM for the remaining break");
2325
+ }
2326
+ this.setSlotState("NEEDS_NEXT_AD", "edge_fallback");
2327
+ this.requestNextAd("edge_fallback");
2328
+ }
2329
+ },
1909
2330
  {
1910
2331
  // ───────────────────────────────────────────────────────────────
1911
2332
  // Next-ad scheduling (single flight)
@@ -1961,7 +2382,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1961
2382
  value: function getStopRequestingReason(remainingMs) {
1962
2383
  if (this.breakSource === "vmap") {
1963
2384
  if (this.adPodQueue.length === 0) return "vmap_queue_empty";
1964
- } else if (this.breakSource !== "scte35" || !this.followUpBaseVastUrl) {
2385
+ } else if (this.breakSource !== "scte35" || !this.followUpBaseVastUrl && !this.canRequestEdge()) {
1965
2386
  return "no_follow_up_source";
1966
2387
  }
1967
2388
  if (this.timing.consecutiveFailures >= this.timing.maxConsecutiveFailures) {
@@ -1973,7 +2394,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
1973
2394
  if (this.timing.totalAdRequestsInBreak >= this.timing.maxTotalAdRequestsPerBreak) {
1974
2395
  return "request_cap";
1975
2396
  }
1976
- if (this.timing.isGamInCooldown()) {
2397
+ if (!this.canRequestEdge() && this.timing.isGamInCooldown()) {
1977
2398
  return "gam_cooldown";
1978
2399
  }
1979
2400
  if (this.isBreakTimeBounded() && remainingMs < this.getMinRemainingMsToRequestAd()) {
@@ -2067,10 +2488,33 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2067
2488
  key: "requestFollowUpAd",
2068
2489
  value: function requestFollowUpAd(trigger) {
2069
2490
  return _async_to_generator(function() {
2070
- var url, epoch, unused;
2491
+ var edgeUrl, epoch2, outcome, url, epoch, unused;
2071
2492
  return _ts_generator(this, function(_state) {
2072
2493
  switch(_state.label){
2073
2494
  case 0:
2495
+ if (!this.canRequestEdge()) return [
2496
+ 3,
2497
+ 2
2498
+ ];
2499
+ edgeUrl = this.prepareEdgePodRequest();
2500
+ if (!edgeUrl) {
2501
+ this.requestNextAd("".concat(trigger, ":edge_unavailable"));
2502
+ return [
2503
+ 2
2504
+ ];
2505
+ }
2506
+ epoch2 = this.timing.adBreakEpoch;
2507
+ return [
2508
+ 4,
2509
+ this.runEdgePod(edgeUrl, "edge_".concat(trigger))
2510
+ ];
2511
+ case 1:
2512
+ outcome = _state.sent();
2513
+ this.continueAfterEdgeOutcome(outcome, epoch2);
2514
+ return [
2515
+ 2
2516
+ ];
2517
+ case 2:
2074
2518
  if (this.breakSource === "vmap") {
2075
2519
  url = this.adPodQueue.shift();
2076
2520
  } else if (this.followUpBaseVastUrl) {
@@ -2081,32 +2525,32 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2081
2525
  ];
2082
2526
  this.noteVastUrlGenerated(ensureVideoAdType(url), trigger);
2083
2527
  epoch = this.timing.adBreakEpoch;
2084
- _state.label = 1;
2085
- case 1:
2528
+ _state.label = 3;
2529
+ case 3:
2086
2530
  _state.trys.push([
2087
- 1,
2088
2531
  3,
2532
+ 5,
2089
2533
  ,
2090
- 4
2534
+ 6
2091
2535
  ]);
2092
2536
  return [
2093
2537
  4,
2094
2538
  this.playSingleAd(url, trigger)
2095
2539
  ];
2096
- case 2:
2540
+ case 4:
2097
2541
  _state.sent();
2098
2542
  return [
2099
2543
  3,
2100
- 4
2544
+ 6
2101
2545
  ];
2102
- case 3:
2546
+ case 5:
2103
2547
  unused = _state.sent();
2104
2548
  this.continueAfterRequestFailure(epoch);
2105
2549
  return [
2106
2550
  3,
2107
- 4
2551
+ 6
2108
2552
  ];
2109
- case 4:
2553
+ case 6:
2110
2554
  return [
2111
2555
  2
2112
2556
  ];
@@ -2195,12 +2639,14 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2195
2639
  this.timing.failedVastUrls.add(vastTagUrl);
2196
2640
  }
2197
2641
  this.countFunnelStage("request_outcome:".concat(outcome));
2198
- this.logOrchestratorFunnel("request_outcome", {
2642
+ this.logOrchestratorFunnel("request_outcome", _object_spread({
2199
2643
  outcome: outcome,
2200
2644
  errorMessage: message,
2201
2645
  consecutiveFailures: this.timing.consecutiveFailures,
2202
- unusableResponsesInBreak: this.unusableResponsesInBreak
2203
- });
2646
+ unusableResponsesInBreak: this.unusableResponsesInBreak,
2647
+ vastAdCount: diag === null || diag === void 0 ? void 0 : diag.vastAdCount,
2648
+ impressionCount: diag === null || diag === void 0 ? void 0 : diag.impressionCount
2649
+ }, this.requestFloorFields(diag)));
2204
2650
  }
2205
2651
  },
2206
2652
  {
@@ -2217,7 +2663,11 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2217
2663
  case "gam_http_request_sent":
2218
2664
  diag.gamRequestSent++;
2219
2665
  break;
2666
+ case "edge_http_request_sent":
2667
+ diag.edgeRequestSent++;
2668
+ break;
2220
2669
  case "gam_response_received":
2670
+ case "edge_response_received":
2221
2671
  diag.vastHttpStatus = payload.httpStatus;
2222
2672
  break;
2223
2673
  case "vast_parsed":
@@ -2239,6 +2689,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2239
2689
  break;
2240
2690
  case "impression_fired":
2241
2691
  diag.impressionFired = true;
2692
+ diag.impressionCount++;
2242
2693
  break;
2243
2694
  default:
2244
2695
  if (REQUEST_FAILURE_STAGES.has(payload.stage) && !diag.failureReason) {
@@ -2295,6 +2746,67 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2295
2746
  }
2296
2747
  }
2297
2748
  },
2749
+ {
2750
+ key: "opportunityFloorFields",
2751
+ value: function opportunityFloorFields() {
2752
+ var floor = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : this.opportunityFloor;
2753
+ if (!floor) return {};
2754
+ return {
2755
+ floorStatus: floor.status,
2756
+ floorCents: floor.floorCents,
2757
+ floorSource: floor.source,
2758
+ floorReason: floor.reason,
2759
+ floorSelector: floor.selector,
2760
+ floorConfigVersion: floor.configurationVersion,
2761
+ adPosition: floor.position
2762
+ };
2763
+ }
2764
+ },
2765
+ {
2766
+ key: "requestFloorFields",
2767
+ value: function requestFloorFields(diag) {
2768
+ if (!diag || diag.floorApplied === void 0) return {};
2769
+ return {
2770
+ floorApplied: diag.floorApplied,
2771
+ floorCents: diag.floorCents,
2772
+ floorSource: diag.floorSource,
2773
+ floorReason: diag.floorReason,
2774
+ floorConfigVersion: diag.floorConfigVersion
2775
+ };
2776
+ }
2777
+ },
2778
+ {
2779
+ key: "applyOpportunityFloor",
2780
+ value: function applyOpportunityFloor(url) {
2781
+ var floor = this.opportunityFloor;
2782
+ if (!floor) return url;
2783
+ var result = applyFloorToVastUrl(url, floor);
2784
+ var diag = this.currentRequestId != null ? this.requestDiagnostics.get(this.currentRequestId) : void 0;
2785
+ if (diag) {
2786
+ diag.floorApplied = result.applied;
2787
+ if (result.applied && floor.floorCents != null) diag.floorCents = floor.floorCents;
2788
+ diag.floorSource = result.applied ? floor.source : "none";
2789
+ if (result.reason) diag.floorReason = result.reason;
2790
+ diag.floorConfigVersion = floor.configurationVersion;
2791
+ }
2792
+ if (floor.status === "skipped" && floor.reason === "disabled") {
2793
+ this.countFunnelStage("floor_skipped:disabled");
2794
+ return result.url;
2795
+ }
2796
+ this.logOrchestratorFunnel(result.applied ? "floor_applied" : "floor_skipped", {
2797
+ reason: result.reason,
2798
+ floorCents: result.applied ? floor.floorCents : void 0,
2799
+ floorSource: result.applied ? floor.source : "none",
2800
+ floorSelector: floor.selector,
2801
+ floorConfigVersion: floor.configurationVersion,
2802
+ adPosition: floor.position
2803
+ });
2804
+ if (!result.applied && result.reason) {
2805
+ this.countFunnelStage("floor_skipped:".concat(result.reason));
2806
+ }
2807
+ return result.url;
2808
+ }
2809
+ },
2298
2810
  {
2299
2811
  key: "countFunnelStage",
2300
2812
  value: function countFunnelStage(stage) {
@@ -2317,7 +2829,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2317
2829
  },
2318
2830
  {
2319
2831
  key: "noteOpportunity",
2320
- value: function noteOpportunity(source, breakKey) {
2832
+ value: function noteOpportunity(source, breakKey, position) {
2321
2833
  if (breakKey && breakKey === this.opportunityBreakKey && this.opportunityEpoch === this.timing.adBreakEpoch) {
2322
2834
  this.logOrchestratorFunnel("duplicate_break_start", {
2323
2835
  source: source,
@@ -2327,16 +2839,22 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2327
2839
  }
2328
2840
  this.opportunityBreakKey = breakKey;
2329
2841
  this.opportunityEpoch = this.timing.adBreakEpoch;
2330
- this.logOrchestratorFunnel("opportunity_created", {
2842
+ try {
2843
+ this.opportunityFloor = this.host.getFloorSnapshot(position);
2844
+ } catch (unused) {
2845
+ this.opportunityFloor = void 0;
2846
+ }
2847
+ this.logOrchestratorFunnel("opportunity_created", _object_spread({
2331
2848
  source: source,
2332
2849
  breakKey: breakKey,
2333
2850
  expectedDurationMs: this.timing.expectedAdBreakDurationMs
2334
- });
2851
+ }, this.opportunityFloorFields()));
2335
2852
  }
2336
2853
  },
2337
2854
  {
2338
2855
  key: "beginFunnelRequest",
2339
2856
  value: function beginFunnelRequest(adPlayer, requestToken, trigger) {
2857
+ var source = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : "gam";
2340
2858
  var _adPlayer_setFunnelContext;
2341
2859
  if (this.controllerCallInFlight) {
2342
2860
  this.logOrchestratorFunnel("duplicate_request_detected", {
@@ -2351,13 +2869,16 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2351
2869
  requestId: requestToken,
2352
2870
  controllerId: this.controllerGeneration,
2353
2871
  trigger: trigger,
2872
+ source: source,
2354
2873
  gamRequestSent: 0,
2874
+ edgeRequestSent: 0,
2355
2875
  wrapperDepth: 0,
2356
2876
  inlineResolved: false,
2357
2877
  mediaCandidateCount: 0,
2358
2878
  supportedMediaCount: 0,
2359
2879
  playStarted: false,
2360
2880
  impressionFired: false,
2881
+ impressionCount: 0,
2361
2882
  startedAt: Date.now()
2362
2883
  });
2363
2884
  (_adPlayer_setFunnelContext = adPlayer.setFunnelContext) === null || _adPlayer_setFunnelContext === void 0 ? void 0 : _adPlayer_setFunnelContext.call(adPlayer, {
@@ -2442,7 +2963,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2442
2963
  value: function playSingleAd(vastTagUrl) {
2443
2964
  var trigger = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "vmap";
2444
2965
  return _async_to_generator(function() {
2445
- var epoch, generation, requestToken, ap, requestUrl, _ap_getLoadedAdDuration, adDurationS, remainingMs, error;
2966
+ var epoch, generation, requestToken, ap, budgetedUrl, requestUrl, _ap_getLoadedAdDuration, adDurationS, remainingMs, error;
2446
2967
  return _ts_generator(this, function(_state) {
2447
2968
  switch(_state.label){
2448
2969
  case 0:
@@ -2480,10 +3001,12 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2480
3001
  requestToken = ++this.timing.adRequestTokenCounter;
2481
3002
  this.timing.activeAdRequestToken = requestToken;
2482
3003
  ap = this.host.getAdPlayer();
2483
- requestUrl = this.withCurrentPodBudget(vastTagUrl);
2484
- this.currentVastUrl = requestUrl;
3004
+ budgetedUrl = this.withCurrentPodBudget(vastTagUrl);
3005
+ this.currentPodSource = "gam";
2485
3006
  this.setSlotState("REQUESTING", trigger);
2486
3007
  this.beginFunnelRequest(ap, requestToken, trigger);
3008
+ requestUrl = this.applyOpportunityFloor(budgetedUrl);
3009
+ this.currentVastUrl = requestUrl;
2487
3010
  _state.label = 1;
2488
3011
  case 1:
2489
3012
  _state.trys.push([
@@ -2616,14 +3139,21 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2616
3139
  endReason: endReason
2617
3140
  });
2618
3141
  }
2619
- console.log("[AdBreak][FUNNEL] Break summary", {
3142
+ console.log("[AdBreak][FUNNEL] Break summary", _object_spread_props(_object_spread({
2620
3143
  breakId: breakId,
2621
3144
  endReason: endReason,
2622
- opportunities: count("opportunity_created"),
3145
+ opportunities: count("opportunity_created")
3146
+ }, this.opportunityFloorFields()), {
3147
+ floorAppliedRequests: count("floor_applied"),
3148
+ floorSkippedRequests: count("floor_skipped") + count("floor_skipped:disabled"),
2623
3149
  vastUrlsGenerated: count("vast_url_generated"),
2624
3150
  requests: requests,
2625
3151
  requestRetries: requestRetries,
2626
3152
  gamResponses: count("gam_response_received"),
3153
+ edgeRequests: count("edge_request_sent"),
3154
+ edgeFills: count("edge_fill"),
3155
+ edgeEmptySentinels: count("edge_empty_sentinel"),
3156
+ edgeFallbacks: countPrefix("edge_fallback:"),
2627
3157
  requestsSuppressed: count("request_suppressed"),
2628
3158
  duplicates: count("duplicate_request_detected") + count("duplicate_break_start"),
2629
3159
  filled: count("vast_received"),
@@ -2671,7 +3201,7 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2671
3201
  visibilityHidden: count("while_hidden"),
2672
3202
  playbackFailures: count("play_failed") + count("media_error") + count("hls_fatal") + count("stall") + count("decode_failure") + count("media_unsupported"),
2673
3203
  stages: funnel
2674
- });
3204
+ }));
2675
3205
  }
2676
3206
  },
2677
3207
  {
@@ -2720,8 +3250,10 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
2720
3250
  this.breakSource = void 0;
2721
3251
  this.followUpBaseVastUrl = void 0;
2722
3252
  this.unusableResponsesInBreak = 0;
3253
+ this.resetEdgeStateForBreak();
2723
3254
  adPlayer.stop().catch(function() {});
2724
3255
  if (!alreadyEnded) this.logBreakFunnel(reason);
3256
+ this.opportunityFloor = void 0;
2725
3257
  this.setSlotState("IDLE", "break_complete");
2726
3258
  var restoredMuted = this.host.getAdPlayer().getOriginalMutedState();
2727
3259
  var restoredVolume = this.host.getAdPlayer().getOriginalVolume();
@@ -3032,7 +3564,13 @@ var AdBreakOrchestrator = /*#__PURE__*/ function() {
3032
3564
  });
3033
3565
  return;
3034
3566
  }
3035
- this.timing.consecutiveFailures++;
3567
+ if (this.currentPodSource === "edge") {
3568
+ this.markEdgeExhausted("edge_playback_failed", {
3569
+ reason: reason
3570
+ });
3571
+ } else {
3572
+ this.timing.consecutiveFailures++;
3573
+ }
3036
3574
  var diag = this.currentRequestId != null ? this.requestDiagnostics.get(this.currentRequestId) : void 0;
3037
3575
  if (diag && !diag.failureReason) diag.failureReason = reason;
3038
3576
  if (this.debug) {