@streamlayer/feature-advertisement 0.4.3 → 1.1.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.
@@ -1,19 +1,62 @@
1
1
  import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
- import { externalAd, type ExternalAdOptions } from './units/external-ad';
2
+ import { externalAd } from './units/external-ad';
3
3
  import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
4
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';
5
8
  export type ExternalAdStore = ReturnType<ReturnType<typeof externalAd>>;
6
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
+ };
7
17
  export declare class Advertisement extends AbstractFeatureUnit {
8
- getExternalAd: (adUrl: string, options: ExternalAdOptions) => [ExternalAdStore, string];
18
+ getExternalAd: (adUrl?: string) => [ExternalAdStore, string];
9
19
  getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
10
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;
11
53
  private externalAdStores;
12
- private externalAdEnabled;
13
54
  private externalPauseAdStores;
14
55
  private externalPauseAdEnabled;
15
56
  private pauseAdStore;
16
57
  private pauseAdEnabled;
58
+ private ssaiDetector;
59
+ private ssaiCache;
17
60
  private log;
18
61
  constructor(instance: StreamLayerContext);
19
62
  connect: () => void;
@@ -1,18 +1,56 @@
1
- import { AbstractFeatureUnit, createSingleStore } from '@streamlayer/sdk-web-interfaces';
1
+ import { AbstractFeatureUnit, createSingleStore, eventBus } from '@streamlayer/sdk-web-interfaces';
2
2
  import { createLogger } from '@streamlayer/sdk-web-logger';
3
3
  import { externalAd } from './units/external-ad';
4
4
  import { externalPauseAd } from './units/external-pause-ad';
5
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';
6
11
  export class Advertisement extends AbstractFeatureUnit {
7
12
  getExternalAd;
8
13
  getExternalPauseAd;
9
14
  getPauseAdList;
15
+ ssaiAdBreak = createSsaiAdBreakStore();
16
+ startSsaiMonitoring;
17
+ stopSsaiMonitoring;
18
+ /**
19
+ * Fire the impression tracking pixel for a creative. Called by the UI the
20
+ * moment the bar actually renders the creative, so "impression" means the
21
+ * viewer saw it (not merely that metadata resolved).
22
+ */
23
+ fireSsaiImpression;
24
+ /**
25
+ * Fire a named tracking beacon for a creative (`clickThrough`, or any event
26
+ * the creative carries). Lifecycle beacons — `start`, the quartiles and
27
+ * `complete` — are fired automatically by the detector as playback crosses
28
+ * them; this is for the ones only the UI knows about.
29
+ */
30
+ fireSsaiTracking;
31
+ /**
32
+ * Fire the companion's `clickTracking` beacons. Exposed for hosts that render
33
+ * the companion banner themselves — the TV overlay has no companion surface,
34
+ * so nothing in this SDK calls it.
35
+ */
36
+ fireSsaiCompanionClick;
37
+ /**
38
+ * User-dismissed the current break: hide the bar now and keep it hidden for
39
+ * the rest of this break (a later break/creative shows again).
40
+ */
41
+ dismissSsaiBreak;
42
+ /**
43
+ * Read-only timing introspection of the running SSAI monitor (null while no
44
+ * monitor runs). Feeds the demo app's SSAI debug window; safe to poll.
45
+ */
46
+ getSsaiDebugSnapshot;
10
47
  externalAdStores = new Map();
11
- externalAdEnabled = new Map();
12
48
  externalPauseAdStores = new Map();
13
49
  externalPauseAdEnabled = new Map();
14
50
  pauseAdStore = null;
15
51
  pauseAdEnabled = null;
52
+ ssaiDetector = null;
53
+ ssaiCache = null;
16
54
  log;
17
55
  constructor(instance) {
18
56
  super();
@@ -20,6 +58,12 @@ export class Advertisement extends AbstractFeatureUnit {
20
58
  const externalAdGenerator = externalAd(instance);
21
59
  const externalPauseAdGenerator = externalPauseAd(instance);
22
60
  const pauseAdGenerator = pauseAdListStore(instance);
61
+ this.fireSsaiImpression = () => undefined;
62
+ this.fireSsaiTracking = () => undefined;
63
+ this.fireSsaiCompanionClick = () => undefined;
64
+ this.dismissSsaiBreak = () => undefined;
65
+ // Stable across start/stop cycles — always answers from the CURRENT detector.
66
+ this.getSsaiDebugSnapshot = () => this.ssaiDetector?.getDebugSnapshot() ?? null;
23
67
  this.getPauseAdList = (options) => {
24
68
  if (this.pauseAdStore) {
25
69
  const $enabled = this.pauseAdEnabled;
@@ -38,29 +82,23 @@ export class Advertisement extends AbstractFeatureUnit {
38
82
  this.pauseAdStore = pauseAdGenerator($enabled, options);
39
83
  return this.pauseAdStore;
40
84
  };
41
- this.getExternalAd = (adUrl, options) => {
85
+ this.getExternalAd = (adUrl = '') => {
42
86
  if (this.externalAdStores.has(adUrl)) {
43
- const $enabled = this.externalAdEnabled.get(adUrl);
44
87
  const $store = this.externalAdStores.get(adUrl);
45
- const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
46
- $enabled.set(options.$enabled);
47
- if (shouldRevalidate) {
48
- window.requestAnimationFrame(() => {
49
- $store.revalidate();
50
- });
51
- }
52
88
  return [$store, adUrl];
53
89
  }
54
- const $enabled = createSingleStore(options.$enabled);
55
- const store = externalAdGenerator(adUrl, $enabled, options);
56
- this.externalAdEnabled.set(adUrl, $enabled);
90
+ const store = externalAdGenerator(adUrl);
57
91
  this.externalAdStores.set(adUrl, store);
58
92
  return [store, adUrl];
59
93
  };
60
94
  this.getExternalPauseAd = (adUrl, options) => {
61
- if (this.externalPauseAdStores.has(adUrl)) {
62
- const $enabled = this.externalPauseAdEnabled.get(adUrl);
63
- const $store = this.externalPauseAdStores.get(adUrl);
95
+ // Store identity is (template, url): the template selects a DIFFERENT creative
96
+ // from the SAME tag, so the two must not share a store. Required because the
97
+ // cache-hit branch below applies only `$enabled` and drops the rest of `options`.
98
+ const storeKey = `${options.template ?? 'default'}::${adUrl}`;
99
+ if (this.externalPauseAdStores.has(storeKey)) {
100
+ const $enabled = this.externalPauseAdEnabled.get(storeKey);
101
+ const $store = this.externalPauseAdStores.get(storeKey);
64
102
  const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
65
103
  $enabled.set(options.$enabled);
66
104
  if (shouldRevalidate) {
@@ -68,27 +106,182 @@ export class Advertisement extends AbstractFeatureUnit {
68
106
  $store.revalidate();
69
107
  });
70
108
  }
109
+ // The 2nd element MUST stay the BARE url — `ExposedPauseAd` compares it to
110
+ // `vastUrlRef.current` (index.tsx:121) to scope store errors to the selected tag.
71
111
  return [$store, adUrl];
72
112
  }
73
113
  const $enabled = createSingleStore(options.$enabled);
74
114
  const store = externalPauseAdGenerator(adUrl, $enabled, options);
75
- this.externalPauseAdEnabled.set(adUrl, $enabled);
76
- this.externalPauseAdStores.set(adUrl, store);
115
+ this.externalPauseAdEnabled.set(storeKey, $enabled);
116
+ this.externalPauseAdStores.set(storeKey, store);
77
117
  return [store, adUrl];
78
118
  };
119
+ this.startSsaiMonitoring = (options) => {
120
+ this.stopSsaiMonitoring();
121
+ const provider = resolveProvider(options.provider);
122
+ const cache = new SsaiCreativeCache(provider);
123
+ this.ssaiCache = cache;
124
+ this.fireSsaiImpression = (creativeId) => {
125
+ cache.fireImpressionById(creativeId);
126
+ };
127
+ this.fireSsaiTracking = (creativeId, event) => {
128
+ cache.fireTrackingById(creativeId, event);
129
+ };
130
+ this.fireSsaiCompanionClick = (creativeId) => {
131
+ cache.fireCompanionClickById(creativeId);
132
+ };
133
+ // Creative ids whose artwork preload has completed. Tracked apart from the
134
+ // cache: `peek` alone would let the bar show with a logo still in flight.
135
+ const preloadedIds = new Set();
136
+ /**
137
+ * Resolve + warm a creative the moment its id shows up in the manifest —
138
+ * typically seconds before its break starts, since the playhead trails the
139
+ * live edge. By break start the sync fast-path in `loadCreative` then flips
140
+ * the store straight to 'ready'. Failure here is only a debug note: the
141
+ * break may still be minutes away, and `loadCreative` retries (and warns)
142
+ * when the ad actually starts.
143
+ */
144
+ const prefetchCreative = (creativeId) => {
145
+ cache
146
+ .resolve(creativeId)
147
+ .then((creative) => preloadCreativeAssets(creative))
148
+ .then(() => {
149
+ preloadedIds.add(creativeId);
150
+ })
151
+ .catch((error) => {
152
+ this.log.debug(error, 'ssai creative prefetch failed');
153
+ });
154
+ };
155
+ const publishReady = (range, creative) => {
156
+ this.ssaiAdBreak.set({
157
+ creativeId: range.creativeId,
158
+ creative,
159
+ status: 'ready',
160
+ startMs: range.startMs,
161
+ durationSec: range.durationSec,
162
+ });
163
+ // "loaded" = metadata resolved. Impression (eventBus + pixel) fires
164
+ // from the UI when the bar is actually rendered, not here.
165
+ eventBus.emit('ssaiAdBreak', {
166
+ action: 'vastLoaded',
167
+ payload: {
168
+ creativeId: creative.creativeId,
169
+ advertiser: creative.advertiser,
170
+ clickThroughUrl: creative.clickThroughUrl,
171
+ },
172
+ });
173
+ };
174
+ const loadCreative = (range) => {
175
+ // Fast path: prefetched at discovery, artwork warm — go straight to
176
+ // 'ready'. Skipping the 'detecting' hop matters for pods in particular:
177
+ // it momentarily empties the store, which the UI renders as the bar
178
+ // starting to close between two back-to-back ads.
179
+ const prefetched = cache.peek(range.creativeId);
180
+ if (prefetched && preloadedIds.has(range.creativeId)) {
181
+ publishReady(range, prefetched);
182
+ return;
183
+ }
184
+ this.ssaiAdBreak.set({
185
+ creativeId: range.creativeId,
186
+ status: 'detecting',
187
+ startMs: range.startMs,
188
+ durationSec: range.durationSec,
189
+ });
190
+ cache
191
+ .resolve(range.creativeId)
192
+ // Warm the artwork BEFORE flipping to 'ready', so the bar fades in with
193
+ // its logo already decoded instead of popping it in mid-animation. Capped
194
+ // internally, so a slow image host delays nothing.
195
+ .then((creative) => preloadCreativeAssets(creative).then(() => {
196
+ preloadedIds.add(range.creativeId);
197
+ return creative;
198
+ }))
199
+ .then((creative) => {
200
+ const current = this.ssaiAdBreak.get();
201
+ // Re-checked after the preload as well as the fetch: the break can end
202
+ // while either is in flight.
203
+ if (!current || current.creativeId !== range.creativeId) {
204
+ return;
205
+ }
206
+ publishReady(range, creative);
207
+ })
208
+ .catch((error) => {
209
+ this.log.warn(error, 'ssai creative resolution failed');
210
+ });
211
+ };
212
+ this.ssaiDetector = createSsaiDetector({
213
+ player: options.player,
214
+ videoRef: options.videoRef,
215
+ manifestUrl: options.manifestUrl,
216
+ onAdDiscovered: (creativeId) => {
217
+ this.log.debug({ creativeId }, 'ssai ad discovered, prefetching creative');
218
+ prefetchCreative(creativeId);
219
+ },
220
+ onBreakStart: (range) => {
221
+ this.log.debug({ creativeId: range.creativeId }, 'ssai break start');
222
+ eventBus.emit('ssaiAdBreak', {
223
+ action: 'breakStart',
224
+ payload: { creativeId: range.creativeId },
225
+ });
226
+ loadCreative(range);
227
+ },
228
+ onCreativeChange: (range) => {
229
+ this.log.debug({ creativeId: range.creativeId }, 'ssai creative change');
230
+ loadCreative(range);
231
+ },
232
+ onAdEnding: (range) => {
233
+ const current = this.ssaiAdBreak.get();
234
+ // Only a bar currently showing this creative has anything to wind
235
+ // down — a dismissed or still-resolving break stays as it is.
236
+ if (!current || current.creativeId !== range.creativeId || current.status !== 'ready') {
237
+ return;
238
+ }
239
+ this.ssaiAdBreak.set({ ...current, status: 'ending' });
240
+ },
241
+ onProgress: (range, event) => {
242
+ this.log.debug({ creativeId: range.creativeId, event }, 'ssai progress');
243
+ cache.fireTrackingById(range.creativeId, event);
244
+ },
245
+ onBreakEnd: () => {
246
+ this.log.debug('ssai break end');
247
+ const current = this.ssaiAdBreak.get();
248
+ eventBus.emit('ssaiAdBreak', {
249
+ action: 'breakEnd',
250
+ payload: { creativeId: current?.creativeId },
251
+ });
252
+ this.ssaiAdBreak.set(null);
253
+ },
254
+ });
255
+ this.dismissSsaiBreak = () => {
256
+ this.ssaiDetector?.dismissActiveBreak();
257
+ this.ssaiAdBreak.set(null);
258
+ };
259
+ };
260
+ this.stopSsaiMonitoring = () => {
261
+ if (this.ssaiDetector) {
262
+ this.ssaiDetector.destroy();
263
+ this.ssaiDetector = null;
264
+ }
265
+ if (this.ssaiCache) {
266
+ this.ssaiCache.destroy();
267
+ this.ssaiCache = null;
268
+ }
269
+ this.fireSsaiImpression = () => undefined;
270
+ this.fireSsaiTracking = () => undefined;
271
+ this.fireSsaiCompanionClick = () => undefined;
272
+ this.dismissSsaiBreak = () => undefined;
273
+ this.ssaiAdBreak.set(null);
274
+ };
79
275
  }
80
276
  connect = () => {
81
277
  this.log.debug('connect');
82
278
  };
83
279
  disconnect = () => {
84
280
  this.log.debug('disconnect');
281
+ this.stopSsaiMonitoring();
85
282
  this.externalAdStores.forEach((store) => {
86
283
  store.off();
87
284
  });
88
- this.externalAdEnabled.forEach((enabled) => {
89
- enabled.off();
90
- });
91
- this.externalAdEnabled.clear();
92
285
  this.externalAdStores.clear();
93
286
  this.externalPauseAdStores.forEach((store) => {
94
287
  store.off();
package/lib/index.d.ts CHANGED
@@ -4,8 +4,16 @@ declare module '@streamlayer/sdk-web-interfaces' {
4
4
  advertisement: import('./advertisement').Advertisement;
5
5
  }
6
6
  }
7
- export { type ExternalAdStore, type ExternalPauseAdStore } from './advertisement';
8
- export { type GAMStaticAdCore } from './units/external-ad';
9
- export { type GAMStaticCore } from './units/external-pause-ad';
10
- export { type PauseAdCard, type PauseAdStore } from './units/pause-ad';
7
+ export { type ExternalAdStore, type ExternalPauseAdStore, type SsaiMonitorOptions } from './advertisement';
8
+ export { type GAMStaticCore, type PauseAdTemplate, isFullbleedVideoTemplate } from './units/external-pause-ad';
9
+ export { INSIGHT_BODY_MAX_LENGTH, type PauseAdCard, type PauseAdKind, type PauseAdMediaMode, } from './units/pause-ad-card';
10
+ export { type PauseAdStore } from './units/pause-ad';
11
+ export { type AdStore, type AdData, type AdCompanion, type AdNonLinear } from './units/external-ad';
12
+ export { gam, type GamNonceResult, type PalNonceManager } from './units/gam';
13
+ export { createCompanionTracker, createVastTracker, resolveVastTrackerTarget, type VastTrackerTarget, } from './units/vast-tracker';
14
+ export { type SsaiCreative, type SsaiTrackingEvent, type SsaiCompanion, type SsaiProgressEvent, DEFAULT_CREATIVE_API_BASE, } from './units/ssai-creative';
15
+ 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';
16
+ export { type SsaiDateRange, type SsaiTrackingMarker, type HlsLike, type SsaiClockSource, type SsaiDebugEvent, type SsaiDebugRange, type SsaiDebugSnapshot, } from './units/ssai-detector';
17
+ export { preloadImages, preloadCreativeAssets, DEFAULT_PRELOAD_TIMEOUT_MS } from './units/ssai-preload';
18
+ export { type SsaiAdBreakState, type SsaiAdBreakStatus, type SsaiAdBreakStore } from './units/ssai-ad-break';
11
19
  export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
package/lib/index.js CHANGED
@@ -1,4 +1,16 @@
1
1
  import { Advertisement } from './advertisement';
2
+ export { isFullbleedVideoTemplate } from './units/external-pause-ad';
3
+ // Card shapes come straight from the pure module rather than through `units/pause-ad`. NOT a bundle
4
+ // guarantee for barrel consumers — this file value-imports `Advertisement` on line 3, which imports
5
+ // the store module anyway, and the package declares no `sideEffects: false`. What it does buy: the
6
+ // unit spec and any deep importer get the card shapes without the store, and dropping the
7
+ // `export *` keeps `units/pause-ad` from silently re-exporting `parsePauseAdCards`.
8
+ export { INSIGHT_BODY_MAX_LENGTH, } from './units/pause-ad-card';
9
+ export { gam } from './units/gam';
10
+ export { createCompanionTracker, createVastTracker, resolveVastTrackerTarget, } from './units/vast-tracker';
11
+ export { DEFAULT_CREATIVE_API_BASE, } from './units/ssai-creative';
12
+ export { createStreamLayerProvider, createQuortexProvider, createVastProvider, resolveProvider, fireBeacon, fireTrackingEvents, BUILTIN_PROVIDERS, } from './units/ssai-providers';
13
+ export { preloadImages, preloadCreativeAssets, DEFAULT_PRELOAD_TIMEOUT_MS } from './units/ssai-preload';
2
14
  export const advertisement = (instance, _opts, done) => {
3
15
  instance.sdk.advertisement = new Advertisement(instance);
4
16
  instance.sdk.onMount({ name: 'advertisement' }, () => {
@@ -1,19 +1,58 @@
1
- import { createSingleStore, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
1
+ import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { VastAd, VastCompanionAd, VastCreativeCompanion, VastCreativeLinear, VastCreativeNonLinear, VastNonLinearAd, VastResponse } from '@dailymotion/vast-client';
2
3
  import '@streamlayer/sdk-web-api';
3
- export type GAMStaticAdCore = {
4
- id: string;
5
- parentId: string;
6
- url: string;
7
- imageSrc?: string;
8
- videoSrc?: string;
9
- sponsorSrc?: string;
4
+ import { type PalNonceManager } from './gam';
5
+ import { VastCreative } from './parse-vast-extensions';
6
+ export type AdCompanion = {
7
+ imageUrl?: string;
8
+ clickUrl?: string;
9
+ width?: number;
10
+ height?: number;
11
+ /** The `<CompanionAds>` creative — the `VASTTracker` 3rd argument. */
12
+ creative?: VastCreativeCompanion;
13
+ /** The `<Companion>` variation — the 4th argument; carries this companion's own `creativeView`. */
14
+ variation?: VastCompanionAd;
15
+ };
16
+ /**
17
+ * First `<NonLinear>` variation of a `<NonLinearAds>` creative. Tags that ship this instead of a
18
+ * `<Linear>` creative make it the ad's main media, not a companion strip.
19
+ */
20
+ export type AdNonLinear = {
21
+ imageUrl: string;
22
+ clickUrl?: string;
23
+ width?: number;
24
+ height?: number;
25
+ /** The `<NonLinearAds>` creative — carries the creative-level `<TrackingEvents>`. */
26
+ creative: VastCreativeNonLinear;
27
+ /** The `<NonLinear>` variation — the `VASTTracker` 4th argument (click-through + its trackers). */
28
+ variation: VastNonLinearAd;
29
+ };
30
+ export type AdData = {
31
+ creative: VastCreativeLinear;
32
+ order: number;
33
+ mediaFileUrl?: string;
34
+ /** First companion creative (static image + click-through) — sources the L-Bar banner strip. */
35
+ companion?: AdCompanion;
36
+ /** The 300x75 `<Companion>` — the sponsor logo. Only set for a companion pair. */
37
+ logo?: AdCompanion;
38
+ /** Banner-sized `<Companion>` (468x60 class) — the sidebar media for companion-pair tags. */
39
+ mediaCompanion?: AdCompanion;
40
+ /** NonLinear (image-only) creative — present when the tag has no `<Linear>` media. */
41
+ nonLinear?: AdNonLinear;
42
+ /** VAST `<AdTitle>`. */
10
43
  title?: string;
44
+ /** VAST `<Description>`. */
11
45
  description?: string;
12
- adUrl?: string;
46
+ /** Linear `<ClickThrough>`, else `<NonLinearClickThrough>`, else `<CompanionClickThrough>`. */
47
+ clickThrough?: string;
48
+ ad: VastAd;
49
+ extension?: VastCreative;
50
+ raw: VastResponse;
13
51
  };
14
- export type ExternalAdOptions = {
15
- refetchInterval: number;
16
- prefetch: boolean;
17
- $enabled: boolean;
52
+ export type AdStore = {
53
+ ads: AdData[];
54
+ autoplay: boolean;
55
+ muted: boolean;
56
+ nonceManager?: PalNonceManager;
18
57
  };
19
- export declare const externalAd: (instance: StreamLayerContext) => (gamUrl: string, $enabled: ReturnType<typeof createSingleStore<boolean>>, options: ExternalAdOptions) => import("@nanostores/query").FetcherStore<GAMStaticAdCore | undefined, any>;
58
+ export declare const externalAd: (instance: StreamLayerContext) => (gamUrl: string) => import("@nanostores/query").FetcherStore<AdStore | undefined, any>;