@streamlayer/feature-advertisement 1.1.1-rc.8.pd3aefe41 → 1.2.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.
Files changed (96) hide show
  1. package/lib/advertisement.d.ts +6 -6
  2. package/lib/advertisement.js +9 -9
  3. package/lib/index.d.ts +14 -15
  4. package/lib/index.js +9 -9
  5. package/lib/units/external-ad.d.ts +2 -2
  6. package/lib/units/external-ad.js +3 -3
  7. package/lib/units/external-pause-ad.js +1 -1
  8. package/lib/units/gam.js +1 -1
  9. package/lib/units/pause-ad.d.ts +1 -1
  10. package/lib/units/pause-ad.js +1 -1
  11. package/lib/units/sponsor-logo.d.ts +4 -12
  12. package/lib/units/sponsor-logo.js +3 -4
  13. package/lib/units/ssai-ad-break.d.ts +1 -1
  14. package/lib/units/ssai-creative.d.ts +1 -1
  15. package/lib/units/ssai-creative.js +1 -1
  16. package/lib/units/ssai-detector.d.ts +1 -1
  17. package/lib/units/ssai-detector.js +1 -1
  18. package/lib/units/ssai-preload.d.ts +1 -1
  19. package/lib/units/ssai-providers.d.ts +1 -1
  20. package/lib/units/ssai-providers.js +1 -1
  21. package/lib/units/vast-tracker.d.ts +1 -1
  22. package/package.json +9 -42
  23. package/classic/package.json +0 -5
  24. package/lib/cjs/advertisement.d.ts +0 -64
  25. package/lib/cjs/advertisement.js +0 -322
  26. package/lib/cjs/index.d.ts +0 -21
  27. package/lib/cjs/index.js +0 -41
  28. package/lib/cjs/package.json +0 -1
  29. package/lib/cjs/units/ad-macros.d.ts +0 -7
  30. package/lib/cjs/units/ad-macros.js +0 -91
  31. package/lib/cjs/units/external-ad.d.ts +0 -127
  32. package/lib/cjs/units/external-ad.js +0 -421
  33. package/lib/cjs/units/external-pause-ad.d.ts +0 -50
  34. package/lib/cjs/units/external-pause-ad.js +0 -259
  35. package/lib/cjs/units/gam.d.ts +0 -26
  36. package/lib/cjs/units/gam.js +0 -152
  37. package/lib/cjs/units/parse-vast-extensions.d.ts +0 -26
  38. package/lib/cjs/units/parse-vast-extensions.js +0 -56
  39. package/lib/cjs/units/pause-ad-card.d.ts +0 -49
  40. package/lib/cjs/units/pause-ad-card.js +0 -136
  41. package/lib/cjs/units/pause-ad.d.ts +0 -16
  42. package/lib/cjs/units/pause-ad.js +0 -52
  43. package/lib/cjs/units/sponsor-logo.d.ts +0 -78
  44. package/lib/cjs/units/sponsor-logo.js +0 -194
  45. package/lib/cjs/units/ssai-ad-break.d.ts +0 -21
  46. package/lib/cjs/units/ssai-ad-break.js +0 -11
  47. package/lib/cjs/units/ssai-creative.d.ts +0 -89
  48. package/lib/cjs/units/ssai-creative.js +0 -136
  49. package/lib/cjs/units/ssai-detector.d.ts +0 -180
  50. package/lib/cjs/units/ssai-detector.js +0 -1172
  51. package/lib/cjs/units/ssai-log.d.ts +0 -16
  52. package/lib/cjs/units/ssai-log.js +0 -23
  53. package/lib/cjs/units/ssai-preload.d.ts +0 -20
  54. package/lib/cjs/units/ssai-preload.js +0 -62
  55. package/lib/cjs/units/ssai-providers.d.ts +0 -117
  56. package/lib/cjs/units/ssai-providers.js +0 -300
  57. package/lib/cjs/units/vast-tracker.d.ts +0 -45
  58. package/lib/cjs/units/vast-tracker.js +0 -64
  59. package/lib/classic/_virtual/_rollupPluginBabelHelpers.js +0 -71
  60. package/lib/classic/advertisement.js +0 -348
  61. package/lib/classic/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -77
  62. package/lib/classic/cjs/advertisement.js +0 -350
  63. package/lib/classic/cjs/index.js +0 -49
  64. package/lib/classic/cjs/package.json +0 -1
  65. package/lib/classic/cjs/units/ad-macros.js +0 -115
  66. package/lib/classic/cjs/units/external-ad.js +0 -500
  67. package/lib/classic/cjs/units/external-pause-ad.js +0 -278
  68. package/lib/classic/cjs/units/gam.js +0 -173
  69. package/lib/classic/cjs/units/parse-vast-extensions.js +0 -60
  70. package/lib/classic/cjs/units/pause-ad-card.js +0 -134
  71. package/lib/classic/cjs/units/pause-ad.js +0 -72
  72. package/lib/classic/cjs/units/sponsor-logo.js +0 -201
  73. package/lib/classic/cjs/units/ssai-ad-break.js +0 -11
  74. package/lib/classic/cjs/units/ssai-creative.js +0 -142
  75. package/lib/classic/cjs/units/ssai-detector.js +0 -1195
  76. package/lib/classic/cjs/units/ssai-log.js +0 -23
  77. package/lib/classic/cjs/units/ssai-preload.js +0 -72
  78. package/lib/classic/cjs/units/ssai-providers.js +0 -361
  79. package/lib/classic/cjs/units/vast-tracker.js +0 -81
  80. package/lib/classic/index.js +0 -29
  81. package/lib/classic/units/ad-macros.js +0 -113
  82. package/lib/classic/units/external-ad.js +0 -491
  83. package/lib/classic/units/external-pause-ad.js +0 -275
  84. package/lib/classic/units/gam.js +0 -171
  85. package/lib/classic/units/parse-vast-extensions.js +0 -58
  86. package/lib/classic/units/pause-ad-card.js +0 -131
  87. package/lib/classic/units/pause-ad.js +0 -70
  88. package/lib/classic/units/sponsor-logo.js +0 -197
  89. package/lib/classic/units/ssai-ad-break.js +0 -9
  90. package/lib/classic/units/ssai-creative.js +0 -140
  91. package/lib/classic/units/ssai-detector.js +0 -1192
  92. package/lib/classic/units/ssai-log.js +0 -21
  93. package/lib/classic/units/ssai-preload.js +0 -68
  94. package/lib/classic/units/ssai-providers.js +0 -351
  95. package/lib/classic/units/vast-tracker.js +0 -77
  96. package/lib/vast-client.d.ts +0 -9
