@glomex/integration-analytics 1.1480.1 → 1.1481.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 (44) hide show
  1. package/README.md +241 -12
  2. package/dist/base-event-mapper.d.ts +56 -0
  3. package/dist/base-event-mapper.js +313 -0
  4. package/dist/comscore/comscore-event-mapper.d.ts +26 -0
  5. package/dist/comscore/comscore-event-mapper.js +218 -0
  6. package/dist/comscore/comscore-metadata.d.ts +15 -0
  7. package/dist/comscore/comscore-metadata.js +58 -0
  8. package/dist/comscore/comscore-sdk-loader.d.ts +12 -0
  9. package/dist/comscore/comscore-sdk-loader.js +35 -0
  10. package/dist/comscore/comscore-types.d.ts +11 -0
  11. package/dist/comscore/comscore-types.js +6 -0
  12. package/dist/comscore/index.d.ts +72 -0
  13. package/dist/comscore/index.js +101 -0
  14. package/dist/nielsen/index.d.ts +70 -0
  15. package/dist/nielsen/index.js +87 -0
  16. package/dist/nielsen/nielsen-event-mapper.d.ts +23 -0
  17. package/dist/nielsen/nielsen-event-mapper.js +167 -0
  18. package/dist/nielsen/nielsen-metadata.d.ts +35 -0
  19. package/dist/nielsen/nielsen-metadata.js +134 -0
  20. package/dist/nielsen/nielsen-sdk-loader.d.ts +13 -0
  21. package/dist/nielsen/nielsen-sdk-loader.js +70 -0
  22. package/dist/nielsen/nielsen-types.d.ts +128 -0
  23. package/dist/nielsen/nielsen-types.js +4 -0
  24. package/dist/npaw/index.d.ts +7 -291
  25. package/dist/npaw/index.js +32 -313
  26. package/dist/npaw/npaw-turbo-player-ad-adapter.d.ts +43 -0
  27. package/dist/npaw/npaw-turbo-player-ad-adapter.js +142 -0
  28. package/dist/npaw/npaw-turbo-player-adapter.d.ts +47 -0
  29. package/dist/npaw/npaw-turbo-player-adapter.js +136 -0
  30. package/dist/npaw/npaw-types.d.ts +202 -0
  31. package/dist/npaw/npaw-types.js +6 -0
  32. package/dist/npaw/package-info.d.ts +2 -0
  33. package/dist/npaw/package-info.js +3 -0
  34. package/dist/sensic/index.d.ts +89 -0
  35. package/dist/sensic/index.js +103 -0
  36. package/dist/sensic/sensic-event-mapper.d.ts +25 -0
  37. package/dist/sensic/sensic-event-mapper.js +147 -0
  38. package/dist/sensic/sensic-metadata.d.ts +34 -0
  39. package/dist/sensic/sensic-metadata.js +102 -0
  40. package/dist/sensic/sensic-sdk-loader.d.ts +16 -0
  41. package/dist/sensic/sensic-sdk-loader.js +102 -0
  42. package/dist/sensic/sensic-types.d.ts +80 -0
  43. package/dist/sensic/sensic-types.js +6 -0
  44. package/package.json +18 -8
