@streamlayer/feature-advertisement 0.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.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # feature-advertisement
2
+
3
+ This library was generated with [Nx](https://nx.dev).
4
+
5
+ ## Building
6
+
7
+ Run `nx build feature-advertisement` to build the library.
@@ -0,0 +1,12 @@
1
+ import { AbstractFeatureUnit, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import { externalPauseAd, type ExternalPauseAdOptions } from './units/external-pause-ad';
3
+ export type ExternalPauseAdStore = ReturnType<ReturnType<typeof externalPauseAd>>;
4
+ export declare class Advertisement extends AbstractFeatureUnit {
5
+ getExternalPauseAd: (adUrl: string, options: ExternalPauseAdOptions) => [ExternalPauseAdStore, string];
6
+ private externalPauseAdStores;
7
+ private externalPauseAdEnabled;
8
+ private log;
9
+ constructor(instance: StreamLayerContext);
10
+ connect: () => void;
11
+ disconnect: () => void;
12
+ }
@@ -0,0 +1,49 @@
1
+ import { AbstractFeatureUnit, createSingleStore } from '@streamlayer/sdk-web-interfaces';
2
+ import { createLogger } from '@streamlayer/sdk-web-logger';
3
+ import { externalPauseAd } from './units/external-pause-ad';
4
+ export class Advertisement extends AbstractFeatureUnit {
5
+ getExternalPauseAd;
6
+ externalPauseAdStores = new Map();
7
+ externalPauseAdEnabled = new Map();
8
+ log;
9
+ constructor(instance) {
10
+ super();
11
+ this.log = createLogger('advertisement');
12
+ const externalPauseAdGenerator = externalPauseAd(instance);
13
+ this.getExternalPauseAd = (adUrl, options) => {
14
+ if (this.externalPauseAdStores.has(adUrl)) {
15
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
16
+ const $enabled = this.externalPauseAdEnabled.get(adUrl);
17
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
18
+ const $store = this.externalPauseAdStores.get(adUrl);
19
+ const shouldRevalidate = $enabled.get() === false && options.$enabled === true;
20
+ $enabled.set(options.$enabled);
21
+ if (shouldRevalidate) {
22
+ window.requestAnimationFrame(() => {
23
+ $store.revalidate();
24
+ });
25
+ }
26
+ return [$store, adUrl];
27
+ }
28
+ const $enabled = createSingleStore(options.$enabled);
29
+ const store = externalPauseAdGenerator(adUrl, $enabled, options);
30
+ this.externalPauseAdEnabled.set(adUrl, $enabled);
31
+ this.externalPauseAdStores.set(adUrl, store);
32
+ return [store, adUrl];
33
+ };
34
+ }
35
+ connect = () => {
36
+ this.log.debug('connect');
37
+ };
38
+ disconnect = () => {
39
+ this.log.debug('disconnect');
40
+ this.externalPauseAdStores.forEach((store) => {
41
+ store.off();
42
+ });
43
+ this.externalPauseAdEnabled.forEach((enabled) => {
44
+ enabled.off();
45
+ });
46
+ this.externalPauseAdEnabled.clear();
47
+ this.externalPauseAdStores.clear();
48
+ };
49
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ declare module '@streamlayer/sdk-web-interfaces' {
3
+ interface StreamLayerSDK {
4
+ advertisement: import('./advertisement').Advertisement;
5
+ }
6
+ }
7
+ export { type ExternalPauseAdStore } from './advertisement';
8
+ export { type GAMStaticCore } from './units/external-pause-ad';
9
+ export declare const advertisement: (instance: StreamLayerContext, _opts: unknown, done: () => void) => void;
package/lib/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import { Advertisement } from './advertisement';
2
+ export const advertisement = (instance, _opts, done) => {
3
+ instance.sdk.advertisement = new Advertisement(instance);
4
+ instance.sdk.onMount({ name: 'advertisement' }, () => {
5
+ instance.sdk.advertisement.connect();
6
+ return () => {
7
+ instance.sdk.advertisement.disconnect();
8
+ };
9
+ });
10
+ done();
11
+ };
@@ -0,0 +1,15 @@
1
+ import { createSingleStore, StreamLayerContext } from '@streamlayer/sdk-web-interfaces';
2
+ import '@streamlayer/sdk-web-api';
3
+ export type GAMStaticCore = {
4
+ id: string;
5
+ parentId: string;
6
+ url: string;
7
+ imageSrc?: string;
8
+ adUrl?: string;
9
+ };
10
+ export type ExternalPauseAdOptions = {
11
+ refetchInterval: number;
12
+ prefetch: boolean;
13
+ $enabled: boolean;
14
+ };
15
+ export declare const externalPauseAd: (instance: StreamLayerContext) => (gamUrl: string, $enabled: ReturnType<typeof createSingleStore<boolean>>, options: ExternalPauseAdOptions) => import("@nanostores/query").FetcherStore<GAMStaticCore | undefined, any>;
@@ -0,0 +1,96 @@
1
+ import { eventBus } from '@streamlayer/sdk-web-interfaces';
2
+ import { createLogger } from '@streamlayer/sdk-web-logger';
3
+ import '@streamlayer/sdk-web-api';
4
+ import { VASTClient } from '@dailymotion/vast-client';
5
+ const vastClient = new VASTClient();
6
+ const log = createLogger('ui:gam-static');
7
+ const preloadImage = (src) => {
8
+ return new Promise((resolve, reject) => {
9
+ const image = new Image();
10
+ image.onload = () => resolve(src);
11
+ image.onerror = () => reject();
12
+ image.src = src;
13
+ });
14
+ };
15
+ export const externalPauseAd = (instance) => {
16
+ return (gamUrl, $enabled, options) => instance.transport.nanoquery.createFetcherStore([gamUrl, $enabled], {
17
+ dedupeTime: options.prefetch ? options.refetchInterval : Infinity,
18
+ revalidateInterval: options.refetchInterval > 0 ? options.refetchInterval : 0,
19
+ fetcher: async (adUrl) => {
20
+ if (!adUrl || typeof adUrl !== 'string') {
21
+ throw new Error('no_valid_ad');
22
+ }
23
+ eventBus.emit('exposedPauseAd', {
24
+ action: 'load',
25
+ payload: {},
26
+ });
27
+ try {
28
+ const parsedVAST = await vastClient.get(adUrl);
29
+ const validAd = parsedVAST.ads.find((ads) => ads.creatives.length > 0);
30
+ if (validAd) {
31
+ const parentId = validAd.id || '';
32
+ log.debug(validAd, 'validAd');
33
+ const nonlinear = validAd.creatives.find((creative) => creative.type === 'nonlinear');
34
+ if (nonlinear) {
35
+ const id = nonlinear.adId || nonlinear.id || parentId;
36
+ log.debug(nonlinear, 'nonlinear');
37
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
38
+ // @ts-ignore
39
+ const staticResource = nonlinear.variations?.[0]?.staticResource;
40
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
+ // @ts-ignore
42
+ const adUrlTemplate = nonlinear.variations?.[0]?.nonlinearClickThroughURLTemplate;
43
+ if (staticResource) {
44
+ await preloadImage(staticResource);
45
+ eventBus.emit('exposedPauseAd', {
46
+ action: 'loaded',
47
+ payload: {
48
+ id,
49
+ parentId,
50
+ },
51
+ });
52
+ return {
53
+ id,
54
+ parentId,
55
+ url: adUrl,
56
+ imageSrc: staticResource,
57
+ adUrl: adUrlTemplate,
58
+ };
59
+ }
60
+ }
61
+ const companion = validAd.creatives.find((creative) => creative.type === 'companion');
62
+ log.debug(companion, 'companion');
63
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
64
+ // @ts-ignore
65
+ const staticResource = companion?.variations?.[0]?.staticResources?.[0]?.url;
66
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
67
+ // @ts-ignore
68
+ const adUrlTemplate = companion?.variations?.[0]?.companionClickThroughURLTemplate;
69
+ if (staticResource) {
70
+ const id = companion.adId || companion.id || parentId;
71
+ await preloadImage(staticResource);
72
+ eventBus.emit('exposedPauseAd', {
73
+ action: 'loaded',
74
+ payload: {
75
+ id,
76
+ parentId,
77
+ },
78
+ });
79
+ return {
80
+ id,
81
+ parentId,
82
+ url: adUrl,
83
+ imageSrc: staticResource,
84
+ adUrl: adUrlTemplate,
85
+ };
86
+ }
87
+ }
88
+ throw new Error('no_valid_ad');
89
+ }
90
+ catch (err) {
91
+ console.error(err, 'err');
92
+ throw new Error('no_valid_ad');
93
+ }
94
+ },
95
+ });
96
+ };
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@streamlayer/feature-advertisement",
3
+ "version": "0.1.0",
4
+ "peerDependencies": {
5
+ "@dailymotion/vast-client": "^6.4.2",
6
+ "@streamlayer/sl-eslib": "^5.228.0",
7
+ "nanostores": "^1.1.0",
8
+ "@streamlayer/sdk-web-core": "^1.19.0",
9
+ "@streamlayer/sdk-web-api": "^1.15.0",
10
+ "@streamlayer/sdk-web-interfaces": "^1.9.0",
11
+ "@streamlayer/sdk-web-types": "^1.16.9",
12
+ "@streamlayer/sdk-web-logger": "^1.0.94"
13
+ },
14
+ "devDependencies": {
15
+ "@types/vast-client": "^3.0.4"
16
+ },
17
+ "type": "module",
18
+ "main": "./lib/index.js",
19
+ "typings": "./lib/index.d.ts",
20
+ "files": [
21
+ "lib/",
22
+ "package.json"
23
+ ]
24
+ }