@streamlayer/feature-advertisement 1.1.0 → 1.1.1-rc.10.p6eb7304a

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 (103) hide show
  1. package/classic/package.json +5 -0
  2. package/lib/advertisement.d.ts +6 -6
  3. package/lib/advertisement.js +32 -17
  4. package/lib/cjs/advertisement.d.ts +64 -0
  5. package/lib/cjs/advertisement.js +322 -0
  6. package/lib/cjs/index.d.ts +21 -0
  7. package/lib/cjs/index.js +41 -0
  8. package/lib/cjs/package.json +1 -0
  9. package/lib/cjs/units/ad-macros.d.ts +7 -0
  10. package/lib/cjs/units/ad-macros.js +91 -0
  11. package/lib/cjs/units/external-ad.d.ts +127 -0
  12. package/lib/cjs/units/external-ad.js +421 -0
  13. package/lib/cjs/units/external-pause-ad.d.ts +50 -0
  14. package/lib/cjs/units/external-pause-ad.js +259 -0
  15. package/lib/cjs/units/gam.d.ts +26 -0
  16. package/lib/cjs/units/gam.js +152 -0
  17. package/lib/cjs/units/parse-vast-extensions.d.ts +26 -0
  18. package/lib/cjs/units/parse-vast-extensions.js +56 -0
  19. package/lib/cjs/units/pause-ad-card.d.ts +49 -0
  20. package/lib/cjs/units/pause-ad-card.js +136 -0
  21. package/lib/cjs/units/pause-ad.d.ts +16 -0
  22. package/lib/cjs/units/pause-ad.js +52 -0
  23. package/lib/cjs/units/sponsor-logo.d.ts +78 -0
  24. package/lib/cjs/units/sponsor-logo.js +194 -0
  25. package/lib/cjs/units/ssai-ad-break.d.ts +21 -0
  26. package/lib/cjs/units/ssai-ad-break.js +11 -0
  27. package/lib/cjs/units/ssai-creative.d.ts +89 -0
  28. package/lib/cjs/units/ssai-creative.js +136 -0
  29. package/lib/cjs/units/ssai-detector.d.ts +180 -0
  30. package/lib/cjs/units/ssai-detector.js +1172 -0
  31. package/lib/cjs/units/ssai-log.d.ts +16 -0
  32. package/lib/cjs/units/ssai-log.js +23 -0
  33. package/lib/cjs/units/ssai-preload.d.ts +20 -0
  34. package/lib/cjs/units/ssai-preload.js +62 -0
  35. package/lib/cjs/units/ssai-providers.d.ts +117 -0
  36. package/lib/cjs/units/ssai-providers.js +300 -0
  37. package/lib/cjs/units/vast-tracker.d.ts +45 -0
  38. package/lib/cjs/units/vast-tracker.js +64 -0
  39. package/lib/classic/_virtual/_rollupPluginBabelHelpers.js +71 -0
  40. package/lib/classic/advertisement.js +348 -0
  41. package/lib/classic/cjs/_virtual/_rollupPluginBabelHelpers.js +77 -0
  42. package/lib/classic/cjs/advertisement.js +350 -0
  43. package/lib/classic/cjs/index.js +49 -0
  44. package/lib/classic/cjs/package.json +1 -0
  45. package/lib/classic/cjs/units/ad-macros.js +115 -0
  46. package/lib/classic/cjs/units/external-ad.js +500 -0
  47. package/lib/classic/cjs/units/external-pause-ad.js +278 -0
  48. package/lib/classic/cjs/units/gam.js +173 -0
  49. package/lib/classic/cjs/units/parse-vast-extensions.js +60 -0
  50. package/lib/classic/cjs/units/pause-ad-card.js +134 -0
  51. package/lib/classic/cjs/units/pause-ad.js +72 -0
  52. package/lib/classic/cjs/units/sponsor-logo.js +201 -0
  53. package/lib/classic/cjs/units/ssai-ad-break.js +11 -0
  54. package/lib/classic/cjs/units/ssai-creative.js +142 -0
  55. package/lib/classic/cjs/units/ssai-detector.js +1195 -0
  56. package/lib/classic/cjs/units/ssai-log.js +23 -0
  57. package/lib/classic/cjs/units/ssai-preload.js +72 -0
  58. package/lib/classic/cjs/units/ssai-providers.js +361 -0
  59. package/lib/classic/cjs/units/vast-tracker.js +81 -0
  60. package/lib/classic/index.js +29 -0
  61. package/lib/classic/units/ad-macros.js +113 -0
  62. package/lib/classic/units/external-ad.js +491 -0
  63. package/lib/classic/units/external-pause-ad.js +275 -0
  64. package/lib/classic/units/gam.js +171 -0
  65. package/lib/classic/units/parse-vast-extensions.js +58 -0
  66. package/lib/classic/units/pause-ad-card.js +131 -0
  67. package/lib/classic/units/pause-ad.js +70 -0
  68. package/lib/classic/units/sponsor-logo.js +197 -0
  69. package/lib/classic/units/ssai-ad-break.js +9 -0
  70. package/lib/classic/units/ssai-creative.js +140 -0
  71. package/lib/classic/units/ssai-detector.js +1192 -0
  72. package/lib/classic/units/ssai-log.js +21 -0
  73. package/lib/classic/units/ssai-preload.js +68 -0
  74. package/lib/classic/units/ssai-providers.js +351 -0
  75. package/lib/classic/units/vast-tracker.js +77 -0
  76. package/lib/index.d.ts +15 -13
  77. package/lib/index.js +9 -8
  78. package/lib/units/ad-macros.d.ts +7 -0
  79. package/lib/units/ad-macros.js +87 -0
  80. package/lib/units/external-ad.d.ts +71 -2
  81. package/lib/units/external-ad.js +94 -66
  82. package/lib/units/external-pause-ad.js +7 -1
  83. package/lib/units/gam.d.ts +11 -1
  84. package/lib/units/gam.js +65 -17
  85. package/lib/units/parse-vast-extensions.d.ts +10 -1
  86. package/lib/units/parse-vast-extensions.js +5 -1
  87. package/lib/units/pause-ad.d.ts +1 -1
  88. package/lib/units/pause-ad.js +1 -1
  89. package/lib/units/sponsor-logo.d.ts +78 -0
  90. package/lib/units/sponsor-logo.js +188 -0
  91. package/lib/units/ssai-ad-break.d.ts +1 -1
  92. package/lib/units/ssai-creative.d.ts +1 -1
  93. package/lib/units/ssai-creative.js +1 -1
  94. package/lib/units/ssai-detector.d.ts +1 -1
  95. package/lib/units/ssai-detector.js +27 -11
  96. package/lib/units/ssai-log.d.ts +16 -0
  97. package/lib/units/ssai-log.js +18 -0
  98. package/lib/units/ssai-preload.d.ts +1 -1
  99. package/lib/units/ssai-providers.d.ts +1 -1
  100. package/lib/units/ssai-providers.js +48 -11
  101. package/lib/units/vast-tracker.d.ts +1 -1
  102. package/lib/vast-client.d.ts +9 -0
  103. package/package.json +42 -9