@@ -0,0 +1,103 @@
1
+ import { IntegrationEvent } from '@glomex/integration-web-component';
2
+ import { SensicEventMapper } from './sensic-event-mapper.js';
3
+ import { buildDefaultCustomParams, buildDefaultStreamId, createAtCustomParamsBuilder, createAtStreamIdBuilder } from './sensic-metadata.js';
4
+ import { buildSensicScriptUrl, initSensicS2S } from './sensic-sdk-loader.js';
5
+ /** IAB TCF vendor ID for Sensic */
6
+ const IAB_VENDOR_SENSIC = '758';
7
+ /**
8
+ * Connects Sensic analytics integration to a turbo player integration.
9
+ *
10
+ * Sensic (formerly GfK) provides streaming measurement for video content.
11
+ * This integration automatically tracks playback events and reports them
12
+ * to Sensic's S2S (Server-to-Server) measurement system.
13
+ *
14
+ * @param integration - The integration element to connect to
15
+ * @param options - Sensic configuration options
16
+ * @returns A cleanup function to disconnect the integration
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { connectToSensic } from '@glomex/integration-analytics/sensic';
21
+ *
22
+ * const disconnect = connectToSensic(integration, {
23
+ * media: 'your-media-id',
24
+ * platform: 'web',
25
+ * country: 'de'
26
+ * });
27
+ *
28
+ * // Later, to disconnect:
29
+ * disconnect();
30
+ * ```
31
+ */
32
+ export const connectToSensic = (integration, options) => {
33
+ const { media, platform, country, warnCallback } = options;
34
+ const scriptUrl = buildSensicScriptUrl(country, platform);
35
+ let initializationPromise;
36
+ let currentMapper;
37
+ const onWarn = (error) => {
38
+ if (warnCallback) {
39
+ warnCallback(error);
40
+ }
41
+ else {
42
+ setTimeout(() => {
43
+ // biome-ignore lint/suspicious/noConsole: Fallback error logging
44
+ console.error(error);
45
+ }, 0);
46
+ }
47
+ };
48
+ const initializeSensic = () => {
49
+ if (initializationPromise)
50
+ return initializationPromise;
51
+ initializationPromise = initSensicS2S({
52
+ media,
53
+ url: scriptUrl,
54
+ type: 'WEB'
55
+ }).catch((error) => {
56
+ onWarn(error instanceof Error ? error : new Error(String(error)));
57
+ return undefined;
58
+ });
59
+ return initializationPromise;
60
+ };
61
+ const hasConsent = () => {
62
+ const hasVendorConsent = Boolean(integration.consent?.vendorConsents?.[IAB_VENDOR_SENSIC]);
63
+ return hasVendorConsent;
64
+ };
65
+ const onContentSelect = () => {
66
+ if (currentMapper) {
67
+ currentMapper.destroy();
68
+ currentMapper = undefined;
69
+ }
70
+ if (!hasConsent())
71
+ return;
72
+ const customParamsBuilder = createCustomParamsBuilder();
73
+ if (customParamsBuilder() == null)
74
+ return;
75
+ // Start initialization if not already done, but don't await - events will be queued
76
+ const sdkReadyPromise = initializeSensic();
77
+ currentMapper = new SensicEventMapper(integration, sdkReadyPromise, onWarn, customParamsBuilder, createStreamIdBuilder());
78
+ };
79
+ function createCustomParamsBuilder() {
80
+ const internalBuilder = country.toLowerCase() === 'at'
81
+ ? createAtCustomParamsBuilder({ integration })
82
+ : buildDefaultCustomParams;
83
+ return () => {
84
+ const internalResult = internalBuilder();
85
+ return options.buildCustomParams ? options.buildCustomParams(internalResult) : internalResult;
86
+ };
87
+ }
88
+ function createStreamIdBuilder() {
89
+ const internalBuilder = country.toLowerCase() === 'at'
90
+ ? createAtStreamIdBuilder({ integration })
91
+ : () => buildDefaultStreamId(integration, integration.currentAd !== undefined);
92
+ return () => {
93
+ const internalResult = internalBuilder();
94
+ return options.buildStreamId ? options.buildStreamId(internalResult) : internalResult;
95
+ };
96
+ }
97
+ integration.addEventListener(IntegrationEvent.CONTENT_SELECT, onContentSelect);
98
+ return () => {
99
+ integration.removeEventListener(IntegrationEvent.CONTENT_SELECT, onContentSelect);
100
+ currentMapper?.destroy();
101
+ currentMapper = undefined;
102
+ };
103
+ };
@@ -0,0 +1,25 @@
1
+ import { type IntegrationElement } from '@glomex/integration-web-component';
2
+ import { BaseEventMapper } from '../base-event-mapper.js';
3
+ import type { SensicGlobal } from './sensic-types.js';
4
+ export declare class SensicEventMapper extends BaseEventMapper {
5
+ #private;
6
+ constructor(integration: IntegrationElement, sdkReadyPromise: Promise<SensicGlobal | undefined>, onWarn: (error: Error) => void, buildCustomParams: () => Record<string, string> | null | undefined, buildStreamId?: () => string);
7
+ protected onContentStart(): void;
8
+ protected onContentImpression(): void;
9
+ protected onContentPlay(): void;
10
+ protected onContentPause(): void;
11
+ protected onContentSeeking(): void;
12
+ protected onContentSeeked(): void;
13
+ protected onContentBufferingStart(): void;
14
+ protected onContentBufferingEnd(): void;
15
+ protected onContentEnded(): void;
16
+ protected onAdImpression(): void;
17
+ protected onAdPaused(): void;
18
+ protected onAdResumed(): void;
19
+ protected onAdBufferingStart(): void;
20
+ protected onAdBufferingEnd(): void;
21
+ protected onAdEnd(): void;
22
+ protected onPresentationModeChange(): void;
23
+ protected onVolumeChange(): void;
24
+ protected onDestroy(): void;
25
+ }
@@ -0,0 +1,147 @@
1
+ import { PlaybackMode, PresentationMode } from '@glomex/integration-web-component';
2
+ import { BaseEventMapper } from '../base-event-mapper.js';
3
+ export class SensicEventMapper extends BaseEventMapper {
4
+ #sensicSdk;
5
+ #buildCustomParams;
6
+ #buildStreamId;
7
+ #agent;
8
+ #isLive = false;
9
+ constructor(integration, sdkReadyPromise, onWarn, buildCustomParams, buildStreamId) {
10
+ super(integration, onWarn);
11
+ this.#buildCustomParams = buildCustomParams;
12
+ this.#buildStreamId = buildStreamId;
13
+ sdkReadyPromise.then((sensicSdk) => {
14
+ if (this.isStopped)
15
+ return;
16
+ if (!sensicSdk) {
17
+ this.onWarn(new Error('Sensic SDK not available after initialization'));
18
+ return;
19
+ }
20
+ this.#sensicSdk = sensicSdk;
21
+ this.markSdkReady();
22
+ });
23
+ }
24
+ onContentStart() {
25
+ if (!this.#sensicSdk)
26
+ return;
27
+ this.#isLive = this.integration.source?.playbackMode === PlaybackMode.LIVE;
28
+ // Create agent with position callback
29
+ this.#agent = this.#sensicSdk.getAgent(() => this.#isLive ? undefined : this.#currentTimeInMilliseconds);
30
+ }
31
+ onContentImpression() {
32
+ this.#triggerPlay(this.#currentTimeInMilliseconds);
33
+ }
34
+ onContentPlay() {
35
+ if (this.integration.seeking)
36
+ return;
37
+ this.#triggerPlay(this.#currentTimeInMilliseconds);
38
+ }
39
+ onContentPause() {
40
+ if (this.integration.seeking)
41
+ return;
42
+ this.#triggerStop(this.#currentTimeInMilliseconds);
43
+ }
44
+ onContentSeeking() {
45
+ if (!this.integration.paused) {
46
+ this.#triggerStop(this.#currentTimeInMilliseconds);
47
+ }
48
+ }
49
+ onContentSeeked() {
50
+ if (!this.integration.paused) {
51
+ this.#triggerPlay(this.#currentTimeInMilliseconds);
52
+ }
53
+ }
54
+ onContentBufferingStart() {
55
+ if (this.integration.seeking)
56
+ return;
57
+ this.#triggerStop(this.#currentTimeInMilliseconds);
58
+ }
59
+ onContentBufferingEnd() {
60
+ if (this.integration.seeking)
61
+ return;
62
+ this.#triggerPlay(this.#currentTimeInMilliseconds);
63
+ }
64
+ onContentEnded() {
65
+ this.#triggerStop(this.#currentTimeInMilliseconds);
66
+ }
67
+ onAdImpression() {
68
+ this.#triggerPlay(0);
69
+ }
70
+ onAdPaused() {
71
+ this.#triggerStop(this.#adCurrentTimeInMilliseconds);
72
+ }
73
+ onAdResumed() {
74
+ this.#triggerPlay(this.#adCurrentTimeInMilliseconds);
75
+ }
76
+ onAdBufferingStart() {
77
+ this.#triggerStop(this.#adCurrentTimeInMilliseconds);
78
+ }
79
+ onAdBufferingEnd() {
80
+ this.#triggerPlay(this.#adCurrentTimeInMilliseconds);
81
+ }
82
+ onAdEnd() {
83
+ this.#triggerStop(this.#adCurrentTimeInMilliseconds);
84
+ }
85
+ onPresentationModeChange() {
86
+ if (!this.#agent)
87
+ return;
88
+ this.#agent.screen(this.#isFullscreen ? '1' : '0');
89
+ }
90
+ onVolumeChange() {
91
+ if (!this.#agent)
92
+ return;
93
+ this.#agent.volume(this.#volume.toString());
94
+ }
95
+ #triggerPlay(position) {
96
+ if (!this.#agent)
97
+ return;
98
+ const customParams = this.#buildCustomParams() ?? {};
99
+ const contentId = 'default'; // Always 'default' as per integration guide
100
+ const isAd = this.isInAd;
101
+ const streamId = this.#buildStreamId
102
+ ? this.#buildStreamId()
103
+ : (() => {
104
+ const sourceOrContentId = this.integration.source?.id || this.integration.content?.id || '';
105
+ return isAd ? `${sourceOrContentId}-ad` : sourceOrContentId;
106
+ })();
107
+ const options = {
108
+ screen: this.#isFullscreen ? '1' : '0',
109
+ volume: this.#volume.toString()
110
+ };
111
+ if (this.#isLive && !isAd) {
112
+ const streamOffset = Math.round(Date.now() - position);
113
+ const contentStart = ''; // Always empty string
114
+ this.#agent.playStreamLive(contentId, contentStart, streamOffset, streamId, options, customParams);
115
+ }
116
+ else {
117
+ this.#agent.playStreamOnDemand(contentId, streamId, position, options, customParams);
118
+ }
119
+ }
120
+ #triggerStop(position) {
121
+ if (!this.#agent)
122
+ return;
123
+ this.#agent.stop(this.#isLive ? undefined : position);
124
+ }
125
+ get #currentTimeInMilliseconds() {
126
+ if (this.#isLive) {
127
+ return this.integration.wallClockTime * 1000;
128
+ }
129
+ return this.integration.currentTime * 1000;
130
+ }
131
+ get #adCurrentTimeInMilliseconds() {
132
+ const adTime = this.integration.adCurrentTime;
133
+ return (Number.isNaN(adTime) ? 0 : adTime) * 1000;
134
+ }
135
+ get #isFullscreen() {
136
+ return this.integration.presentationMode === PresentationMode.FULLSCREEN;
137
+ }
138
+ get #volume() {
139
+ return this.integration.muted ? 0 : Math.round(this.integration.volume * 100);
140
+ }
141
+ onDestroy() {
142
+ // Stop any ongoing playback tracking
143
+ if (this.#agent) {
144
+ this.#triggerStop(this.#currentTimeInMilliseconds);
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,34 @@
1
+ import { type IntegrationElement } from '@glomex/integration-web-component';
2
+ /**
3
+ * Build the default stream ID for Sensic tracking.
4
+ * Uses source ID if available, otherwise falls back to content ID.
5
+ *
6
+ * @param integration - The integration element containing source and content data
7
+ * @param isAd - Whether this is for an ad stream
8
+ * @returns The stream ID to use for tracking
9
+ */
10
+ export declare function buildDefaultStreamId(integration: IntegrationElement, isAd: boolean): string;
11
+ /**
12
+ * Build default custom parameters for Sensic tracking.
13
+ * Returns an empty object by default - consumers can provide their own buildCustomParams callback.
14
+ *
15
+ * @returns Empty custom parameters object
16
+ */
17
+ export declare function buildDefaultCustomParams(): Record<string, string>;
18
+ export interface SensicAtBuilderOptions {
19
+ integration: IntegrationElement;
20
+ }
21
+ /**
22
+ * Creates a custom params builder for Austria (GfK AT).
23
+ *
24
+ * @param options - The builder options containing integration and platform
25
+ * @returns A function that builds custom params for Sensic tracking, or undefined if tracking should be skipped
26
+ */
27
+ export declare function createAtCustomParamsBuilder(options: SensicAtBuilderOptions): () => Record<string, string>;
28
+ /**
29
+ * Creates a stream ID builder for Austria (GfK AT).
30
+ *
31
+ * @param options - The builder options containing integration
32
+ * @returns A function that builds the stream ID for Sensic tracking
33
+ */
34
+ export declare function createAtStreamIdBuilder(options: SensicAtBuilderOptions): () => string;
@@ -0,0 +1,102 @@
1
+ import { PlaybackMode } from '@glomex/integration-web-component';
2
+ /**
3
+ * Build the default stream ID for Sensic tracking.
4
+ * Uses source ID if available, otherwise falls back to content ID.
5
+ *
6
+ * @param integration - The integration element containing source and content data
7
+ * @param isAd - Whether this is for an ad stream
8
+ * @returns The stream ID to use for tracking
9
+ */
10
+ export function buildDefaultStreamId(integration, isAd) {
11
+ const sourceId = integration.source?.id ?? '';
12
+ const contentId = integration.content?.id ?? '';
13
+ const baseId = sourceId || contentId;
14
+ return isAd ? `${baseId}-ad` : baseId;
15
+ }
16
+ /**
17
+ * Build default custom parameters for Sensic tracking.
18
+ * Returns an empty object by default - consumers can provide their own buildCustomParams callback.
19
+ *
20
+ * @returns Empty custom parameters object
21
+ */
22
+ export function buildDefaultCustomParams() {
23
+ return {};
24
+ }
25
+ /**
26
+ * Creates a custom params builder for Austria (GfK AT).
27
+ *
28
+ * @param options - The builder options containing integration and platform
29
+ * @returns A function that builds custom params for Sensic tracking, or undefined if tracking should be skipped
30
+ */
31
+ export function createAtCustomParamsBuilder(options) {
32
+ const { integration } = options;
33
+ return () => {
34
+ const content = integration.content;
35
+ const source = integration.source;
36
+ const isLive = source?.playbackMode === PlaybackMode.LIVE;
37
+ const channelName = content?.channel?.name || '';
38
+ const tvShowId = content?.show?.id ||
39
+ content?.competition?.id ||
40
+ content?.compilation?.id ||
41
+ content?.additionalIds?.originId ||
42
+ content?.id;
43
+ const programName = content?.show?.name ||
44
+ content?.competition?.name ||
45
+ content?.compilation?.name ||
46
+ content?.title;
47
+ const duration = Math.floor(content?.duration === undefined ? integration.duration : content.duration) || '-1';
48
+ const livestreamId = isLive ? content?.id : content?.liveOnDemandChannel?.id;
49
+ const mediaId = isLive ? content?.id : source?.id || content?.id;
50
+ const mediaTitle = content?.title;
51
+ const externalId = content?.additionalIds?.externalId;
52
+ const ad = integration.currentAd;
53
+ const isAd = ad !== undefined;
54
+ const adDuration = ad?.duration?.toString() || '-1';
55
+ // GfK AT specific custom parameters
56
+ return {
57
+ /** Each channel gets a playerid assigned by Sensic */
58
+ playerid: '',
59
+ /** Channel/brand title */
60
+ channel: channelName,
61
+ /** Device identifier (e.g., 'WEB') */
62
+ deviceid: 'WEB',
63
+ /** Type of clip: 'Live', 'Sendung', 'preroll', 'midroll', 'postroll' */
64
+ cliptype: isAd ? ad.breakName || '' : isLive ? 'Live' : 'Sendung',
65
+ /** Video identifier */
66
+ videoid: isLive ? `${livestreamId}-24x7` : mediaId || '',
67
+ /** Episode external identifier */
68
+ episodeid: isLive ? '' : externalId || '',
69
+ /** Player duration in milliseconds, '-1' for live */
70
+ playerduration: isAd ? adDuration : isLive ? '-1' : duration.toString(),
71
+ /** Video duration in seconds, '-1' for live */
72
+ videoduration: isAd ? adDuration : isLive ? '-1' : duration.toString(),
73
+ /** Episode duration in milliseconds, '-1' for live */
74
+ episodeduration: isAd ? adDuration : isLive ? '-1' : duration.toString(),
75
+ /** Video title (asset title + media title) */
76
+ videotitle: isLive ? '' : [programName, mediaTitle].filter(Boolean).join(' '),
77
+ /** Program/show name */
78
+ programname: isLive ? '' : programName || '',
79
+ /** TV show/asset identifier */
80
+ tvshowid: isLive ? '' : tvShowId || ''
81
+ };
82
+ };
83
+ }
84
+ /**
85
+ * Creates a stream ID builder for Austria (GfK AT).
86
+ *
87
+ * @param options - The builder options containing integration
88
+ * @returns A function that builds the stream ID for Sensic tracking
89
+ */
90
+ export function createAtStreamIdBuilder(options) {
91
+ const { integration } = options;
92
+ return () => {
93
+ const ad = integration.currentAd;
94
+ if (ad) {
95
+ return `${ad.breakName || ''}${ad.breakIndex == null ? '' : ad.breakIndex}-${ad.positionIndex == null ? '' : ad.positionIndex}`;
96
+ }
97
+ const content = integration.content;
98
+ const source = integration.source;
99
+ const isLive = source?.playbackMode === PlaybackMode.LIVE;
100
+ return isLive ? content?.channel?.name || '' : content?.title || '';
101
+ };
102
+ }
@@ -0,0 +1,16 @@
1
+ import type { SensicConfig, SensicGlobal } from './sensic-types.js';
2
+ /**
3
+ * Builds the Sensic script URL based on country and platform.
4
+ * Script URLs:
5
+ * - AT Web: https://at-config.sensic.net/s2s-web.js
6
+ * - AT TV: https://at-config.sensic.net/ctv/s2s-web.js
7
+ * - DE Web: https://de-config.sensic.net/s2s-web.js
8
+ * - DE TV: https://de-config.sensic.net/ctv/s2s-web.js
9
+ */
10
+ export declare function buildSensicScriptUrl(country: string, platform: 'web' | 'tv'): string;
11
+ /**
12
+ * Initializes the Sensic S2S library.
13
+ * Loads the script and sets up the global gfkS2s object.
14
+ * This is the original implementation from Sensic with minimal TypeScript types.
15
+ */
16
+ export declare function initSensicS2S(gfkS2sConf: SensicConfig): Promise<SensicGlobal>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Builds the Sensic script URL based on country and platform.
3
+ * Script URLs:
4
+ * - AT Web: https://at-config.sensic.net/s2s-web.js
5
+ * - AT TV: https://at-config.sensic.net/ctv/s2s-web.js
6
+ * - DE Web: https://de-config.sensic.net/s2s-web.js
7
+ * - DE TV: https://de-config.sensic.net/ctv/s2s-web.js
8
+ */
9
+ export function buildSensicScriptUrl(country, platform) {
10
+ const path = platform === 'web' ? 's2s-web.js' : 'ctv/s2s-web.js';
11
+ return `https://${country}-config.sensic.net/${path}`;
12
+ }
13
+ /**
14
+ * Initializes the Sensic S2S library.
15
+ * Loads the script and sets up the global gfkS2s object.
16
+ * This is the original implementation from Sensic with minimal TypeScript types.
17
+ */
18
+ export function initSensicS2S(gfkS2sConf) {
19
+ return ((w, d, c, s, id) => {
20
+ if (w[id]) {
21
+ return Promise.resolve(w[id]);
22
+ }
23
+ w.gfkS2sConf = c;
24
+ if (d.getElementById(id)) {
25
+ return Promise.resolve(w[id]);
26
+ }
27
+ w[id] = {};
28
+ w[id].agents = [];
29
+ const api = [
30
+ 'playStreamLive',
31
+ 'playLive',
32
+ 'playStreamOnDemand',
33
+ 'playVOD',
34
+ 'stop',
35
+ 'skip',
36
+ 'screen',
37
+ 'volume',
38
+ 'impression'
39
+ ];
40
+ w.gfks = (() => {
41
+ function f(sA, e, cb) {
42
+ return (...args) => {
43
+ sA.p = cb();
44
+ sA.queue.push({ f: e, a: args });
45
+ };
46
+ }
47
+ function s(c, pId, cb) {
48
+ const sA = { queue: [], config: c, cb: cb, pId: pId };
49
+ for (let i = 0; i < api.length; i++) {
50
+ const e = api[i];
51
+ sA[e] = f(sA, e, cb);
52
+ }
53
+ return sA;
54
+ }
55
+ return s;
56
+ })();
57
+ w[id].getAgent = (cb, pId) => {
58
+ // Use 'new' to call gfks as a constructor - the Sensic script replaces gfks
59
+ // with a constructor function that expects to be called with 'new'
60
+ const GfksConstructor = w.gfks;
61
+ const innerAgent = new GfksConstructor(c, pId || '', cb || (() => 0));
62
+ // Use Proxy to pass through all method calls to the inner agent
63
+ // This ensures that when gfks is replaced by the real Sensic implementation,
64
+ // all methods (including setStreamPositionCallback) are accessible
65
+ const agentProxy = new Proxy({ a: innerAgent }, {
66
+ get(target, prop) {
67
+ if (prop === 'a')
68
+ return target.a;
69
+ // Pass through all other property access to the inner agent
70
+ const value = target.a[prop];
71
+ if (typeof value === 'function') {
72
+ return (...args) => value.apply(target.a, args);
73
+ }
74
+ return value;
75
+ }
76
+ });
77
+ w[id].agents.push(agentProxy);
78
+ return agentProxy;
79
+ };
80
+ const lJS = (eId, url) => new Promise((resolve, reject) => {
81
+ const existingTag = d.querySelector(`script[src="${url}"]`);
82
+ let tag = existingTag;
83
+ if (!existingTag) {
84
+ tag = d.createElement(s);
85
+ tag.async = true;
86
+ tag.type = 'text/javascript';
87
+ tag.src = url;
88
+ tag.id = eId;
89
+ }
90
+ const el = d.getElementsByTagName(s)[0];
91
+ if (tag) {
92
+ tag.onload = () => resolve(w[id]);
93
+ tag.onerror = () => reject(new Error('Failed to load Sensic S2S script'));
94
+ tag.onabort = () => reject(new Error('Sensic S2S script load aborted'));
95
+ }
96
+ if (!existingTag && tag && el?.parentNode) {
97
+ el.parentNode.insertBefore(tag, el);
98
+ }
99
+ });
100
+ return lJS(id, c.url);
101
+ })(window, document, gfkS2sConf, 'script', 'gfkS2s');
102
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Sensic S2S (Server-to-Server) TypeScript Definitions
3
+ * Types for the GfK Sensic streaming measurement SDK
4
+ * @see https://at-config.sensic.net/s2s-web.js
5
+ */
6
+ /**
7
+ * Options passed to play methods for screen and volume state
8
+ */
9
+ export interface SensicOptions {
10
+ /** Fullscreen state: '1' for fullscreen, '0' for not fullscreen */
11
+ screen: string;
12
+ /** Volume level as string (0-100) */
13
+ volume: string;
14
+ }
15
+ /**
16
+ * Custom parameters sent with each play event.
17
+ * The specific keys and values depend on the integration.
18
+ */
19
+ export type SensicCustomParams = Record<string, string>;
20
+ /**
21
+ * Sensic streaming agent instance for tracking playback
22
+ */
23
+ export interface SensicAgent {
24
+ /**
25
+ * Update the volume state
26
+ * @param volume - Volume level as string (0-100)
27
+ */
28
+ volume: (volume: string) => void;
29
+ /**
30
+ * Update the fullscreen state
31
+ * @param isFullscreen - '1' for fullscreen, '0' for not fullscreen
32
+ */
33
+ screen: (isFullscreen: string) => void;
34
+ /**
35
+ * Start tracking a live stream
36
+ * @param contentId - Content identifier (usually 'default')
37
+ * @param contentStart - Content start time (usually empty string)
38
+ * @param streamOffset - Offset from live edge in milliseconds
39
+ * @param streamId - Stream/content title
40
+ * @param options - Screen and volume options
41
+ * @param customParams - Custom tracking parameters
42
+ */
43
+ playStreamLive: (contentId: string, contentStart: string, streamOffset: number, streamId: string, options: SensicOptions, customParams: Record<string, string>) => void;
44
+ /**
45
+ * Start tracking an on-demand stream
46
+ * @param contentId - Content identifier (usually 'default')
47
+ * @param streamId - Stream/content title
48
+ * @param position - Current playback position in milliseconds
49
+ * @param options - Screen and volume options
50
+ * @param customParams - Custom tracking parameters
51
+ */
52
+ playStreamOnDemand: (contentId: string, streamId: string, position: number, options: SensicOptions, customParams: Record<string, string>) => void;
53
+ /**
54
+ * Stop tracking playback
55
+ * @param position - Current playback position in milliseconds (undefined for live)
56
+ */
57
+ stop: (position?: number) => void;
58
+ }
59
+ /**
60
+ * Configuration for initializing the Sensic SDK
61
+ */
62
+ export interface SensicConfig {
63
+ /** Media identifier provided by Sensic */
64
+ media: string;
65
+ /** URL to the Sensic S2S script */
66
+ url: string;
67
+ /** Device type (e.g., 'WEB') */
68
+ type: string;
69
+ }
70
+ /**
71
+ * Global Sensic SDK interface
72
+ */
73
+ export interface SensicGlobal {
74
+ /**
75
+ * Get a streaming agent for tracking playback
76
+ * @param agentArg - Callback that returns current position (undefined for live)
77
+ * @returns A SensicAgent instance for tracking
78
+ */
79
+ getAgent: (agentArg: () => number | undefined) => SensicAgent;
80
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Sensic S2S (Server-to-Server) TypeScript Definitions
3
+ * Types for the GfK Sensic streaming measurement SDK
4
+ * @see https://at-config.sensic.net/s2s-web.js
5
+ */
6
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glomex/integration-analytics",
3
- "version": "1.1480.1",
3
+ "version": "1.1481.0",
4
4
  "description": "Analytics integrations for the turbo player",
5
5
  "documentation": "https://docs.glomex.com",
6
6
  "homepage": "https://glomex.com",
@@ -25,6 +25,18 @@
25
25
  "./npaw": {
26
26
  "types": "./dist/npaw/index.d.ts",
27
27
  "import": "./dist/npaw/index.js"
28
+ },
29
+ "./comscore": {
30
+ "types": "./dist/comscore/index.d.ts",
31
+ "import": "./dist/comscore/index.js"
32
+ },
33
+ "./sensic": {
34
+ "types": "./dist/sensic/index.d.ts",
35
+ "import": "./dist/sensic/index.js"
36
+ },
37
+ "./nielsen": {
38
+ "types": "./dist/nielsen/index.d.ts",
39
+ "import": "./dist/nielsen/index.js"
28
40
  }
29
41
  },
30
42
  "files": [
@@ -34,24 +46,22 @@
34
46
  ],
35
47
  "scripts": {
36
48
  "prepack": "npm run build",
37
- "build": "rm -rf dist && npm run build:ts --force && npm run build:tsup",
38
- "build:ts": "tsc --build",
39
- "build:tsup": "tsup",
49
+ "build": "rm -rf dist && tsc --build --force",
40
50
  "lint": "tsc --noEmit && biome ci",
41
- "watch": "npm run build:ts --force && tsup --watch"
51
+ "watch": "tsc --build --watch"
42
52
  },
43
53
  "dependencies": {
44
- "@glomex/integration-web-component": "1.1479.0",
54
+ "@comscore/analytics": "^7.13.2",
55
+ "@glomex/integration-web-component": "1.1481.0",
45
56
  "npaw-plugin": "^7.3.18"
46
57
  },
47
58
  "devDependencies": {
48
59
  "@biomejs/biome": "catalog:",
49
- "tsup": "catalog:",
50
60
  "typescript": "catalog:"
51
61
  },
52
62
  "publishConfig": {
53
63
  "access": "public"
54
64
  },
55
65
  "license": "MIT",
56
- "gitHead": "8b24c346d5640b423bd85b77d12bebd67407bf7c"
66
+ "gitHead": "a25482ec869f700c6735db24bda8c69eb3ba7c88"
57
67
  }