@@ -1,322 +0,0 @@
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;
@@ -1,21 +0,0 @@
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;
package/lib/cjs/index.js DELETED
@@ -1,41 +0,0 @@
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;
@@ -1 +0,0 @@
1
- {"type":"commonjs"}
@@ -1,7 +0,0 @@
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;
@@ -1,91 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Ad-tag macro substitution, for every ad server — not just GAM.
5
- *
6
- * Studio stores a programmatic tag verbatim (`gamRawUrl`, returned untouched by
7
- * `processGamAdvertisement`), so whatever macros the ad server's own tag builder emitted reach the
8
- * SDK literal. `gam()` used to fill only the GAM `[placeholder]` token, and only for
9
- * `pubads.g.doubleclick.net` — every other vendor's tag went out with its macros unexpanded.
10
- *
11
- * That is not inert. Measured against the ADITION tag on event 11089
12
- * (`…/banner?…&ts=[timestamp]&clickurl=[clickmacro]`): ADITION prepends the value of `clickurl` to
13
- * its own redi URL, so a literal `[clickmacro]` came back inside `<CompanionClickThrough>` as
14
- * `[clickmacro]https%3A%2F%2Fad1…` — a broken click-through. Sending `clickurl=` empty answers with
15
- * a valid redi URL.
16
- *
17
- * **Two sources.** The cache buster is the SDK's own. The consent family — `${GDPR}` and
18
- * `${GDPR_CONSENT_<gvlid>}`, IAB TCF, where the id is the vendor's Global Vendor List entry (39 is
19
- * ADITION / Virtual Minds) — and the click macro come from the host app (`AdTagParams`, set through
20
- * the `adTagParams` option or `sdk.setAdTagParams`). The SDK never reads a CMP and never guesses
21
- * consent: a value the host did not pass goes out EMPTY, which ad servers read as "unknown" — never
22
- * as `gdpr=0`. Empty rather than literal because a literal is not inert either: ADITION copied
23
- * `${GDPR}` into its own redirect URL (`gdpr%3D%24%7BGDPR%7D`). See
24
- * `docs/wiki/programmatic-ads-macros.md`.
25
- *
26
- * An unknown macro is left exactly as configured, which is what keeps the GAM `[placeholder]`
27
- * tokens for the PAL path (`gam.ts` fills those per parameter, not per token).
28
- */
29
- /**
30
- * All three spellings in the wild — `[NAME]` (IAB / SpotX), `${NAME}` (Google) and bare `{NAME}` —
31
- * each with its delimiters either literal or PERCENT-ENCODED.
32
- *
33
- * Neither half of that is theoretical. ADITION emitted `[timestamp]` / `[clickmacro]` for the
34
- * campaign on event 11089 and `{CACHEBUSTER}` / `{GDPR}` for the one on 6203 — same ad server, same
35
- * advertiser, different tag builder. And Studio stores the 6203 tag as `ts=%7BCACHEBUSTER%7D`, so
36
- * matching only literal braces made the whole substitution a silent no-op on it: an unexpanded
37
- * macro raises no error and the ad server still answers 200. `searchParams.get()` decodes on read,
38
- * which is what hides the encoding from anyone inspecting the parsed URL.
39
- *
40
- * The `$` is optional rather than a fourth branch — an unowned `${NAME}` still returns its full
41
- * matched text, `$` included, so the brace forms cannot corrupt each other.
42
- */
43
- const MACRO_PATTERN = /(?:\[|%5B)([a-z0-9_]+)(?:\]|%5D)|(?:\$|%24)?(?:\{|%7B)([a-z0-9_]+)(?:\}|%7D)/gi;
44
- /**
45
- * Every host value lands inside a query-string VALUE, so it is encoded exactly once. That matters
46
- * most for the click URL, which carries its own `?`/`&`: sent raw it re-opens the tag's query, the
47
- * way the malformed 6203 tag did (`clickurl` truncated, every following parameter duplicated).
48
- */
49
- const hostValue = (value) => (value ? encodeURIComponent(value) : '');
50
- const gdprConsent = ({ params }) => hostValue(params?.tcString);
51
- /**
52
- * Macro name (lower-cased) → the value this SDK sends for it.
53
- *
54
- * `clickurl` / `clickmacro` take the host's click tracker, which the ad server PREPENDS to its own
55
- * redirect — so the viewer's click passes host tracker → vendor redirect → landing page. The SDK
56
- * does not fire that tracker itself; it would count the click twice. Without one it is empty, the
57
- * documented "no player tracker" value. VAST click beacons are reported separately by
58
- * `useSidebarVastTracker`.
59
- */
60
- const MACRO_VALUES = {
61
- // One family, one value: every name here means "make this request unique so nothing caches it".
62
- // Epoch milliseconds satisfies that for all of them, and keeps the value already shipped for
63
- // `timestamp` unchanged. (IAB defines `[CACHEBUSTING]` as a random 8-digit number and
64
- // `[TIMESTAMP]` as ISO-8601, but those describe VAST *tracking* URLs; this substitution only
65
- // ever touches the ad REQUEST url, where every ad server documents the parameter as a number.)
66
- timestamp: ({ now }) => String(now),
67
- cachebuster: ({ now }) => String(now),
68
- cachebusting: ({ now }) => String(now),
69
- clickmacro: ({ params }) => hostValue(params?.clickUrl),
70
- clickurl: ({ params }) => hostValue(params?.clickUrl),
71
- // `1` / `0` only from an explicit boolean — an absent value is unknown, not "does not apply".
72
- gdpr: ({ params }) => (params?.gdprApplies === undefined ? '' : params.gdprApplies ? '1' : '0'),
73
- gdpr_consent: gdprConsent,
74
- };
75
- /** `GDPR_CONSENT_<gvlid>`: the vendor id names whose tag this is; the value is the same TC string. */
76
- const GDPR_CONSENT_FOR_VENDOR = /^gdpr_consent_\d+$/;
77
- const resolveMacro = (name) => MACRO_VALUES[name] ?? (GDPR_CONSENT_FOR_VENDOR.test(name) ? gdprConsent : undefined);
78
- /**
79
- * Fill the macros this SDK knows in an ad-tag URL. One timestamp per call, so a tag naming the macro
80
- * twice stays self-consistent. `params` is read as given — the caller passes the host's CURRENT
81
- * values at request time.
82
- */
83
- const substituteAdTagMacros = (url, params) => {
84
- const now = Date.now();
85
- return url.replace(MACRO_PATTERN, (match, bracketed, braced) => {
86
- const value = resolveMacro((bracketed ?? braced ?? '').toLowerCase());
87
- return value ? value({ now, params }) : match;
88
- });
89
- };
90
-
91
- exports.substituteAdTagMacros = substituteAdTagMacros;
@@ -1,127 +0,0 @@
1
- import type { AdTagParams } from '@streamlayer/sdk-web-core';
2
- import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
3
- import { VastAd, VastCompanionAd, VastCreativeCompanion, VastCreativeLinear, VastCreativeNonLinear, VastNonLinearAd, VastResponse } from '@dailymotion/vast-client';
4
- import '@streamlayer/sdk-web-api';
5
- import { type PalNonceManager } from './gam.js';
6
- import { VastCreative } from './parse-vast-extensions.js';
7
- export type AdCompanion = {
8
- imageUrl?: string;
9
- clickUrl?: string;
10
- width?: number;
11
- height?: number;
12
- /** The `<CompanionAds>` creative — the `VASTTracker` 3rd argument. */
13
- creative?: VastCreativeCompanion;
14
- /** The `<Companion>` variation — the 4th argument; carries this companion's own `creativeView`. */
15
- variation?: VastCompanionAd;
16
- };
17
- /**
18
- * First `<NonLinear>` variation of a `<NonLinearAds>` creative. Tags that ship this instead of a
19
- * `<Linear>` creative make it the ad's main media, not a companion strip.
20
- */
21
- export type AdNonLinear = {
22
- imageUrl: string;
23
- clickUrl?: string;
24
- width?: number;
25
- height?: number;
26
- /** The `<NonLinearAds>` creative — carries the creative-level `<TrackingEvents>`. */
27
- creative: VastCreativeNonLinear;
28
- /** The `<NonLinear>` variation — the `VASTTracker` 4th argument (click-through + its trackers). */
29
- variation: VastNonLinearAd;
30
- };
31
- export type AdData = {
32
- creative: VastCreativeLinear;
33
- order: number;
34
- mediaFileUrl?: string;
35
- /** First companion creative (static image + click-through) — sources the L-Bar banner strip. */
36
- companion?: AdCompanion;
37
- /** The 300x75 `<Companion>` — the sponsor logo. Only set for a companion pair. */
38
- logo?: AdCompanion;
39
- /**
40
- * The iOS `SLRVASTSlotRole.cardLogo` pick: companion-only, aspect-scored toward 1:1, and minus
41
- * whatever the hero slot already claimed. Consumed **only** by the hero-card placements
42
- * (Side-by-Side / Picture in Picture / Sponsor Frame) as a fallback for an otherwise empty
43
- * header logo. `logo` is the SC-335 display rule and is unchanged; when it fires, this stays
44
- * `undefined` because the display rule already won the slot.
45
- */
46
- cardLogo?: AdCompanion;
47
- /**
48
- * The tag's ONLY `<Companion>`, when it is exactly 300x75 and the hero is a Linear or a
49
- * NonLinear — i.e. the companion is logo-sized and nothing else needs it. The pair rule behind
50
- * `logo` exists to tell the logo from the media among companions; with the media coming from
51
- * elsewhere there is nothing to tell apart, and iOS fills its logo from a single companion.
52
- * Consumed only by the Standard Ad Sidebar (`createActiveAdFromVast`) as a header-logo fallback;
53
- * a companion-only tag leaves it `undefined`, because there the companion IS the media.
54
- */
55
- loneLogo?: AdCompanion;
56
- /** Banner-sized `<Companion>` (468x60 class) — the sidebar media for companion-pair tags. */
57
- mediaCompanion?: AdCompanion;
58
- /** NonLinear (image-only) creative — present when the tag has no `<Linear>` media. */
59
- nonLinear?: AdNonLinear;
60
- /** VAST `<AdTitle>`. */
61
- title?: string;
62
- /** VAST `<Description>`. */
63
- description?: string;
64
- /** Linear `<ClickThrough>`, else `<NonLinearClickThrough>`, else `<CompanionClickThrough>`. */
65
- clickThrough?: string;
66
- ad: VastAd;
67
- extension?: VastCreative;
68
- raw: VastResponse;
69
- };
70
- export type AdStore = {
71
- ads: AdData[];
72
- autoplay: boolean;
73
- muted: boolean;
74
- nonceManager?: PalNonceManager;
75
- };
76
- /**
77
- * Split a `<CompanionAds>` creative's variations into the logo and media slots.
78
- *
79
- * The pair mapping activates only for a tag that ships an exact 300x75 companion next to at
80
- * least one other. Deliberately conservative pending broader product sizes: on any other tag
81
- * no companion is promoted to the logo slot and the first one stays the media/strip candidate.
82
- *
83
- * Both halves of that gate are load-bearing and neither is an aspect rule: a 320x80 companion is
84
- * the same 4:1 shape as 300x75 and is still rejected, and a lone 300x75 companion is rejected too
85
- * (the Standard Ad Sidebar recovers that one as `AdData.loneLogo` when a Linear / NonLinear is
86
- * the media, so it is not needed here).
87
- * This is **narrower than iOS**, which has no size rule at all and fills its logo slot from a
88
- * single companion — see `docs/wiki/programmatic-ads-display-ios-parity.md`. Exported for
89
- * `external-ad.spec.ts` only; `src/index.ts` re-exports names explicitly, so this stays out of the
90
- * package's public API.
91
- */
92
- export declare const selectCompanions: (companions: AdCompanion[]) => {
93
- logo: undefined;
94
- media: AdCompanion;
95
- } | {
96
- logo: AdCompanion;
97
- media: AdCompanion | undefined;
98
- };
99
- /**
100
- * Map a parsed VAST response into our `AdData` shape. Shared by both acquisition
101
- * paths — the GAM/VMAP tag flow and the Prebid auction flow — so the two always
102
- * produce identical downstream data.
103
- *
104
- * Returns `undefined` for a no-ad, which is two distinct shapes: a response with no ad carrying
105
- * creatives, and an ad whose creatives carry no RENDERABLE asset — no linear `<MediaFile>`, no
106
- * `<NonLinear>` still and no `<Companion>` image. Both callers `filter(Boolean)` the result, so a
107
- * no-ad leaves `ads: []` and the card keeps whatever the server configured for it.
108
- *
109
- * Exported for `vast-mapping.spec.ts`, which drives it over the same fixture corpus the iOS suite
110
- * uses. `src/index.ts` re-exports names explicitly, so this stays out of the package's public API.
111
- */
112
- export declare const buildAdData: (parsedVAST: VastResponse, order: number) => AdData | undefined;
113
- /**
114
- * Build the OpenRTB request body for a single video auction. Placement config is
115
- * carried on the Prebid link's query string so the consumer-facing interface stays a
116
- * single URL string:
117
- * - `configId` → `imp.ext.prebid.storedrequest.id` (production stored request).
118
- * - `storedauctionresponse` → `imp.ext.prebid.storedauctionresponse.id` (staging mock).
119
- * - `test=1` → top-level `test` flag.
120
- * - `w` / `h` → video dimensions (default 1920×1080).
121
- *
122
- * The host's GDPR consent (`AdTagParams`) rides the OpenRTB 2.5 extension fields Prebid Server
123
- * reads — `regs.ext.gdpr` and `user.ext.consent` — the same signal the `${GDPR}` /
124
- * `${GDPR_CONSENT}` macros carry for a VAST tag. Each is sent only when the host passed it.
125
- */
126
- export declare const buildAuctionRequest: (url: URL, adTagParams?: AdTagParams) => Record<string, unknown>;
127
- export declare const externalAd: (instance: StreamLayerContext) => (gamUrl: string) => import("@nanostores/query", { with: { "resolution-mode": "import" } }).FetcherStore<AdStore | undefined, any>;