@@ -0,0 +1,5 @@
1
+ {
2
+ "main": "../lib/classic/cjs/index.js",
3
+ "module": "../lib/classic/index.js",
4
+ "types": "../lib/cjs/index.d.ts"
5
+ }
@@ -1,10 +1,10 @@
1
1
  import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
- import { externalAd } from './units/external-ad';
3
- import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
4
- import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad';
5
- import { type SsaiProviderOption } from './units/ssai-providers';
6
- import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector';
7
- import { type SsaiAdBreakStore } from './units/ssai-ad-break';
2
+ import { externalAd } from './units/external-ad.js';
3
+ import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad.js';
4
+ import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad.js';
5
+ import { type SsaiProviderOption } from './units/ssai-providers.js';
6
+ import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector.js';
7
+ import { type SsaiAdBreakStore } from './units/ssai-ad-break.js';
8
8
  export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
9
9
  export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
10
10
  export type SsaiMonitorOptions = {
@@ -1,13 +1,14 @@
1
1
  import { AbstractFeatureUnit, createSingleStore, eventBus } from '@streamlayer/sdk-web-interfaces';
2
2
  import { createLogger } from '@streamlayer/sdk-web-logger';
3
- import { externalAd } from './units/external-ad';
4
- import { externalPauseAd } from './units/external-pause-ad';
5
- import { pauseAdListStore } from './units/pause-ad';
6
- import { SsaiCreativeCache } from './units/ssai-creative';
7
- import { resolveProvider } from './units/ssai-providers';
8
- import { createSsaiDetector, } from './units/ssai-detector';
9
- import { createSsaiAdBreakStore } from './units/ssai-ad-break';
10
- import { preloadCreativeAssets } from './units/ssai-preload';
3
+ import { externalAd } from './units/external-ad.js';
4
+ import { externalPauseAd } from './units/external-pause-ad.js';
5
+ import { pauseAdListStore } from './units/pause-ad.js';
6
+ import { SsaiCreativeCache } from './units/ssai-creative.js';
7
+ import { resolveProvider } from './units/ssai-providers.js';
8
+ import { createSsaiDetector, } from './units/ssai-detector.js';
9
+ import { createSsaiAdBreakStore } from './units/ssai-ad-break.js';
10
+ import { ssaiq } from './units/ssai-log.js';
11
+ import { preloadCreativeAssets } from './units/ssai-preload.js';
11
12
  export class Advertisement extends AbstractFeatureUnit {
12
13
  getExternalAd;
13
14
  getExternalPauseAd;
@@ -121,10 +122,25 @@ export class Advertisement extends AbstractFeatureUnit {
121
122
  const provider = resolveProvider(options.provider);
122
123
  const cache = new SsaiCreativeCache(provider);
123
124
  this.ssaiCache = cache;
125
+ /**
126
+ * Every beacon the SDK fires gets a line, with the URL it was fired at.
127
+ * The narrative log is read as a live trace of one ad, and "we called the
128
+ * ad server's own tracking URL" is as much of the story as the request
129
+ * that fetched the creative in the first place.
130
+ */
131
+ const logBeacon = (creativeId, event) => {
132
+ const url = cache.peek(creativeId)?.trackingEvents?.find((entry) => entry.event === event)?.url;
133
+ ssaiq.info({ creativeId, event, url }, `[beacon] ${event} fired — ${creativeId}`);
134
+ };
124
135
  this.fireSsaiImpression = (creativeId) => {
136
+ // The UI only calls this once the bar is actually rendered, so this is
137
+ // the moment the viewer saw the ad — not the moment it resolved.
138
+ ssaiq.info({ creativeId }, `[ad] ad on screen — ${cache.peek(creativeId)?.advertiser || creativeId}`);
139
+ logBeacon(creativeId, 'impression');
125
140
  cache.fireImpressionById(creativeId);
126
141
  };
127
142
  this.fireSsaiTracking = (creativeId, event) => {
143
+ logBeacon(creativeId, event);
128
144
  cache.fireTrackingById(creativeId, event);
129
145
  };
130
146
  this.fireSsaiCompanionClick = (creativeId) => {
@@ -144,12 +160,14 @@ export class Advertisement extends AbstractFeatureUnit {
144
160
  const prefetchCreative = (creativeId) => {
145
161
  cache
146
162
  .resolve(creativeId)
147
- .then((creative) => preloadCreativeAssets(creative))
148
- .then(() => {
163
+ .then((creative) => preloadCreativeAssets(creative).then(() => creative))
164
+ .then((creative) => {
149
165
  preloadedIds.add(creativeId);
166
+ ssaiq.info({ creativeId }, `[ad] creative ready — ${creative.advertiser} (artwork preloaded)`);
150
167
  })
151
- .catch((error) => {
152
- this.log.debug(error, 'ssai creative prefetch failed');
168
+ .catch(() => {
169
+ // Already on `ssaiq` from the provider; the break may still be
170
+ // minutes away, and `loadCreative` retries when it actually starts.
153
171
  });
154
172
  };
155
173
  const publishReady = (range, creative) => {
@@ -214,11 +232,9 @@ export class Advertisement extends AbstractFeatureUnit {
214
232
  videoRef: options.videoRef,
215
233
  manifestUrl: options.manifestUrl,
216
234
  onAdDiscovered: (creativeId) => {
217
- this.log.debug({ creativeId }, 'ssai ad discovered, prefetching creative');
218
235
  prefetchCreative(creativeId);
219
236
  },
220
237
  onBreakStart: (range) => {
221
- this.log.debug({ creativeId: range.creativeId }, 'ssai break start');
222
238
  eventBus.emit('ssaiAdBreak', {
223
239
  action: 'breakStart',
224
240
  payload: { creativeId: range.creativeId },
@@ -226,7 +242,6 @@ export class Advertisement extends AbstractFeatureUnit {
226
242
  loadCreative(range);
227
243
  },
228
244
  onCreativeChange: (range) => {
229
- this.log.debug({ creativeId: range.creativeId }, 'ssai creative change');
230
245
  loadCreative(range);
231
246
  },
232
247
  onAdEnding: (range) => {
@@ -239,11 +254,11 @@ export class Advertisement extends AbstractFeatureUnit {
239
254
  this.ssaiAdBreak.set({ ...current, status: 'ending' });
240
255
  },
241
256
  onProgress: (range, event) => {
242
- this.log.debug({ creativeId: range.creativeId, event }, 'ssai progress');
257
+ // Logged by the detector as `progress:<event>` at the moment playback
258
+ // crosses the marker; nothing to add here.
243
259
  cache.fireTrackingById(range.creativeId, event);
244
260
  },
245
261
  onBreakEnd: () => {
246
- this.log.debug('ssai break end');
247
262
  const current = this.ssaiAdBreak.get();
248
263
  eventBus.emit('ssaiAdBreak', {
249
264
  action: 'breakEnd',
@@ -0,0 +1,64 @@
1
+ import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { externalAd } from './units/external-ad.js';
3
+ import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad.js';
4
+ import { type PauseAdListOptions, type PauseAdStore } from './units/pause-ad.js';
5
+ import { type SsaiProviderOption } from './units/ssai-providers.js';
6
+ import { type HlsLike, type SsaiDebugSnapshot } from './units/ssai-detector.js';
7
+ import { type SsaiAdBreakStore } from './units/ssai-ad-break.js';
8
+ export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
9
+ export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
10
+ export type SsaiMonitorOptions = {
11
+ player?: HlsLike;
12
+ videoRef?: HTMLVideoElement;
13
+ manifestUrl?: string;
14
+ /** Creative provider: a ready instance, a built-in name, or `{ name, config }`. Defaults to `streamlayer`. */
15
+ provider?: SsaiProviderOption;
16
+ };
17
+ export declare class Advertisement extends AbstractFeatureUnit {
18
+ getExternalAd: (adUrl?: string) => [ExternalAdStore, string];
19
+ getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
20
+ getPauseAdList: (options: PauseAdListOptions) => PauseAdStore;
21
+ readonly ssaiAdBreak: SsaiAdBreakStore;
22
+ startSsaiMonitoring: (options: SsaiMonitorOptions) => void;
23
+ stopSsaiMonitoring: () => void;
24
+ /**
25
+ * Fire the impression tracking pixel for a creative. Called by the UI the
26
+ * moment the bar actually renders the creative, so "impression" means the
27
+ * viewer saw it (not merely that metadata resolved).
28
+ */
29
+ fireSsaiImpression: (creativeId: string) => void;
30
+ /**
31
+ * Fire a named tracking beacon for a creative (`clickThrough`, or any event
32
+ * the creative carries). Lifecycle beacons — `start`, the quartiles and
33
+ * `complete` — are fired automatically by the detector as playback crosses
34
+ * them; this is for the ones only the UI knows about.
35
+ */
36
+ fireSsaiTracking: (creativeId: string, event: string) => void;
37
+ /**
38
+ * Fire the companion's `clickTracking` beacons. Exposed for hosts that render
39
+ * the companion banner themselves — the TV overlay has no companion surface,
40
+ * so nothing in this SDK calls it.
41
+ */
42
+ fireSsaiCompanionClick: (creativeId: string) => void;
43
+ /**
44
+ * User-dismissed the current break: hide the bar now and keep it hidden for
45
+ * the rest of this break (a later break/creative shows again).
46
+ */
47
+ dismissSsaiBreak: () => void;
48
+ /**
49
+ * Read-only timing introspection of the running SSAI monitor (null while no
50
+ * monitor runs). Feeds the demo app's SSAI debug window; safe to poll.
51
+ */
52
+ getSsaiDebugSnapshot: () => SsaiDebugSnapshot | null;
53
+ private externalAdStores;
54
+ private externalPauseAdStores;
55
+ private externalPauseAdEnabled;
56
+ private pauseAdStore;
57
+ private pauseAdEnabled;
58
+ private ssaiDetector;
59
+ private ssaiCache;
60
+ private log;
61
+ constructor(instance: StreamLayerContext);
62
+ connect: () => void;
63
+ disconnect: () => void;
64
+ }
@@ -0,0 +1,322 @@
1
+ 'use strict';
2
+
3
+ var sdkWebInterfaces = require('@streamlayer/sdk-web-interfaces');
4
+ var sdkWebLogger = require('@streamlayer/sdk-web-logger');
5
+ var externalAd = require('./units/external-ad.js');
6
+ var externalPauseAd = require('./units/external-pause-ad.js');
7
+ var pauseAd = require('./units/pause-ad.js');
8
+ var ssaiCreative = require('./units/ssai-creative.js');
9
+ var ssaiProviders = require('./units/ssai-providers.js');
10
+ var ssaiDetector = require('./units/ssai-detector.js');
11
+ var ssaiAdBreak = require('./units/ssai-ad-break.js');
12
+ var ssaiLog = require('./units/ssai-log.js');
13
+ var ssaiPreload = require('./units/ssai-preload.js');
14
+
15
+ class Advertisement extends sdkWebInterfaces.AbstractFeatureUnit {
16
+ getExternalAd;
17
+ getExternalPauseAd;
18
+ getPauseAdList;
19
+ ssaiAdBreak = ssaiAdBreak.createSsaiAdBreakStore();
20
+ startSsaiMonitoring;
21
+ stopSsaiMonitoring;
22
+ /**
23
+ * Fire the impression tracking pixel for a creative. Called by the UI the
24
+ * moment the bar actually renders the creative, so "impression" means the
25
+ * viewer saw it (not merely that metadata resolved).
26
+ */
27
+ fireSsaiImpression;
28
+ /**
29
+ * Fire a named tracking beacon for a creative (`clickThrough`, or any event
30
+ * the creative carries). Lifecycle beacons — `start`, the quartiles and
31
+ * `complete` — are fired automatically by the detector as playback crosses
32
+ * them; this is for the ones only the UI knows about.
33
+ */
34
+ fireSsaiTracking;
35
+ /**
36
+ * Fire the companion's `clickTracking` beacons. Exposed for hosts that render
37
+ * the companion banner themselves — the TV overlay has no companion surface,
38
+ * so nothing in this SDK calls it.
39
+ */
40
+ fireSsaiCompanionClick;
41
+ /**
42
+ * User-dismissed the current break: hide the bar now and keep it hidden for
43
+ * the rest of this break (a later break/creative shows again).
44
+ */
45
+ dismissSsaiBreak;
46
+ /**
47
+ * Read-only timing introspection of the running SSAI monitor (null while no
48
+ * monitor runs). Feeds the demo app's SSAI debug window; safe to poll.
49
+ */
50
+ getSsaiDebugSnapshot;
51
+ externalAdStores = new Map();
52
+ externalPauseAdStores = new Map();
53
+ externalPauseAdEnabled = new Map();
54
+ pauseAdStore = null;
55
+ pauseAdEnabled = null;
56
+ ssaiDetector = null;
57
+ ssaiCache = null;
58
+ log;
59
+ constructor(instance) {
60
+ super();
61
+ this.log = sdkWebLogger.createLogger('advertisement');
62
+ const externalAdGenerator = externalAd.externalAd(instance);
63
+ const externalPauseAdGenerator = externalPauseAd.externalPauseAd(instance);
64
+ const pauseAdGenerator = pauseAd.pauseAdListStore(instance);
65
+ this.fireSsaiImpression = () => undefined;
66
+ this.fireSsaiTracking = () => undefined;
67
+ this.fireSsaiCompanionClick = () => undefined;
68
+ this.dismissSsaiBreak = () => undefined;
69
+ // Stable across start/stop cycles — always answers from the CURRENT detector.
70
+ this.getSsaiDebugSnapshot = () => this.ssaiDetector?.getDebugSnapshot() ?? null;
71
+ this.getPauseAdList = (options) => {
72
+ if (this.pauseAdStore) {
73
+ const $enabled = this.pauseAdEnabled;
74
+ const wasDisabled = $enabled.get() === false;
75
+ $enabled.set(options.$enabled);
76
+ if (wasDisabled && options.$enabled) {
77
+ window.requestAnimationFrame(() => {
78
+ if (this.pauseAdStore)
79
+ this.pauseAdStore.$list.revalidate();
80
+ });
81
+ }
82
+ return this.pauseAdStore;
83
+ }
84
+ const $enabled = sdkWebInterfaces.createSingleStore(options.$enabled);
85
+ this.pauseAdEnabled = $enabled;
86
+ this.pauseAdStore = pauseAdGenerator($enabled, options);
87
+ return this.pauseAdStore;
88
+ };
89
+ this.getExternalAd = (adUrl = '') => {
90
+ if (this.externalAdStores.has(adUrl)) {
91
+ const $store = this.externalAdStores.get(adUrl);
92
+ return [$store, adUrl];
93
+ }
94
+ const store = externalAdGenerator(adUrl);
95
+ this.externalAdStores.set(adUrl, store);
96
+ return [store, adUrl];
97
+ };
98
+ this.getExternalPauseAd = (adUrl, options) => {
99
+ // Store identity is (template, url): the template selects a DIFFERENT creative
100
+ // from the SAME tag, so the two must not share a store. Required because the
101
+ // cache-hit branch below applies only `$enabled` and drops the rest of `options`.
102
+ const storeKey = `${options.template ?? 'default'}::${adUrl}`;
103
+ if (this.externalPauseAdStores.has(storeKey)) {
104
+ const $enabled = this.externalPauseAdEnabled.get(storeKey);
105
+ const $store = this.externalPauseAdStores.get(storeKey);
106
+ const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
107
+ $enabled.set(options.$enabled);
108
+ if (shouldRevalidate) {
109
+ window.requestAnimationFrame(() => {
110
+ $store.revalidate();
111
+ });
112
+ }
113
+ // The 2nd element MUST stay the BARE url — `ExposedPauseAd` compares it to
114
+ // `vastUrlRef.current` (index.tsx:121) to scope store errors to the selected tag.
115
+ return [$store, adUrl];
116
+ }
117
+ const $enabled = sdkWebInterfaces.createSingleStore(options.$enabled);
118
+ const store = externalPauseAdGenerator(adUrl, $enabled, options);
119
+ this.externalPauseAdEnabled.set(storeKey, $enabled);
120
+ this.externalPauseAdStores.set(storeKey, store);
121
+ return [store, adUrl];
122
+ };
123
+ this.startSsaiMonitoring = (options) => {
124
+ this.stopSsaiMonitoring();
125
+ const provider = ssaiProviders.resolveProvider(options.provider);
126
+ const cache = new ssaiCreative.SsaiCreativeCache(provider);
127
+ this.ssaiCache = cache;
128
+ /**
129
+ * Every beacon the SDK fires gets a line, with the URL it was fired at.
130
+ * The narrative log is read as a live trace of one ad, and "we called the
131
+ * ad server's own tracking URL" is as much of the story as the request
132
+ * that fetched the creative in the first place.
133
+ */
134
+ const logBeacon = (creativeId, event) => {
135
+ const url = cache.peek(creativeId)?.trackingEvents?.find((entry) => entry.event === event)?.url;
136
+ ssaiLog.ssaiq.info({ creativeId, event, url }, `[beacon] ${event} fired — ${creativeId}`);
137
+ };
138
+ this.fireSsaiImpression = (creativeId) => {
139
+ // The UI only calls this once the bar is actually rendered, so this is
140
+ // the moment the viewer saw the ad — not the moment it resolved.
141
+ ssaiLog.ssaiq.info({ creativeId }, `[ad] ad on screen — ${cache.peek(creativeId)?.advertiser || creativeId}`);
142
+ logBeacon(creativeId, 'impression');
143
+ cache.fireImpressionById(creativeId);
144
+ };
145
+ this.fireSsaiTracking = (creativeId, event) => {
146
+ logBeacon(creativeId, event);
147
+ cache.fireTrackingById(creativeId, event);
148
+ };
149
+ this.fireSsaiCompanionClick = (creativeId) => {
150
+ cache.fireCompanionClickById(creativeId);
151
+ };
152
+ // Creative ids whose artwork preload has completed. Tracked apart from the
153
+ // cache: `peek` alone would let the bar show with a logo still in flight.
154
+ const preloadedIds = new Set();
155
+ /**
156
+ * Resolve + warm a creative the moment its id shows up in the manifest —
157
+ * typically seconds before its break starts, since the playhead trails the
158
+ * live edge. By break start the sync fast-path in `loadCreative` then flips
159
+ * the store straight to 'ready'. Failure here is only a debug note: the
160
+ * break may still be minutes away, and `loadCreative` retries (and warns)
161
+ * when the ad actually starts.
162
+ */
163
+ const prefetchCreative = (creativeId) => {
164
+ cache
165
+ .resolve(creativeId)
166
+ .then((creative) => ssaiPreload.preloadCreativeAssets(creative).then(() => creative))
167
+ .then((creative) => {
168
+ preloadedIds.add(creativeId);
169
+ ssaiLog.ssaiq.info({ creativeId }, `[ad] creative ready — ${creative.advertiser} (artwork preloaded)`);
170
+ })
171
+ .catch(() => {
172
+ // Already on `ssaiq` from the provider; the break may still be
173
+ // minutes away, and `loadCreative` retries when it actually starts.
174
+ });
175
+ };
176
+ const publishReady = (range, creative) => {
177
+ this.ssaiAdBreak.set({
178
+ creativeId: range.creativeId,
179
+ creative,
180
+ status: 'ready',
181
+ startMs: range.startMs,
182
+ durationSec: range.durationSec,
183
+ });
184
+ // "loaded" = metadata resolved. Impression (eventBus + pixel) fires
185
+ // from the UI when the bar is actually rendered, not here.
186
+ sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
187
+ action: 'vastLoaded',
188
+ payload: {
189
+ creativeId: creative.creativeId,
190
+ advertiser: creative.advertiser,
191
+ clickThroughUrl: creative.clickThroughUrl,
192
+ },
193
+ });
194
+ };
195
+ const loadCreative = (range) => {
196
+ // Fast path: prefetched at discovery, artwork warm — go straight to
197
+ // 'ready'. Skipping the 'detecting' hop matters for pods in particular:
198
+ // it momentarily empties the store, which the UI renders as the bar
199
+ // starting to close between two back-to-back ads.
200
+ const prefetched = cache.peek(range.creativeId);
201
+ if (prefetched && preloadedIds.has(range.creativeId)) {
202
+ publishReady(range, prefetched);
203
+ return;
204
+ }
205
+ this.ssaiAdBreak.set({
206
+ creativeId: range.creativeId,
207
+ status: 'detecting',
208
+ startMs: range.startMs,
209
+ durationSec: range.durationSec,
210
+ });
211
+ cache
212
+ .resolve(range.creativeId)
213
+ // Warm the artwork BEFORE flipping to 'ready', so the bar fades in with
214
+ // its logo already decoded instead of popping it in mid-animation. Capped
215
+ // internally, so a slow image host delays nothing.
216
+ .then((creative) => ssaiPreload.preloadCreativeAssets(creative).then(() => {
217
+ preloadedIds.add(range.creativeId);
218
+ return creative;
219
+ }))
220
+ .then((creative) => {
221
+ const current = this.ssaiAdBreak.get();
222
+ // Re-checked after the preload as well as the fetch: the break can end
223
+ // while either is in flight.
224
+ if (!current || current.creativeId !== range.creativeId) {
225
+ return;
226
+ }
227
+ publishReady(range, creative);
228
+ })
229
+ .catch((error) => {
230
+ this.log.warn(error, 'ssai creative resolution failed');
231
+ });
232
+ };
233
+ this.ssaiDetector = ssaiDetector.createSsaiDetector({
234
+ player: options.player,
235
+ videoRef: options.videoRef,
236
+ manifestUrl: options.manifestUrl,
237
+ onAdDiscovered: (creativeId) => {
238
+ prefetchCreative(creativeId);
239
+ },
240
+ onBreakStart: (range) => {
241
+ sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
242
+ action: 'breakStart',
243
+ payload: { creativeId: range.creativeId },
244
+ });
245
+ loadCreative(range);
246
+ },
247
+ onCreativeChange: (range) => {
248
+ loadCreative(range);
249
+ },
250
+ onAdEnding: (range) => {
251
+ const current = this.ssaiAdBreak.get();
252
+ // Only a bar currently showing this creative has anything to wind
253
+ // down — a dismissed or still-resolving break stays as it is.
254
+ if (!current || current.creativeId !== range.creativeId || current.status !== 'ready') {
255
+ return;
256
+ }
257
+ this.ssaiAdBreak.set({ ...current, status: 'ending' });
258
+ },
259
+ onProgress: (range, event) => {
260
+ // Logged by the detector as `progress:<event>` at the moment playback
261
+ // crosses the marker; nothing to add here.
262
+ cache.fireTrackingById(range.creativeId, event);
263
+ },
264
+ onBreakEnd: () => {
265
+ const current = this.ssaiAdBreak.get();
266
+ sdkWebInterfaces.eventBus.emit('ssaiAdBreak', {
267
+ action: 'breakEnd',
268
+ payload: { creativeId: current?.creativeId },
269
+ });
270
+ this.ssaiAdBreak.set(null);
271
+ },
272
+ });
273
+ this.dismissSsaiBreak = () => {
274
+ this.ssaiDetector?.dismissActiveBreak();
275
+ this.ssaiAdBreak.set(null);
276
+ };
277
+ };
278
+ this.stopSsaiMonitoring = () => {
279
+ if (this.ssaiDetector) {
280
+ this.ssaiDetector.destroy();
281
+ this.ssaiDetector = null;
282
+ }
283
+ if (this.ssaiCache) {
284
+ this.ssaiCache.destroy();
285
+ this.ssaiCache = null;
286
+ }
287
+ this.fireSsaiImpression = () => undefined;
288
+ this.fireSsaiTracking = () => undefined;
289
+ this.fireSsaiCompanionClick = () => undefined;
290
+ this.dismissSsaiBreak = () => undefined;
291
+ this.ssaiAdBreak.set(null);
292
+ };
293
+ }
294
+ connect = () => {
295
+ this.log.debug('connect');
296
+ };
297
+ disconnect = () => {
298
+ this.log.debug('disconnect');
299
+ this.stopSsaiMonitoring();
300
+ this.externalAdStores.forEach((store) => {
301
+ store.off();
302
+ });
303
+ this.externalAdStores.clear();
304
+ this.externalPauseAdStores.forEach((store) => {
305
+ store.off();
306
+ });
307
+ this.externalPauseAdEnabled.forEach((enabled) => {
308
+ enabled.off();
309
+ });
310
+ this.externalPauseAdEnabled.clear();
311
+ this.externalPauseAdStores.clear();
312
+ if (this.pauseAdStore) {
313
+ this.pauseAdStore.$list.off();
314
+ this.pauseAdStore.$index.off();
315
+ this.pauseAdEnabled?.off();
316
+ this.pauseAdStore = null;
317
+ this.pauseAdEnabled = null;
318
+ }
319
+ };
320
+ }
321
+
322
+ exports.Advertisement = Advertisement;
@@ -0,0 +1,21 @@
1
+ /// <reference path="../vast-client.d.ts" />
2
+ import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
3
+ declare module '@streamlayer/sdk-web-interfaces' {
4
+ interface StreamLayerSDK {
5
+ advertisement: import('./advertisement.js').Advertisement;
6
+ }
7
+ }
8
+ export { type ExternalAdStore, type ExternalPauseAdStore, type SsaiMonitorOptions } from './advertisement.js';
9
+ export { type GAMStaticCore, type PauseAdTemplate, isFullbleedVideoTemplate } from './units/external-pause-ad.js';
10
+ export { INSIGHT_BODY_MAX_LENGTH, type PauseAdCard, type PauseAdKind, type PauseAdMediaMode, } from './units/pause-ad-card.js';
11
+ export { type PauseAdStore } from './units/pause-ad.js';
12
+ export { type AdStore, type AdData, type AdCompanion, type AdNonLinear } from './units/external-ad.js';
13
+ export { selectSponsorLogo, type SponsorLogoAsset } from './units/sponsor-logo.js';
14
+ export { gam, type GamNonceResult, type PalNonceManager } from './units/gam.js';
15
+ export { createCompanionTracker, createVastTracker, resolveVastTrackerTarget, type VastTrackerTarget, } from './units/vast-tracker.js';
16
+ export { type SsaiCreative, type SsaiTrackingEvent, type SsaiCompanion, type SsaiProgressEvent, DEFAULT_CREATIVE_API_BASE, } from './units/ssai-creative.js';
17
+ export { type SsaiCreativeProvider, type SsaiProviderOption, type SsaiProviderName, type SsaiProviderConfig, type StreamLayerProviderConfig, type QuortexProviderConfig, createStreamLayerProvider, createQuortexProvider, createVastProvider, resolveProvider, fireBeacon, fireTrackingEvents, BUILTIN_PROVIDERS, } from './units/ssai-providers.js';
18
+ export { type SsaiDateRange, type SsaiTrackingMarker, type HlsLike, type SsaiClockSource, type SsaiDebugEvent, type SsaiDebugRange, type SsaiDebugSnapshot, } from './units/ssai-detector.js';
19
+ export { preloadImages, preloadCreativeAssets, DEFAULT_PRELOAD_TIMEOUT_MS } from './units/ssai-preload.js';
20
+ export { type SsaiAdBreakState, type SsaiAdBreakStatus, type SsaiAdBreakStore } from './units/ssai-ad-break.js';
21
+ export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var advertisement$1 = require('./advertisement.js');
4
+ var externalPauseAd = require('./units/external-pause-ad.js');
5
+ var pauseAdCard = require('./units/pause-ad-card.js');
6
+ var sponsorLogo = require('./units/sponsor-logo.js');
7
+ var gam = require('./units/gam.js');
8
+ var vastTracker = require('./units/vast-tracker.js');
9
+ var ssaiProviders = require('./units/ssai-providers.js');
10
+ var ssaiPreload = require('./units/ssai-preload.js');
11
+
12
+ const advertisement = (instance, _opts, done) => {
13
+ instance.sdk.advertisement = new advertisement$1.Advertisement(instance);
14
+ instance.sdk.onMount({ name: 'advertisement' }, () => {
15
+ instance.sdk.advertisement.connect();
16
+ return () => {
17
+ instance.sdk.advertisement.disconnect();
18
+ };
19
+ });
20
+ done();
21
+ };
22
+
23
+ exports.isFullbleedVideoTemplate = externalPauseAd.isFullbleedVideoTemplate;
24
+ exports.INSIGHT_BODY_MAX_LENGTH = pauseAdCard.INSIGHT_BODY_MAX_LENGTH;
25
+ exports.selectSponsorLogo = sponsorLogo.selectSponsorLogo;
26
+ exports.gam = gam.gam;
27
+ exports.createCompanionTracker = vastTracker.createCompanionTracker;
28
+ exports.createVastTracker = vastTracker.createVastTracker;
29
+ exports.resolveVastTrackerTarget = vastTracker.resolveVastTrackerTarget;
30
+ exports.BUILTIN_PROVIDERS = ssaiProviders.BUILTIN_PROVIDERS;
31
+ exports.DEFAULT_CREATIVE_API_BASE = ssaiProviders.DEFAULT_CREATIVE_API_BASE;
32
+ exports.createQuortexProvider = ssaiProviders.createQuortexProvider;
33
+ exports.createStreamLayerProvider = ssaiProviders.createStreamLayerProvider;
34
+ exports.createVastProvider = ssaiProviders.createVastProvider;
35
+ exports.fireBeacon = ssaiProviders.fireBeacon;
36
+ exports.fireTrackingEvents = ssaiProviders.fireTrackingEvents;
37
+ exports.resolveProvider = ssaiProviders.resolveProvider;
38
+ exports.DEFAULT_PRELOAD_TIMEOUT_MS = ssaiPreload.DEFAULT_PRELOAD_TIMEOUT_MS;
39
+ exports.preloadCreativeAssets = ssaiPreload.preloadCreativeAssets;
40
+ exports.preloadImages = ssaiPreload.preloadImages;
41
+ exports.advertisement = advertisement;
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,7 @@
1
+ import type { AdTagParams } from '@streamlayer/sdk-web-core';
2
+ /**
3
+ * Fill the macros this SDK knows in an ad-tag URL. One timestamp per call, so a tag naming the macro
4
+ * twice stays self-consistent. `params` is read as given — the caller passes the host's CURRENT
5
+ * values at request time.
6
+ */
7
+ export declare const substituteAdTagMacros: (url: string, params?: AdTagParams) => string;